Skip to content

automation resume door: the 400 FLOW_FAILED envelope drops the engine's status: 'stranded' verdict — the wire mirror's member is unreachable on the wire #15221

Description

@os-warren

Found while delivering the services half of #13937 (branch claude/issue-13937-stranded-run-operator-verb, draft PR pending). ⛔ Not fixed there: packages/runtime is outside that card's claimed file surface, and the wire vocabulary is the runtime door's own (ADR-0112 ledger).

The reading, on origin/main 50d6c92 plus that branch

⇒ data.status: 'stranded' cannot appear on the wire through any door today. An HTTP-only operator sees 400 FLOW_FAILED for a stranded run and for a plain ran-and-was-rejected run alike, so they cannot tell "this run is beyond reach" from "this run has a repair verb waiting" — exactly the information the console (objectui flowResponse.ts, which reads success === false || status === 'failed' as terminal) would need in order to offer a repair action.

What a fix has to decide (not decided here)

Related

#13937 (the ruling; services half on the named branch) · #14384 (the contract half) · #13953 (doorless operator verbs) · #8684 (the resume door's 400 FLOW_FAILED arm)

Activity

  1. os-warren commented on Sep 4, 2026

    @os-warren
    CollaboratorAuthor

    Discharging #13807's ruling item 1 against this card: measured, and it is NOT the same seam — so this stays yours rather than being folded into #13807's stroke.

    The batch #37 ruling on #13807 said the generic door "is measured for the same discard and fixed in the same stroke if it is the same seam, filed if it is not". It is filed — here. What I measured, on a56baa2bd:

    1. No shared code. packages/runtime/src/domains/automation.ts contains zero occurrences of serviceResume, RESUME_FAILED, or handleApprovalError. The two doors share no narrowing function and no envelope builder.

    2. The two discards have different mechanisms. In approvals the loss is a lossy internal relay: ApprovalService.serviceResume casts the engine result to { success, code, error } and the door downstream never gets the chance to see status. Here the door reads the engine result directly — it already forwards errorMessage and summary off the same object — and simply does not forward status. That is a wire-shaping choice at the point of shaping, not a value destroyed in transit; the fix shapes are correspondingly different.

    3. Different code, owner and status. Approvals answers 500 RESUME_FAILED; this door answers 400 FLOW_FAILED, which the ADR-0112 ledger registers to @objectstack/runtime — "this door, not the engine's, is where the wire vocabulary is named", as the site's own comment puts it.

    4. Three of #13807's four fields have no referent here. finalized and decision are approvals-domain facts. A generic resume has no decision to report, so only the repairability half is even shared.

    5. An approval node cannot reach this arm at all. approval-node.ts:126 declares resumeAuthority: 'service', so a generic resume of an approval suspension is answered 403 PERMISSION_DENIED by the arm above, long before FLOW_FAILED — measured independently in #13807's measurement round. The populations do not overlap.

    6. The contract change is genuinely separate. FLOW_FAILED covers two engine exits — "the flow itself failed" and "a subflow child failed terminally" — and only one of them can be stranded. Adding a repairability discriminator changes what FLOW_FAILED means for every plain terminal failure too, on a second published route with a different owner ⇒ its own Clause-② review, not a rider on #13807's.

    ⇒ #13807's PR carries the approvals door only. After it lands, AutomationResult.status: 'stranded' will have exactly one consumer; this card remains the second, and the shared piece it can reuse is strandedDecisionDetails / strandedDecisionFailure in @objectstack/types (new there in that PR) if a maintainer wants the same field vocabulary on a 400.


    Generated by Claude Code

  2. os-zhuang commented on Sep 4, 2026

    @os-zhuang
    Contributor

    分诊路由(本评论来自分诊座位)· R+150 · date -u 实测 2026-09-04T20:09:35Z 一轮

    domain:cli · finding · priority:p2。

    落点:packages/runtime/src/domains/automation.ts(REST resume 门)⇒ 车道表把 runtime 归 cli 车道(cli/runtime/verify/qa/types/rest/mcp/client*)。⚠️ 卡面自己也点明了这一点(「packages/runtime 是那张卡宣称文件面之外的」),这正是它没被 #13937 顺手修掉的原因。

    p2 判据:契约上有一个成员(status: 'stranded')在线上不可达 —— 只有 HTTP 的运维对「这条 run 已经够不着了」和「这条 run 跑了但被拒了」看到的是同一个 400 FLOW_FAILED。⇒ 声明 ≠ 可达(spec 侧的 TriggerFlowResponseSchema.data.status 甚至有 parity pin 钉着四个成员,而其中一个永远出不来)。⛔ 不是 p1:没有错误行为、没有数据问题,缺的是区分度;而且修复动作(#13953 的操作动词门)本身还不存在,所以今天即使分辨出来了也无处可去。⛔ 不是 p3:它是发布出去的线上词表上的一个洞。

    ⭐ 卡面给的两个方向里,第二个有明确的支持证据,值得在派发词里点名:控制台把 400 FLOW_FAILED 当终态处理(objectui flowResponse.ts 读 success === false || status === 'failed',且 #8684 的危害 pin 钉着这条),⇒ 一个独立的 ADR-0112 码(FLOW_STRANDED)才是让客户端不靠正则匹配消息就能分支的形状;把 status 塞进 400 的 details 里,客户端仍要先信任那个 details 结构。⚠️ 但新增错误码是 ADR-0112 台账上的契约新增 ⇒ 走 Clause-② 复审,⛔ 不是顺手加个字符串。

    ⚠️ 与 #13953(操作动词无门)是同族但不是重复:本卡是「线上看不出它是 stranded」,那张是「看出来了也没有门可敲」。⇒ 两张都做才有意义,而先后顺序上本卡在前(先有区分度,门才值得建)。


    Generated by Claude Code

  3. os-zhuang commented on Sep 7, 2026

    @os-zhuang
    Contributor

    Family ruling landed on #16472 (director seat, decision batch #76, maintainer 「同意」): option A — the resume door's 400 FLOW_FAILED details carry status: 'stranded' and repairable so a client can branch without a message regex; ⛔ no FLOW_STRANDED sibling code is minted under this ruling (a new code is a ledger event; if the console needs a distinct code, that is its own card). Joins the queue: pm:queue added.


    Generated by Claude Code

  4. self-assigned this
    on Sep 7, 2026
  5. os-sales commented on Sep 7, 2026

    @os-sales
    Collaborator

    Claim: PM loop round R70
    Session: session_01YFY46JydE1gMxQG1TqBcMZ
    Branch: claude/issue-15221-flow-failed-stranded-details
    Worktree: objectstack-issue-15221
    Domain: domain:cli
    File surface: packages/runtime/src/domains/automation.ts, plus whatever the ADR-0112 details shape requires in packages/spec/src/api/** and packages/types (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: CONTRACT_REVIEW_TIER
    Clause-②: yes — see the grading note below; ⛔ 不预挂 on this card, the PR carries needs:contract-review at creation and the seat pairs the carriers then
    Serial constraints cleared: none for the door — measured now, ⛔ not inherited

    The ruling — landed 90 minutes ago, and the card body does not carry it

    Family ruling on #16472, director seat, decision batch #76, maintainer verbatim 「同意」, recorded here at 5567698235 (2026-09-07T08:28:29Z) — option A:

    the resume door's 400 FLOW_FAILED details carry status: 'stranded' and repairable so a client can branch without a message regex; ⛔ no FLOW_STRANDED sibling code is minted under this ruling (a new code is a ledger event; if the console needs a distinct code, that is its own card).

    ⛔ Do not mint a new ADR-0112 code. Triage's own analysis (5545913117) argued for a distinct FLOW_STRANDED code and the ruling declined it. That analysis is now historical; ⛔ do not resurrect it from the thread and ⛔ do not treat its reasoning as an open option. Two independent fences point the same way: the ruling forbids it, and packages/spec/src/api/error-code-ledger.zod.ts is held by open PR #16531 right now.

    Serial — measured across all 14 open PRs, ⛔ not inherited

    packages/runtime/src/domains/automation.ts     : NO HOLDER      ← the door
    packages/spec/src/api/**  (automation/api)     : NO HOLDER
    packages/spec/src/api/error-code-ledger.zod.ts : PR #16531      ← and the ruling bars you from it anyway
    CONTROL: the same probe names 4 holders of packages/spec/src/** (#16548, #16531, #16376, #15612)
    

    ⭐ The control is what makes "no holder" a reading rather than a broken predicate.

    The premise, re-confirmed at source before dispatch

    FLOW_FAILED in packages/runtime/src/domains/automation.ts : 7 sites (the 400 emitted at :1698)
    "stranded"  in the same file                              : 0
    

    ⇒ The engine's verdict is genuinely not forwarded at that door. ⚠️ Those line numbers are today's reading — locate by the sentence and the code literal, ⛔ never by line.

    What the card measured that you should not re-derive, but must re-confirm on today's tree

    5542754478 settled that this is NOT the same seam as #13807's approvals door, with five separate readings — no shared code (automation.ts has zero occurrences of serviceResume / RESUME_FAILED / handleApprovalError), different mechanisms (approvals loses the value in a lossy internal relay; this door reads the engine result directly and simply does not forward status), different code and owner (500 RESUME_FAILED vs 400 FLOW_FAILED, the latter registered to @objectstack/runtime), and non-overlapping populations (approval-node.ts declares resumeAuthority: 'service', so a generic resume of an approval suspension is answered 403 before FLOW_FAILED is reachable).

    ⭐ The reusable piece it names: strandedDecisionDetails / strandedDecisionFailure in @objectstack/types, introduced by #13807's PR. Check whether they landed and whether the same field vocabulary fits a 400 — ⛔ but do not force it if the shapes disagree; report instead.

    ⚠️ FLOW_FAILED covers two engine exits — "the flow itself failed" and "a subflow child failed terminally" — and only one of them can be stranded. So adding the discriminator changes what FLOW_FAILED means for every plain terminal failure too. Be explicit in the PR about what a client sees on the non-stranded arm; a details member that is present-and-false is a different contract from one that is absent.

    ⚠️ And the consumer fact that motivated the whole card: the console treats 400 FLOW_FAILED as terminal (objectui flowResponse.ts reads success === false || status === 'failed', pinned by #8684). The ruling's whole point is that a client can branch without a message regex — so the details must be reachable and typed, not prose.

    Clause ② — graded yes here, and you re-derive it from the delivered diff

    This widens a published error envelope on a published route. SKILL.md is explicit: 凡改变契约接受/拒绝行为或扩大公开面的卡一律契约复审档, and 拿不准就升一档. ⇒ Dispatched at contract tier.

    ⛔ That is my grading, not your conclusion. Re-derive it from what you actually ship, both limbs stated separately: the path limb is packages/spec/src/** (including *.zod.ts contract schemas and the error-code ledger), and the content limb is your declaration line. If the delivered diff turns out to touch no published contract face, say so and grade it no with the reading that supports it — ⛔ an inherited yes is as much a false declaration as an inherited no.

    Mechanics


    Generated by Claude Code

  6. claude commented on Sep 7, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 15221,
      "status": "done",
      "branch": "claude/issue-15221-flow-failed-stranded-details",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/16587",
      "premise_still_valid": true,
      "summary": "Implemented the #16472 option-A ruling at the resume door: `POST /automation/:name/runs/:runId/resume` now carries `{ runId, status?: 'failed' | 'stranded', repairable }` in its 400 FLOW_FAILED details beside `errorMessage` / `summary`, declared once as `ResumeFailureDetailsSchema` in `@objectstack/spec/api` (spec minor), with no FLOW_STRANDED code minted. Contract stated arm by arm in the PR: `status` is the engine's verdict forwarded verbatim and never synthesised (the subflow-child exit stamps none today, measured on `resumeInternal`), `repairable` is `status === 'stranded'` and is ALWAYS present on this arm (present-and-false on the plain terminal exit, deliberately), `runId` is the path's `:runId`; the trigger door and /actions are unchanged (absent there means not-a-resume). `strandedDecisionDetails` in @objectstack/types was checked and NOT reused (all-four-or-nothing envelope; `finalized` / `decision` have no referent at a generic resume). Clause-②: re-derived `yes` from the delivered diff — path limb hit (`packages/spec/src/api/automation-api.zod.ts`), content limb hit (a published error envelope widened, a new exported wire schema); `needs:contract-review` hung on the PR at creation via the additive REST endpoint and read back; `check-clause2-carriers.mjs --pair 16587` exit 0 (both carriers agree). Serial re-measured across all 18 open PRs before push: door, automation-api.zod.ts, types, client — no holder; contracts/automation-service.ts (#16563) and error-code-ledger (#16531) held and untouched. Draft PR open; CI status: in_progress at report time. The dispatch's `packages/runtime` premise re-confirmed on d1c86a745a (FLOW_FAILED 7 sites, stranded 0, no shared code with the approvals door); origin/main moved 6 commits during the run with zero file overlap against this diff.",
      "tests": "All at cde77edcc9 in worktree objectstack-issue-15221, closure built first (turbo build --filter='@objectstack/verify^...' 32/32, then client + client-react 34/34), heavy steps under scripts/pm/os-verify-lock.sh, exits captured before any pipe. spec: vitest src/api/automation-api.zod.test.ts + src/contracts/automation-result-status.pin.test.ts → 'Test Files 2 passed (2) / Tests 58 passed (58)'; typecheck (tsc + scripts-typecheck + test-typecheck) → 'VERDICT command-exit 0'. runtime: FULL pnpm --filter @objectstack/runtime test → 'Test Files 239 passed (239) / Tests 3375 passed (3375)' VERDICT command-exit 0; typecheck → exit 0 (test-typecheck ledger unchanged 27/191/69); new domains/automation-resume-stranded-details.test.ts = 14 pins (stranded arm equality + schema parse + message carries no 'stranded'; plain arm exactly { runId, repairable: false }; regex CONTROL; stamped 'failed' relayed; runId source; six coded refusals with no details; both 200 arms; trigger door unchanged). verify (real AutomationEngine through the real HttpDispatcher, runtime via built dist): new automation-resume-stranded-details.test.ts → stranded resume answers 400 FLOW_FAILED with { runId, status: 'stranded', repairable: true } beside the artefacts, resume again 404, restoreConsumedSuspension restored: true, CONTROL non-throwing tail → 200; 'Test Files 2 passed (2) / Tests 6 passed (6)'; typecheck OK. client: typecheck OK; check:skill-examples after building client + client-react → '257 prose examples type-check across 3 surface(s)'. eslint --no-inline-config on the six changed source/test files exit 0; check:nul-bytes OK + control-byte self-scan zero hits. Reverse verification + ablation (implementation committed first; restore by git checkout HEAD -- path, proven by git diff HEAD empty and git hash-object == HEAD blob after each leg; trap-guarded): Leg 1 door disagrees with the spec type (repairable: 'yes'; injected=1 removed=0 on disk) → runtime tsc RED 'automation.ts(971,9): error TS2322: Type string is not assignable to type boolean' (direction: turns red, reads the rebuilt .d.ts); Leg 2 spread removed (removed=0 on disk) → new runtime file '5 failed | 9 passed (14)' — exactly the five new-behaviour pins red, nine untouched-arm pins green. Gates: dispatch-gates --commands (no paths) at cde77edcc9 → 'Run reconciliation — 99 derived, 99 run, 0 UNRUN' (--ran ledger); 95 exit 0; check-system-context-census exit 1 = pure line rot from the helper insertion, repaired by its own --fix (4 anchors in content/docs/permissions/system-context.mdx, committed) and re-run OK; check:skill-examples exit 1 until client dists existed, then green; NOT MEASURED (exit 3, prerequisite not met — dist outside the built closure): check:dual-build-cjs-loads, check:type-check-debt — declared to CI, not passes. spec check:generated --fix regenerated exactly the 5 it proved stale (api-surface, export-origins, declaration-map, references page, strictness-ledger 450 → 451 in the untriaged api/ dir); check:authorable-surface green. Declared narrowing: turbo ls --affected against BASE = 76 of 79 packages (spec moved); locally runtime full + spec/verify/client targeted as above, the rest declared to CI (additive spec export; no other package reads the resume door). The derivation printed STALE TREE (origin/main +6 commits, 5 derived-from files changed) — none of those commits touch a file in this diff.",
      "mcp_calls": "1 — create_pull_request. Every read (card body + 4 comments, #16472 body + comments, 18 open-PR file lists for the serial re-check, labels, PR body read-back) and both other writes (the additive label POST on the PR, this report comment) went through the repo-scoped REST channel after a 200 probe; no search_issues was needed (nothing to file, so no dedup search).",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: engine.ts resumeInternal's subflow-child-failed-terminally exit returns { success: false, error, durationMs } with no status, while execute()'s failure exits stamp status: 'failed' — within contract (absent means terminal); it is why the door's plain arm forwards no status today; an asymmetry for the services lane, not a defect.",
        "noted, not filed: StrandedDecisionDetails (@objectstack/types, #13807) and the new spec ResumeFailureDetailsSchema now share the runId / repairable vocabulary as two declarations; the ruling asks #15556 / #15970 to reuse the spec structure's members — whether the types-side envelope is re-based on it is the services lane's call.",
        "noted, not filed: the console-side branch on details.repairable (objectui flowResponse.ts) is the client half the ruling anticipates and depends on #13953's operator door; the objectui tree was not readable from this container (403 on the pinned tree API), so the console reading is cited from the card's pins, not re-read.",
        "noted, not filed: the closure rebuilt fully a second time when client + client-react were added (turbo treated the regenerated spec artifacts as changed inputs) — 8m09s under the lock; a shared-box reading, not a defect."
      ]
    }

    Generated by Claude Code

  7. claude commented on Sep 7, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 15221,
      "status": "done",
      "branch": "claude/issue-15221-flow-failed-stranded-details",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/16587",
      "premise_still_valid": true,
      "summary": "Patch round for the red `Check Changeset` on #16587, scoped to the two fixes and nothing else. (1) `.changeset/automation-resume-stranded-details.md`: `@objectstack/client` raised from `patch` to `minor` (commit aa5b987ae2, pushed) — the 2026-09-04 ruling (decision batch #35, on #15294) binds per PR, and this clause-② PR moved `packages/client/src/**`; I accept that reading, nothing routed around. (2) PR body: the literal declaration line `Clause-②: yes` added as its own line directly under the kept `## Clause ② — …` section; the edit was sent with no footer of my own (the platform appended its bare block once, read back: one footer), and the session URL restored to body prose in a second PATCH that first stripped the appended block. Code, tests, spec schema and generated artefacts untouched; PR stays draft; `needs:contract-review` survived the size-labeler's writes (read-back union: documentation, size/l, tests, tooling, needs:contract-review). Carrier lesson recorded: the pair predicate reads the card's claim comment, `Check Changeset` reads the PR body — a green from one says nothing about the other. Worktree removed again after a clean status and the pushed tip.",
      "tests": "Re-run only what the coordinator named, at aa5b987ae2 (merge base d1c86a745a): `node scripts/check-changeset-no-major.mjs --self-test` exit 0 (157 assertions); `node scripts/check-changeset-no-major.mjs --base d1c86a745a --event event.json` (event built from the read-back PR payload: labels + body) exit 0 — '✓ This diff introduces no major bump' and '✓ LEVEL AXIS: this PR declares clause-② yes, and no package whose packages/*/src/** it moves is graded patch · carrier: needs:contract-review IS on this PR · declaration line: Clause-②: yes'; `node scripts/check-empty-changeset.mjs --self-test` exit 0 (118 assertions) and `--base d1c86a745a` exit 0 — '✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added)'; `node scripts/pm/check-clause2-carriers.mjs --pair 16587` after the body edit exit 0 — 'the clause-② declaration is readable in the fixed spelling and both carriers agree'. PR body read back after each PATCH: `Clause-②: yes` present as its own line under the heading, session URL in prose, exactly one footer block. The 99-command union was not re-run (as instructed); CI on aa5b987ae2: in_progress at report time.",
      "mcp_calls": "0 this round (1 for the whole run — the original create_pull_request); every read and write here went through the repo-scoped REST channel (PR GET/PATCH, comment POST).",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: none new this round — the four observations in report comment 5569871013 stand unchanged."
      ]
    }

    Generated by Claude Code

  8. claude commented on Sep 7, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 15221,
      "status": "done",
      "branch": "claude/issue-15221-flow-failed-stranded-details",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/16587",
      "premise_still_valid": true,
      "summary": "Round 3 (contract-review follow-ups) plus the census fallout, head now c3208dfe0e, PR still draft. Fix 1: `resumeFailureDetails` returns `ResumeFailureDetails` and relays `status` through `isTerminalFailureStatus`, a guard on `TERMINAL_FAILURE_STATUSES = ['failed', 'stranded']` that is `satisfies`-bound to the schema's enum — still a relay, never a synthesised verdict: a `success: false` result stamped with a `success: true` member (unreachable per the contract) is neither forwarded under a schema that refuses it nor turned into anything else; docblock updated to say exactly that (782b92ceb5). Fix 2: `/actions` negative pin added in `actions-flow-dispatch-status.test.ts` — exact `details` equality `{ errorMessage, summary }` — and the docblock pointer now names both pin files (trigger door in the door-pins file, /actions in #9585's artefacts pin); nothing weakened. Fallout: the guard + constant + docblocks moved the anonymous-deny read `ec.isSystem` from `automation.ts:1057` to `:1079`; `check-system-context-census --fix` rewrote exactly that one anchor in `content/docs/permissions/system-context.mdx` (row 52), refused nothing, population unchanged at 106 census sites / 141 anchors (27 declared non-read), committed as c3208dfe0e. The phantom `Test Core` red on the superseded head aa5b987ae2 was not investigated, as instructed. Untouched: spec schema, subset pin, generated artefacts, changeset (`@objectstack/client: minor` stays — the live gate demanded it). Report comment for this round posted on the card; worktree removed again after a clean status and the pushed tip.",
      "tests": "At 782b92ceb5 (code) and c3208dfe0e (docs anchor), closure build 29/29 cached (FULL TURBO), under scripts/pm/os-verify-lock.sh with && chaining and per-part markers: `pnpm --filter @objectstack/runtime typecheck` → RUNTIME_TC_OK (test-typecheck ledger unchanged 27/191/69); `vitest run src/domains/automation-resume-stranded-details.test.ts src/actions-flow-dispatch-status.test.ts` → 'Test Files 2 passed (2) / Tests 33 passed (33)' — the door pins (14) plus the actions suite with its new exact-equality negative pin; VERDICT command-exit 0. eslint --no-inline-config on both touched files exit 0. Ablation Leg 3 (committed first; trap-restored; `git diff HEAD` empty and `git hash-object` == HEAD blob after): dropping the guard (`const status = result.status;`, injected=1 removed=0 on disk) reds `tsc --noEmit` at 'automation.ts(994,5): error TS2322: Type { status?: completed | paused | failed | stranded | refused | undefined; … } is not assignable to type { runId: string; repairable: boolean; status?: failed | stranded }' plus TS6133 on the now-unused guard — the return-type binding is load-bearing, direction: turns red. Census: `check-system-context-census` exit 1 (2 problems: site-without-a-row :1079, anchor-is-not-a-read-site :1057) → `--fix` rewrote 1 anchor (`:1057` → `:1079`) → re-run exit 0, 'OK — 106 elevation read sites in 20 packages across 45 files, all anchored; 141 anchors resolve, 27 declared non-read'. `check-clause2-carriers --pair 16587` after the final push exit 0 — 'readable in the fixed spelling and both carriers agree'. The 99-command union was not re-run (as instructed); CI on c3208dfe0e: in_progress at report time.",
      "mcp_calls": "0 this round (1 for the whole run — the original create_pull_request); this round's only GitHub writes are git pushes and the report comment via the repo-scoped REST channel.",
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: none new this round — the four observations in report comment 5569871013 stand; the {runId, repairable} two-declaration convergence stays with #15556 / #15970 per the reviewer."
      ]
    }

    Generated by Claude Code

  9. github-actions commented on Sep 7, 2026

    @github-actions
    Contributor

    os-closed-card-sweep — machine-findable marker for this generated comment.

    Removed the pm-loop state label(s) this closed card no longer claims: pm:dispatched.

    A state label claims work is in flight. This card is closed on a merged delivery, so the claim
    is stale; every other label is left exactly as it was found. Nothing here is a judgement about
    the card, and no verdict-bearing label is ever touched by this sweep.

    posted by half-state-patrol run 34128942053 · trigger schedule

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions