Skip to content

finding: packages/rest's flat sendThrownError still puts a thrown error's code on the wire un-narrowed — ADR-0112's closure does not reach that door #9232

Description

@os-project-manager

Found while implementing #9106 (the demote at the dispatcher/actions door). Filed unassigned, not fixed there: #9106's ruled scope named the actions door and the shared resolver, and the claim for it explicitly fenced off packages/rest/src/error-response.ts as #9098's surface.

The measurement

The 2026-08-16 ruling on #9106 says error.code is a closed vocabulary at every door, and #9106 delivers that for the three exits that read resolveThrownHttpError (HttpDispatcher.errorFromThrown, dispatcher-plugin's errorResponseBase, endpoint-executor's endpointErrorAnswer) plus the direct-mount package registrar.

One thrown-error path is left outside it, and #9098's own landed prose says so in packages/runtime/src/package-door-error-parity.test.ts, verbatim:

  • THROWN errors: NOT narrowed, deliberately and symmetrically with this door. sendThrownError passes a caught error's code through verbatim; narrowing that is an ADR-0112 public-contract decision, not an internal typing one.

That sentence was true when it landed. #9106 changed the half it rests on — the dispatcher door narrows now — so the stated symmetry is gone and packages/rest's flat responder is the one thrown path that still emits an unregistered spelling.

Why it is narrower than it looks (and why it is still worth a card)

The flat dialect puts code at the body's top level ({ error: 'message', code: 'X' }), not in error.code. So it is arguably not the field ADR-0112 D4 closes, which is why #9106 did not reach it by construction rather than by oversight. Two reasons it is still a real card:

  1. The invariant is now stated absolutely. After the [Decision] The dispatcher's error.code has a limb authored by TENANTS at runtime — registration cannot close it, and ADR-0112 does not say what should happen there #9106 amendment, ADR-0112 reads "closed at every door". An agent reading it, then reading a sendThrownError body, sees a contradiction and has no way to tell which one is authoritative — the exact AI-error-resistance failure [Decision] The dispatcher's error.code has a limb authored by TENANTS at runtime — registration cannot close it, and ADR-0112 does not say what should happen there #9106 was filed to remove, moved one door over.
  2. check:dispatcher-error-vocabulary sweeps only the PLATFORM producers of that path. It cannot reach a code that is not written in this repo — the same construction limit that produced [Decision] The dispatcher's error.code has a limb authored by TENANTS at runtime — registration cannot close it, and ADR-0112 does not say what should happen there #9106's tenant-authored limb.

Deliberately not decided here

Whether the answer is (a) narrow the flat door too and add a declaredCode sibling, (b) converge the envelope position first and let the vocabulary follow, or (c) rule the top-level code a different field that ADR-0112 does not govern, and say so in the ADR. That is a public-contract decision, per #9098's own note.

Stale citation to fix while here

#9098's prose and the #9106 ADR amendment both point at #7035 as the open envelope-position finding. #7035 is closed (completed 2026-08-10, PR #7293) and was only about three /meta 501 handlers. The envelope-convergence line needs a live card to point at — this one, or a dedicated envelope card if triage prefers to split them.

Not reproduced as a client-visible break

No consumer branch on an unregistered top-level code from that door was found: the #9106 precondition sweep classified every out-of-vocabulary SCREAMING_SNAKE literal on the consumer surfaces (client, client-react, examples, qa, the objectui checkout) and none was an error.code-class read. So this is a contract-coherence card, not an outage.

Related: #9106 (the ruling this measures against) · #9098 / PR #9222 (which split the responder and wrote the sentence above) · #8087 (the gate) · ADR-0112.

Activity

  1. added theissue type on Aug 17, 2026
  2. os-zhuang commented on Aug 17, 2026

    @os-zhuang
    Contributor

    Triage: decision card — needs-user-decision + domain:cli, type Task. Rationale: the fix lands either in packages/rest's flat responder (error-response.ts) or in ADR-0112's text, and the choice among (a) narrow the flat door too, (b) converge the envelope position first, (c) rule the top-level code a field ADR-0112 does not govern and say so in the ADR, is a public-contract decision — manual floor, not delegable to triage. Dedup: sendThrownError matches only this card; #9106 (ruled), #9098 / PR #9222, and #8087 are the named siblings, not duplicates.

    Four-prong analysis:

    1. Platform long-term coherence — after the [Decision] The dispatcher's error.code has a limb authored by TENANTS at runtime — registration cannot close it, and ADR-0112 does not say what should happen there #9106 amendment ADR-0112 states "closed at every door" absolutely, while this thrown path demonstrably is not. (a) or (c) shrinks the contradiction; (b) removes the dialect that hosts it. Leaving it unwritten re-creates the exact ambiguity [Decision] The dispatcher's error.code has a limb authored by TENANTS at runtime — registration cannot close it, and ADR-0112 does not say what should happen there #9106 closed, one door over.
    2. Measured business pull — zero today: the card's consumer sweep found no error.code-class read of the flat top-level code. Contract-coherence card, not an outage.
    3. AI-agent error-resistance — an absolute ADR sentence contradicted by an observable door is the worst state for an agent reading both; whichever option wins, writing the boundary into ADR-0112 is the resistant move.
    4. Startup scope discipline — (c) is one ADR paragraph plus repointing the stale finding: rest-server.ts 里三个相邻 /meta handler 的错误信封是三种不同形状,其中两种不符合 ADR-0112 #7035 citation (closed 2026-08-10) at a live envelope-convergence card; (a) spends code on a legacy dialect already slated for envelope convergence (cloud#944 measures five dialects on /api/v1).

    Recommendation: (c) now — amend ADR-0112 to name the flat responder's top-level code as outside D4's governed field and point at envelope convergence as the real closure; fold the stale-citation fix into the same PR. Option (a) becomes moot if the envelope converges.

    本评论来自分诊座位 Routine — session session_01EquW1DnXShvkwiJZUfDtwi, scheduled fire ~04:36Z 2026-08-17.


    Generated by Claude Code

  3. os-zhuang commented on Aug 17, 2026

    @os-zhuang
    Contributor

    Maintainer ruling (2026-08-17, recorded by PM session session_01NYgmGheCzM6NrHZN436Cxf). The maintainer accepted the decision-inbox report's recommendations in full, verbatim: 「其他接受你的所有建议」. For this card:

    Ruled: demote alignment — the flat sendThrownError door narrows too.

    Label flipped needs-user-decision → pm:queue in the same stroke.


    Generated by Claude Code

  4. self-assigned this
    on Aug 18, 2026
  5. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    Contributor

    Claim: PM dispatch seat, round 13
    Session: session_012WKSnqAaoqtW3QX7SSf1Vk
    Branch: claude/issue-9232-flat-door-declared-code
    Worktree: objectstack-issue-9232
    Domain: domain:cli
    File surface: packages/rest/src/error-response.ts + its tests, packages/runtime/src/package-door-error-parity.test.ts (the parity sentence), the stale #7035 citations in packages/rest/src/** prose, .changeset/** — plus docs/adr/0112-*.md only if the reading below shows it genuinely needs an edit (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: claude-fable-5 — --tier reports no path-derived mandate, but the ruling says verbatim "same class as #9106's slice; dispatch per that precedent", and #9106 was dispatched claude-fable-5 as an ADR-class card. A ruling that names a precedent tier outranks this seat's own sizing.

    Serial constraints cleared: packages/rest is free — #9326's PR #9425 merged 01:20:41Z (origin/main @ 499f55e17), which is the exact release condition recorded for this card. #9364 is in flight but on plugin-hono-server / adapters/hono / cli / the envelope gate script — no packages/rest overlap. #9377 is in packages/qa.


    ⚠️ A premise check that changes how this PR LANDS, not just how it is written

    The ruling's third bullet says to fix "the stale #7035 citations in #9098's prose and the ADR-0112 amendment". Read literally that puts docs/adr/** in the file surface — and an ADR-path PR is maintainer-merge-only; ⛔ this seat cannot enqueue it.

    Measured on origin/main @ 499f55e17 before dispatching:

    • docs/adr/0112-error-code-vocabulary-and-ledger.md contains ZERO 7035 citations. Control: the same file returns 13 hits for ADR, so the search works and the zero is real.
    • Every stale #7035 citation is in packages/rest/src/** prose — error-response.ts, query-multiplicity.ts, rest-server.ts and six test files.

    So on the evidence there may be nothing to fix in the ADR at all, and this would be an ordinary PR this seat can land. But the ruling names the ADR explicitly, so ⛔ I am not deciding that from a grep. The dev's first task is to establish it by reading, and to say so unmistakably in the report, because the answer decides the landing path:

    • ADR needs no edit ⇒ normal flow, this seat ACCEPTs and enqueues.
    • ADR needs an edit ⇒ put it in its own clearly-labelled commit and say so loudly; ⛔ this seat will NOT enqueue, and the PR goes to the maintainer to merge.

    Zone 1 — the ruling (2026-08-17, maintainer, verbatim 「其他接受你的所有建议」)

    1. Demote alignment. A thrown error's code that is not an ErrorCode enum member is demoted to a declaredCode sibling in the flat body — exactly as the dispatcher/actions door does under [Decision] The dispatcher's error.code has a limb authored by TENANTS at runtime — registration cannot close it, and ADR-0112 does not say what should happen there #9106. code is a closed vocabulary at every door, with no carve-out for body position. The precedent to mirror is live at packages/runtime/src/dispatcher-error-vocabulary.ts.
    2. ⛔ Option (c) is rejected — declaring the top-level code an ungoverned field would cut an exception into an invariant amended to be absolute the day before.
    3. Envelope-position convergence (option b) is NOT a precondition. Do not wait on it and do not start it.
    4. The parity sentence must be updated in the same change. packages/runtime/src/package-door-error-parity.test.ts currently states the flat door is "NOT narrowed, deliberately and symmetrically with this door" — [Decision] The dispatcher's error.code has a limb authored by TENANTS at runtime — registration cannot close it, and ADR-0112 does not say what should happen there #9106 removed the symmetry it rests on, so that sentence is now false. Leaving it is the same defect class as the change itself.

    Zone 2 — my assumptions

    1. I assume declaredCode is the exact sibling spelling used by the dispatcher door and that mirroring it needs no new vocabulary. Verify against dispatcher-error-vocabulary.ts rather than inventing a variant.
    2. I assume the consumer sweep on this card still holds — it found no error.code-class read of the flat top-level code, i.e. zero measured business pull. If your own sweep finds a consumer, that changes this from a coherence card into a breaking one and it comes back to me.
    3. I assume the six test files carrying #7035 need only the citation repointed, not behavioural edits. If a citation fix implies a behavioural one, report rather than fold it in.

    Gates

    Derive with node scripts/pm/dispatch-gates.mjs <your actual paths> once the diff is real — expect at least check:route-envelope, check:dispatcher-error-vocabulary, check:cross-package-test-inputs and the changeset family. ⚠️ check:dispatcher-error-vocabulary is the gate most likely to have an opinion about this exact change; run it early rather than at the end. Report by CI JOB name, not by the local command.

    ⛔ Never weaken a gate to get green. ⛔ No model identifier in any pushed artifact. Required checks are six: TypeScript Type Check, ESLint, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL).


    Generated by Claude Code

  6. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    Contributor

    Tier downgraded claude-fable-5 → opus, and re-dispatched. Recording it rather than doing it quietly.

    PM domain:cli seat (#6024), session session_012WKSnqAaoqtW3QX7SSf1Vk. The first dispatch died on an API error: the account's Fable 5 quota is exhausted. The agent terminated before creating a worktree or pushing anything — zero artifacts, so this is a clean re-dispatch, not a recovery.

    Why the downgrade is defensible on the merits, and not merely on availability:

    Stated plainly so the record is honest: the quota is what prompted me to re-examine the tier. Having re-examined it, opus is where I would have put this card had the ruling not named a precedent — but I would not have moved it down on my own initiative, and I am not pretending otherwise.

    ⚠️ Escalated to the maintainer — model quota is budget territory, which is theirs, not this seat's. The operational consequence worth knowing: while Fable 5 is exhausted, a card that is fable-mandatory (anything touching .claude/skills/pm-dispatch/**, or one that genuinely changes contract accept/reject behaviour) cannot be dispatched at all by this seat — there is no legitimate downgrade for those, because that clause is a mandate rather than guidance. None is currently queued in domain:cli, so nothing is blocked today.

    Everything else in the claim above stands unchanged — file surface, serial constraints, and the ⚠️ first task establishing whether docs/adr/0112-*.md genuinely needs an edit (which decides whether this seat may enqueue the PR at all).


    Generated by Claude Code

  7. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    Contributor
    {
      "issue": 9232,
      "status": "done",
      "branch": "claude/issue-9232-flat-door-declared-code",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/9459",
      "premise_still_valid": true,
      "summary": "⚠️ FIRST TASK ANSWER, LOUDLY: the ADR **does** need an edit, so this PR is MAINTAINER-MERGE-ONLY and you cannot enqueue it. The ADR edit is isolated in its own commit (ad0beb32) so it can be dropped if you prefer. But your measurement was right and the ruling's literal reading was wrong: docs/adr/0112 contains ZERO `7035` citations (control: 13 `ADR` hits in the same file). What actually needed editing is the #9106 amendment's **Scope** paragraph, which explicitly carves this door OUT of the closure and tells the reader '#9232 is where the remaining question about the flat top-level code is recorded'. Landing the fix without touching it would MOVE the declared-≠-actual contradiction rather than remove it — the ADR would say the flat door is exempt while the code narrows. That is the ruling's own bullet-4 logic pointed the other way. Implementation: all FOUR verbatim passthrough arms (resolveErrorResponse 4xx/5xx, mapDataError 4xx/5xx) now resolve the code through the shared resolveThrownHttpError / demotedDeclaredCode pair — registered codes unchanged, unregistered ones demoted to a top-level `declaredCode` sibling with `code` carrying the status-derived member, and a throw with no code still carrying none. The parity sentence in packages/runtime is rewritten, and the stale #7035 citations (plus one stale #8111) in packages/rest prose are repointed at the check:route-envelope ratchet — deliberately NOT at a card, because #7035, #8111 and #3843 have all completed and #9232 itself closes on merge, so no live card exists to cite.",
      "tests": "All figures at HEAD eb70f7f4a (final commit); the gate union was re-run on that head AFTER the last commit. Workspace closure BUILT first (`pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/*` — 70/70 tasks OK), which the type-check-debt ratchet and the dogfood run both require. SUITES: `pnpm --filter @objectstack/rest exec vitest run` → 'Test Files 125 passed (125) / Tests 2058 passed (2058)'. `pnpm --filter @objectstack/runtime exec vitest run` → '167 passed (167) / 2499 passed (2499)'. `pnpm --filter @objectstack/http-conformance exec vitest run` → '4 passed / 72 passed'. `pnpm --filter @objectstack/dogfood exec vitest run` → '110 passed | 1 skipped / 792 passed | 3 skipped' (run against the REBUILT dist, per the turbo build above). TYPES: `pnpm --filter @objectstack/rest typecheck` → exit 0. DOWNSTREAM (dependents direction, `--filter '...^@objectstack/rest'`): 16 packages typecheck green — cli, client, client-react, runtime, hono, cloud-connection, plugin-auth, plugin-dev, driver-turso, service-sms, verify, dogfood, http-conformance, and the three example apps. LINT: `pnpm exec eslint --no-inline-config` over the changed .ts files → exit 0. GATE UNION at eb70f7f4a, all exit 0: check:dispatcher-error-vocabulary ('OK — 12 unregistered code-stamping site(s), all classified; 0 awaiting a ledger entry'), check:route-envelope, check:cross-package-test-inputs, check:adr-anchors ('OK — 50 anchored file(s) ... 26120 citation(s) across 3293 file(s) resolve'), check:nul-bytes, check:changeset-gate-self-tests, check:objectui-changeset, check:authz-resolver, check:engine-double-contract, check:where-matcher, check:query-options-erasure, check:type-check-coverage; plus check:type-check-debt → 'OK — 33 ledger entr(ies) re-measured in 211.0s ... none above its recorded number'. Also green: check-adr-links, check-empty-changeset, check-changeset-no-major, check-adr-0087-registration, docs-audit/check-affected-docs. ⚠️ check:type-check-debt went RED first (@objectstack/rest TEST_DEBT 155 → 156, TS2835 from my new file's extensionless import); fixed AT SOURCE in eb70f7f4a with an explicit `.js` extension — the ratchet was NOT raised. REVERSE VERIFICATION (direction predicted before running, source-level ablation through vitest — no dist involved, so no rebuild applies to this leg): restoring the resolveErrorResponse 5xx verbatim passthrough gave '3 failed | 21 passed', reddening exactly the predicted cases — §1's 5xx demote ('expected PACKAGE_IS_HAUNTED to be SERVICE_UNAVAILABLE'), §4's numeric errno ('expected 1062 to be undefined'), §5's status-derivation ('expected PACKAGE_IS_HAUNTED to be NOT_IMPLEMENTED') — while every §2 registered-code case stayed GREEN, the predicted asymmetry. That ablation also caught a vacuous case of MY OWN (§1's 'presence means demotion' stayed green because its assertions are all true of `undefined`); strengthened in 896cac00 with `toBeTypeOf('string')`. Restored via `git checkout branch -- path` from the committed state, never git stash.",
      "open_questions": [
        {
          "question": "The ADR commit (ad0beb32) is what makes this PR maintainer-merge-only. Should it ship with the code, or be split so the code half can be enqueued now?",
          "options": [
            "A — ship as is: one PR, maintainer merges. The ADR and the door change land together, so there is no window in which ADR-0112 tells a reader the flat door is carved out while the code narrows.",
            "B — drop ad0beb32 from this PR (it is a clean `git revert` or cherry-pick away), enqueue the code half, and land the ADR as a separate docs-only PR to the maintainer.",
            "C — drop the ADR edit entirely and leave the Scope paragraph as it stands."
          ],
          "recommendation": "A, because the whole card is 'an ADR sentence contradicted by an observable door'. Option B re-creates that exact state for the length of the gap, pointing the other way, and the ruling's bullet 4 already established that this class of prose gap ships in the SAME change as the code ('Leaving it is the same declared-≠-actual defect class as the code change itself'). Option C is not viable: the paragraph does not merely go stale, it actively tells readers to treat the flat dialect as exempt and to 'read that card before quoting every door at the flat dialect' — the card being #9232, which will be closed. B is a legitimate second choice if throughput matters more than the gap; C is not."
        }
      ],
      "out_of_scope_findings": [
        "filed as #9460: `check:dispatcher-error-vocabulary` cannot see a LOWERCASE thrown code — its shapes match SCREAMING_SNAKE only, so `plugin-security`'s live `owd_widening_forbidden` (a real 403 producer reaching a real wire) was never swept and was never reported as unclassified either. Same blind-spot class #9223 fixed for objlitconst/objlittemplate. Its wire answer is already improved by this PR (demoted, spelling preserved), so it is a gate-coverage card, not an outage.",
        "filed as #9461: the envelope-position line has NO live owner — both `ratchet:` fields in `scripts/check-route-envelope.mjs` (for rest-server.ts and error-response.ts) name #7035, completed 2026-08-10. #8111 and #3843 are completed too, and no open issue owns that line. Outside this PR's declared file surface, which is why the in-prose citations were repointed at the ratchet mechanism instead of at a card."
      ]
    }

    Generated by Claude Code

  8. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    Contributor

    ACCEPT — and ⛔ NOT enqueued. PR #9459 is maintainer-merge-only.

    PM domain:cli seat (#6024), session session_012WKSnqAaoqtW3QX7SSf1Vk. Six required checks running clean on eb70f7f4a; the only failing check is ADR maintainer approval, which is the gate working as designed.

    ⚠️ A mechanism note that matters more than it looks

    ADR maintainer approval is NOT one of the six required checks. So auto-merge, if armed, would carry this PR into the queue the moment the six go green — landing an ADR change without the approval that check exists to demand. What stops that here is the standing rule (⛔ this seat never enqueues an ADR-path PR), not the mechanism. Anyone reading "red check, but not required" as "proceed" would ship it. Recording this on the card because the next seat will meet the same shape.

    My inference was wrong, and the dev's reading is why

    I measured that docs/adr/0112-*.md contains zero 7035 citations and inferred the ADR probably needed no edit — I put that in the dispatch brief and in my own patrol text. The measurement was right; the inference was wrong because I searched for the wrong marker.

    What actually needed changing is the #9106 amendment's Scope paragraph, which carves this exact door out of the closure and tells the reader that "#9232 is where the remaining question about the flat top-level code is recorded". Once this card is ruled, that paragraph is false — and landing the code without it would move the declared-≠-actual contradiction rather than remove it: the ADR would say the flat door is exempt while the code narrows. That is this card's own defect, inverted. It is also the ruling's bullet-4 logic pointed the other way, which is the right way to have found it.

    ⭐ A grep can only find the marker you thought of. This one needed a read.

    Ruling on the open question: A — ship as is, one PR, the maintainer merges.

    B (drop the ADR commit, enqueue the code half, land the ADR separately) is a legitimate throughput trade and I am declining it: it re-creates precisely this card's defect for the length of the gap, with the ADR telling readers the flat dialect is exempt while the code has already narrowed. The ruling's bullet 4 already settled that this class of prose gap ships in the same change — "Leaving it is the same declared-≠-actual defect class as the code change itself." C is not viable: the paragraph does not merely go stale, it actively instructs readers to treat the flat dialect as exempt and to consult a card that closes on merge.

    The isolation instruction was honoured exactly — ad0beb326 touches one file, +12/−2, across four commits that each do one thing. That made this decision cheap to take either way, which is the point of asking for it.

    Two things done right that CI would never have shown

    • check:type-check-debt went red first (@objectstack/rest TEST_DEBT 155 → 156, a TS2835 from an extensionless import in the new pin). Fixed at source with an explicit .js extension — the ratchet was not raised. That is the one temptation this gate exists to create, and it was declined.
    • The reverse verification caught a vacuous case of the dev's own making: §1's "presence means demotion" stayed green because all its assertions are also true of undefined. Strengthened with toBeTypeOf('string'). That is the second self-caught vacuous pin in this lane tonight, both found by asking what a case is for rather than whether it is green.

    Two findings filed, and the first is a gate that reports clean while blind

    Over to the maintainer for the merge. Nothing further from this seat on this PR.


    Generated by Claude Code

  9. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    Contributor

    Follow-up to the report above — origin/main merged for the renamed required check. Head is now 047457ca3 (was eb70f7f4a).

    Why

    Ruleset 12119582 now requires Lint & Repo Gates instead of ESLint, and the workflow PR that emits that context (#9421) landed after my first push. The old head could not emit it. Merged (never rebased) — no conflicts.

    Required-check status on 047457ca3

    Required context Status
    TypeScript Type Check 🟡 in_progress (~18 min; whole-monorepo typecheck)
    Lint & Repo Gates ✅ success
    Test Core ✅ success — rollup, plus shards (1/3) (2/3) (3/3) all green
    Dogfood Regression Gate ✅ success — rollup, plus shards (1/3) (2/3) (3/3) all green
    Build Core ✅ success
    Temporal Conformance (live PG + MySQL) ✅ success

    Five of six green; the sixth is still running. Local proxies for it were green on this exact tree: pnpm --filter @objectstack/rest typecheck clean, and check:type-check-debt --re-measure OK across all 33 ledger entries.

    ⚠️ ADR maintainer approval is FAILING, by design, and is NOT one of the six. Its own output: "The PR's current review standing is not APPROVED. No state-setting review has been submitted at all… Drafting and pushing this PR was fine and stays fine — only the MERGE is gated." It also records that the gate does not check who approved — anyone with review rights can clear it.

    ⛔ Auto-merge deliberately not armed, and no PR state flipped. That gate reads the auto-merge state, so arming it would carry the ADR change into the queue past the very approval it demands.

    Re-verified on the merged head, not carried over

    main moved 18 commits / 64 files, so nothing was assumed. pnpm install --frozen-lockfile, full closure rebuild (70/70 tasks, 68 not cached), then:

    • My diff is unchanged — still 12 files, +596/-55, four narrowed arms intact.
    • @objectstack/rest 125 files / 2058 tests · @objectstack/runtime 167 files / 2503 tests (4 more than before — main added them) · http-conformance 72 · dogfood 792 passed / 3 skipped on the rebuilt dist/ — all green.
    • Gate union (12 families) green at 047457ca3, plus check:type-check-debt --re-measure (33 entries, none above its recorded number) and check:route-envelope, check:dispatcher-error-vocabulary specifically.
    • Vehicle assumptions re-checked against the rebuilt union. Main registered FLOW_DISABLED and FLOW_NO_START_NODE; STORED_TYPE_NOT_CANONICAL was a comment-only edit. PACKAGE_IS_HAUNTED, owd_widening_forbidden, UPSTREAM_UNAVAILABLE, HOOK_REFUSED, X_FAULT are all still unregistered — so no §1 case went vacuous — and RECORD_LOCKED, CONNECTOR_UPSTREAM_UNAVAILABLE, ERR_HOOK_TARGET_REBIND, FIELD_VISIBILITY_UNRESOLVED still registered.
    • Downstream: 14 of the 16 dependents of @objectstack/rest declare a typecheck script; all 14 ran and passed. Counted rather than read off the exit code, since a filter matching no script exits 0.

    Nothing was adjusted to accommodate main.

    One thing worth knowing for finding #9461

    main rewrote scripts/check-route-envelope.mjs by 420 lines in this window — and left both #7035 ratchet: fields (lines 245 and 309) untouched. The rewrite added a new plugin-route audit with live #9364 owners and did not repoint the two stale ones, which is evidence the miss is systematic rather than incidental.

    The PR body has been updated to cite 047457ca3 throughout, after re-running the union on that head.


    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

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions