Skip to content

Pin the boot consequence of a report-only drift entry through the real artifact boot gate (follow-up to #11535) #11728

Description

@huangyiirene

Filed unassigned by the domain:engine seat (session session_01VK8rFDtg8eREaxBGX99Csn) as a cross-lane follow-up: the work lands in packages/cli, which is not this seat's lane, so it goes to the queue rather than into the originating PR. ⛔ No domain:* set — this seat does not produce that label; triage to route (expected domain:cli).

Provenance

Raised by the #11535 dev in its report and deliberately not added, because packages/cli was outside that dispatch's declared file surface. Flagged for the PM to dispose of rather than silently skipped; this card is that disposal. Landed alongside PR #11720 (Part of #11535).

One-line executable criterion

A drift entry whose category is needs_confirm (specifically the new manual_column_type_change op) must not refuse an artifact-pinned boot — asserted by feeding a real entry through runArtifactBootMigrationGate itself, not by asserting the category string.

Why this pin is worth having

#11535's detection half rests entirely on one measured fact, and today that fact is pinned only structurally (the emitted category is asserted, with the measurement written into a comment). The fact is:

  • severity is read by no boot gate — the ordinary boot path warns on every entry regardless of it.
  • category === 'destructive' is read, by runArtifactBootMigrationGate (packages/cli/src/utils/artifact-boot-migration.ts:137) at kernel:ready, before the socket opens, and it refuses the boot.
  • Measured through the real gate function: destructive → ok=false; needs_confirm → ok=true; safe → ok=true.

That asymmetry is why #11535 could ship a loud finding without bricking the boot of every already-running deployment it exists to help — those deployments are, by the card's own account, all currently serving with the stale column. If a later change moved this op's category to destructive, or made the gate read severity as well, every affected deployment would stop booting, and nothing in packages/drivers/driver-sql can see that happen.

A pin in driver-sql cannot cover it: the gate lives in packages/cli and the driver package must not acquire a dependency on it.

Suggested shape (not prescriptive)

Next to packages/cli/src/utils/artifact-boot-migration.test.ts, feed a manual_column_type_change entry — category needs_confirm, severity error — through runArtifactBootMigrationGate and assert ok === true. ⚠️ Worth pairing it with a destructive control that asserts ok === false, so the test proves the gate can still refuse; without that control the assertion passes for a gate that has stopped refusing anything, which is the false green this whole family keeps producing.

Related

Activity

  1. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 26, 2026
  2. self-assigned this
    on Aug 26, 2026
  3. os-warren commented on Aug 26, 2026

    @os-warren
    Collaborator

    Claimed and dispatched

    This claim comment is going up in the same action as the assign, on this card. Spelling that out because I broke it earlier today on #11991 — assigned the card and sent its claim comment to a different issue, leaving it assigned with no evidence of which session held it for the whole dispatch. The rule only works if its failures are visible.

    What is being dispatched, and the one thing that decides whether it is worth anything

    The criterion is executable and the card states it precisely: a drift entry whose category is needs_confirm — the manual_column_type_change op — must not refuse an artifact-pinned boot, asserted by feeding a real entry through runArtifactBootMigrationGate itself, never by asserting the category string.

    ⭐ The card also names the trap, and the brief carries it as a hard requirement rather than a suggestion: pair it with a destructive control asserting ok === false. Without that control the assertion passes for a gate that has stopped refusing anything — which is precisely the false green this family keeps producing. A one-armed pin here would be worse than no pin, because it would look like coverage.

    Why the pin has to live in packages/cli

    The asymmetry it protects is real and load-bearing: severity is read by no boot gate, while category === 'destructive' is read by runArtifactBootMigrationGate at kernel:ready, before the socket opens, and refuses the boot. If a later change moved this op's category to destructive, or taught the gate to read severity too, every affected deployment would stop booting — and those deployments are, by #11535's own account, exactly the ones currently serving with the stale column. Nothing in packages/drivers/driver-sql can observe that happen, and the driver package must not acquire a dependency on the CLI to try.

    The dev is told to re-locate the gate by symbol on the current ref rather than trusting the card's artifact-boot-migration.ts:137 citation — the card is from 2026-08-24 and line numbers have moved under three separate landings today alone.

    Serial check: lands in packages/cli/src/utils/. Neither card in flight shares it — #12166 is in packages/objectql, #12313 in packages/platform-objects/src/identity/.


    Generated by Claude Code

  4. os-warren commented on Aug 26, 2026

    @os-warren
    Collaborator
    {
      "issue": 11728,
      "branch": "claude/issue-11728-artifact-boot-gate-pin",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/12598",
      "status": "done",
      "premise_still_valid": true,
      "clause2": "no",
      "clause2_evidence": "git diff --stat vs merge-base b72db0198 (three-dot): '1 file changed, 190 insertions(+)' — 0 deletions, and the single path is packages/cli/src/utils/artifact-boot-migration.report-only-drift.test.ts, a .test.ts. No production file is touched, so there is no surface on which accept/reject behaviour could move. Confirmed on the final commit 0f99e0693b with a clean `git status`. No existing assertion was changed: the sibling suite artifact-boot-migration.test.ts is untouched (0 deletions).",
      "gateTableReverified": "yes — re-derived by symbol on current origin/main, not from the card's line citation. `runArtifactBootMigrationGate` is at packages/cli/src/utils/artifact-boot-migration.ts:109 and the category read at :137 (`drift.filter((d) => d.category === 'destructive')`); the card's :137 citation happens to still land on it, but was re-located rather than trusted. All three rows reproduce, measured through the real gate function: destructive -> ok=false; needs_confirm -> ok=true; safe -> ok=true. The severity claim also holds: `grep -n severity` exits 1 (NO matches) in BOTH artifact-boot-migration.ts and commands/serve.ts, with the exit captured before any pipe. Stronger than the card's table in one respect: both non-safe rows are now REAL emitter output from diffManagedTable rather than hand-stamped entries, and the severities run OPPOSITE to the boot consequence (the entry that boots is severity 'error'; the entry that refuses is severity 'warning').",
      "summary": "Adds packages/cli/src/utils/artifact-boot-migration.report-only-drift.test.ts: three cases that drive REAL drift entries (built by @objectstack/driver-sql's diffManagedTable, not hand-stamped) through runArtifactBootMigrationGate. (1) the #11535 stale-column entry — op manual_column_type_change, category needs_confirm, severity error — does NOT refuse an artifact-pinned boot; (2) the required CONTROL: a real destructive entry (orphaned column, op drop_column, severity warning) still refuses, so the pin cannot pass against a gate that has stopped refusing anything; (3) with both present the refusal names the orphan alone and the stale entry still travels to the driver as ordinary non-destructive work. Using the real emitter is what makes this more than a restatement of the category string: the day schema-drift.ts emits this op as destructive, the pin goes red. No production change was needed — the gate's contract is observable from outside, so clause 2 stays 'no'. The pin lives in packages/cli because the gate does; the enabling edge already runs cli -> driver-sql (a CLI test already imports that drift surface at run time), so no package edge and no KNOWN_UNALIASED_TEST_IMPORTS entry was added.",
      "tests": "All figures from final commit 0f99e0693b (clean tree); gate union derived at that same commit via `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` over the real changeset (1 path, three-dot vs merge base b72db0198). Every exit captured with NO pipe between command and $?; each gate quoted by its OWN verdict line, never a bare $?. BASELINE: `pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2` over both artifact-boot-migration suites -> 'Test Files 2 passed (2) / Tests 14 passed (14)', VITEST_EXIT=0, wrapper 'VERDICT command-exit 0'. TYPECHECK: `pnpm --filter @objectstack/cli typecheck` EXIT=0 with the script echoed ('> @objectstack/cli@17.2.0 typecheck' / '> tsc --noEmit') — checked against the zero-match trap, it really ran. Selected gate verdict lines: check-test-source-alias 'OK — 72 packages with tests scanned; 61 registered as still resolving a workspace dep through dist/'; check-engine-double-contract 'OK — 689 pinned, 134 in the DEBT ledger, 3 exempt'; cross-package-test-inputs 'OK: 20 package(s) read outside themselves, all declared'; where-matcher and query-options-erasure both 'baseline key set verified against b72db01: no files added'; check-type-check-coverage --re-measure 'OK — 31 ledger entr(ies) re-measured in 240.2s, 1687 raw tsc error(s) total, none above its recorded number'; check-nul-bytes 'OK (scanned 6948 text file(s) ... no raw ASCII control bytes)'; check-i18n-bundles 'OK (9 package(s) — all bundles in sync)'; check-i18n-coverage 'OK (12 config(s), 602 baselined untranslated string(s), none new)'. All heavy runs went through scripts/pm/os-verify-lock.sh (waits up to 7m33s were observed and spent on lock-free work, never on an idle round).",
      "gates": {
        "green": [
          "pnpm --filter @objectstack/cli typecheck (exit 0, script echoed)",
          "affected suites: artifact-boot-migration.test.ts + artifact-boot-migration.report-only-drift.test.ts (14/14)",
          "check:cross-package-test-inputs",
          "node scripts/check-cross-package-test-inputs.mjs (ci.yml spelling)",
          "check:i18n",
          "check:i18n-coverage",
          "check:objectql-double-limit",
          "check:page-declaration-shape",
          "check:published-files",
          "check:slot-lookup",
          "check:test-source-alias",
          "check:type-source-resolution",
          "node scripts/check-ci-filter-parity.mjs",
          "node scripts/check-comment-mask-adoption.mjs",
          "node scripts/check-plugin-teardown-shape.mjs",
          "node scripts/docs-audit/check-affected-docs.mjs",
          "node scripts/docs-audit/check-drift-comment.mjs",
          "check:query-options-erasure (convention: adds a test file)",
          "check:type-check-coverage (convention)",
          "check:type-check-debt --re-measure (convention; needed the workspace closure built)",
          "check:engine-double-contract (convention)",
          "check:where-matcher (convention)",
          "check:nul-bytes"
        ],
        "red": [],
        "notMeasured": [
          "The FULL @objectstack/cli package suite (185 files, ~13 min per its own vitest.config.ts measurements) — DECLARED NARROWING, not an omission: the two affected files were run in full and CI runs the package suite anyway. Nothing else in the union was left unmeasured.",
          "Recorded because the distinction matters: check:i18n and check:i18n-coverage FIRST returned exit 1 as 'PREREQUISITE NOT MET' / 'COULD NOT MEASURE' on this fresh worktree (unbuilt CLI, then unbuilt connector-mcp/connector-openapi). That is NOT MEASURED, not red — both gates say so in their own text ('Nothing was checked'). Both were then re-measured GREEN after building, and are reported under green as measured results."
        ]
      },
      "ablations": [
        {
          "name": "A — the #11728 risk realised: the gate starts refusing needs_confirm",
          "mutation": "packages/cli/src/utils/artifact-boot-migration.ts:137  `d.category === 'destructive'` -> `d.category !== 'safe'`",
          "predicted": "RED, exactly 3 failures / 14: the existing 'applies needs_confirm alongside safe', plus my new pin case, plus my new combined case. The destructive CONTROL predicted to stay GREEN (insensitive to this mutation by design).",
          "observed": "EXACT MATCH — 'Tests 3 failed | 11 passed (14)', VITEST_EXIT=1, and precisely the three predicted test names. Control stayed green as predicted.",
          "mutationProvenOnDisk": "Anchored grep -cF counts read BEFORE any result: original 1->0, mutation 0->1; plus `git diff --stat` (1 insertion, 1 deletion) and the full hunk. The mutator REFUSES to write unless the anchor matches exactly once (exit 3), so a zero-hit no-op cannot masquerade as a run.",
          "rebuild": "None needed, justified by IMPORT FORM not assertion: both suites reach the gate as './artifact-boot-migration.js', a RELATIVE specifier inside the same package, which vite resolves to the .ts SOURCE — no exports field, no dist consulted. (The new suite's BARE '@objectstack/driver-sql' import does resolve through exports to dist and was built beforehand; it is not mutated by either ablation.) Both ablations predicted RED, so a mutation failing to reach the running code would surface as all-green rather than hide.",
          "restore": "trap '<restore>' EXIT INT TERM; restore is `git checkout <branch> -- <gate path>` (never `git checkout HEAD -- .`), verified with RESTORE_DIFF_LINES=0, original line present 1, mutation line present 0."
        },
        {
          "name": "B — the gate stops refusing ANYTHING (the false green the control exists for)",
          "mutation": "packages/cli/src/utils/artifact-boot-migration.ts:137  `d.category === 'destructive'` -> `d.category === ('nonexistent_category' as any)` (a stale read of a renamed vocabulary)",
          "predicted": "RED, exactly 4 failures / 14: the two existing destructive cases, my CONTROL, and my combined case — and CRUCIALLY my pin case predicted to stay GREEN. Also predicted green: 'never applies a destructive change on its own authority' (the destructive entry is excluded from the safe half too by a separate unmutated filter, so the driver is never called and .every() over an empty array is vacuously true).",
          "observed": "EXACT MATCH — 'Tests 4 failed | 10 passed (14)', VITEST_EXIT=1, precisely the four predicted names, and the pin case is absent from the failure list, i.e. it passed. The vacuous-.every() prediction held too.",
          "whyItMatters": "This is the measured argument for the control being mandatory. A one-armed pin passes in full colour against a gate that refuses nothing whatsoever — it would have reported coverage of a boot decision no longer being made. The control is the arm that goes red there.",
          "mutationProvenOnDisk": "Same anchored grep -cF discipline, counts read before any result: original 1->0, mutation 0->1.",
          "restore": "Same trap-based restore; RESTORE_DIFF_LINES=0, verified, and `git status` clean afterwards."
        }
      ],
      "openQuestions": [],
      "out_of_scope_findings": [
        "NOT FILED — dedup channel unavailable from this seat, flagging for PM disposal rather than filing blind. Direct REST (api.github.com) returns 'GitHub access is not enabled for this session', and the MCP list/search issue tools are the banned GraphQL channel for dedup reads, so I could not run the required search-before-filing. Both items below are self-announcing on EVERY dispatch-gates run for every card, so the PM has near-certainly seen them; filing without a dedup read risked duplicates. (1) OBSERVATION: dispatch-gates reports 9 check families whose declared path population matches NOTHING in the tree — 'THE LAYOUT MOVED under a gate that still spells the old path — a real miss, worth triaging', all @objectstack/spec (check:api-surface, check:dual-source-exports, check:entry-nameability, check:export-origins, check:exported-any, check:meta-url-spelling, check:spec-changes, check:strictness-ledger, check:upgrade-guide). They score a quiet green for every card whether they still work or not. (2) OBSERVATION: check:type-check-debt reports @objectstack/runtime TEST_DEBT records 227 while tsc now measures 226 (-1); the gate itself calls this 'not an error' but notes 1 new error can land in that layer while the gate still reports success. Closed with `pnpm check:type-check-debt --lower`.",
        "NOT a finding, recorded so it is not mistaken for one: the sibling suite already contains a generic needs_confirm case built from a hand-stamped entry. It is correct and was left untouched; it simply cannot observe the emitter, which is exactly the gap this card fills."
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

  5. os-warren commented on Aug 26, 2026

    @os-warren
    Collaborator

    os-dev-report

    ⚠️ Supersedes the previous comment on this card. That one opened with the HTML-comment form of this marker and the GitHub body sanitizer ATE it on write — verified by reading the comment back, whose body now starts with a bare fenced block and no marker at all, so it is invisible to a scan looking for the marker. The same sanitizer also swallowed three short angle-bracket fragments inside the restore fields there (trap '...' EXIT INT TERM lost its argument, and git checkout ... -- ... lost both), which is why this repost spells every placeholder without angle brackets. This comment carries the authoritative report; the earlier one differs only by those deletions.

    {
      "issue": 11728,
      "branch": "claude/issue-11728-artifact-boot-gate-pin",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/12598",
      "status": "done",
      "premise_still_valid": true,
      "clause2": "no",
      "clause2_evidence": "git diff --stat vs merge-base b72db0198 (three-dot): '1 file changed, 190 insertions(+)' — 0 deletions, and the single path is packages/cli/src/utils/artifact-boot-migration.report-only-drift.test.ts, a .test.ts. No production file is touched, so there is no surface on which accept/reject behaviour could move. Confirmed on the final commit 0f99e0693b with a clean `git status`. No existing assertion was changed: the sibling suite artifact-boot-migration.test.ts is untouched (0 deletions).",
      "gateTableReverified": "yes — re-derived by symbol on current origin/main, not from the card's line citation. `runArtifactBootMigrationGate` is at packages/cli/src/utils/artifact-boot-migration.ts:109 and the category read at :137 (`drift.filter((d) => d.category === 'destructive')`); the card's :137 citation happens to still land on it, but was re-located rather than trusted. All three rows reproduce, measured through the real gate function: destructive -> ok=false; needs_confirm -> ok=true; safe -> ok=true. The severity claim also holds: `grep -n severity` exits 1 (NO matches) in BOTH artifact-boot-migration.ts and commands/serve.ts, with the exit captured before any pipe. Stronger than the card's table in one respect: both non-safe rows are now REAL emitter output from diffManagedTable rather than hand-stamped entries, and the severities run OPPOSITE to the boot consequence (the entry that boots is severity 'error'; the entry that refuses is severity 'warning').",
      "summary": "Adds packages/cli/src/utils/artifact-boot-migration.report-only-drift.test.ts: three cases that drive REAL drift entries (built by @objectstack/driver-sql's diffManagedTable, not hand-stamped) through runArtifactBootMigrationGate. (1) the #11535 stale-column entry — op manual_column_type_change, category needs_confirm, severity error — does NOT refuse an artifact-pinned boot; (2) the required CONTROL: a real destructive entry (orphaned column, op drop_column, severity warning) still refuses, so the pin cannot pass against a gate that has stopped refusing anything; (3) with both present the refusal names the orphan alone and the stale entry still travels to the driver as ordinary non-destructive work. Using the real emitter is what makes this more than a restatement of the category string: the day schema-drift.ts emits this op as destructive, the pin goes red. No production change was needed — the gate's contract is observable from outside, so clause 2 stays 'no'. The pin lives in packages/cli because the gate does; the enabling edge already runs cli -> driver-sql (a CLI test already imports that drift surface at run time), so no package edge and no KNOWN_UNALIASED_TEST_IMPORTS entry was added.",
      "tests": "All figures from final commit 0f99e0693b (clean tree); gate union derived at that same commit via `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` over the real changeset (1 path, three-dot vs merge base b72db0198). Every exit captured with NO pipe between command and $?; each gate quoted by its OWN verdict line, never a bare $?. BASELINE: `pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2` over both artifact-boot-migration suites -> 'Test Files 2 passed (2) / Tests 14 passed (14)', VITEST_EXIT=0, wrapper 'VERDICT command-exit 0'. TYPECHECK: `pnpm --filter @objectstack/cli typecheck` EXIT=0 with the script echoed ('@objectstack/cli@17.2.0 typecheck' / 'tsc --noEmit') — checked against the zero-match trap, it really ran. Selected gate verdict lines: check-test-source-alias 'OK — 72 packages with tests scanned; 61 registered as still resolving a workspace dep through dist/'; check-engine-double-contract 'OK — 689 pinned, 134 in the DEBT ledger, 3 exempt'; cross-package-test-inputs 'OK: 20 package(s) read outside themselves, all declared'; where-matcher and query-options-erasure both 'baseline key set verified against b72db01: no files added'; check-type-check-coverage --re-measure 'OK — 31 ledger entr(ies) re-measured in 240.2s, 1687 raw tsc error(s) total, none above its recorded number'; check-nul-bytes 'OK (scanned 6948 text file(s) ... no raw ASCII control bytes)'; check-i18n-bundles 'OK (9 package(s) — all bundles in sync)'; check-i18n-coverage 'OK (12 config(s), 602 baselined untranslated string(s), none new)'. All heavy runs went through scripts/pm/os-verify-lock.sh (waits up to 7m33s were observed and spent on lock-free work, never on an idle round).",
      "gates": {
        "green": [
          "pnpm --filter @objectstack/cli typecheck (exit 0, script echoed)",
          "affected suites: artifact-boot-migration.test.ts + artifact-boot-migration.report-only-drift.test.ts (14/14)",
          "check:cross-package-test-inputs",
          "node scripts/check-cross-package-test-inputs.mjs (ci.yml spelling)",
          "check:i18n",
          "check:i18n-coverage",
          "check:objectql-double-limit",
          "check:page-declaration-shape",
          "check:published-files",
          "check:slot-lookup",
          "check:test-source-alias",
          "check:type-source-resolution",
          "node scripts/check-ci-filter-parity.mjs",
          "node scripts/check-comment-mask-adoption.mjs",
          "node scripts/check-plugin-teardown-shape.mjs",
          "node scripts/docs-audit/check-affected-docs.mjs",
          "node scripts/docs-audit/check-drift-comment.mjs",
          "check:query-options-erasure (convention: adds a test file)",
          "check:type-check-coverage (convention)",
          "check:type-check-debt --re-measure (convention; needed the workspace closure built)",
          "check:engine-double-contract (convention)",
          "check:where-matcher (convention)",
          "check:nul-bytes"
        ],
        "red": [],
        "notMeasured": [
          "The FULL @objectstack/cli package suite (185 files, ~13 min per its own vitest.config.ts measurements) — DECLARED NARROWING, not an omission: the two affected files were run in full and CI runs the package suite anyway. Nothing else in the union was left unmeasured.",
          "Recorded because the distinction matters: check:i18n and check:i18n-coverage FIRST returned exit 1 as 'PREREQUISITE NOT MET' / 'COULD NOT MEASURE' on this fresh worktree (unbuilt CLI, then unbuilt connector-mcp/connector-openapi). That is NOT MEASURED, not red — both gates say so in their own text ('Nothing was checked'). Both were then re-measured GREEN after building, and are reported under green as measured results."
        ]
      },
      "ablations": [
        {
          "name": "A — the #11728 risk realised: the gate starts refusing needs_confirm",
          "mutation": "packages/cli/src/utils/artifact-boot-migration.ts:137  `d.category === 'destructive'` becomes `d.category !== 'safe'`",
          "predicted": "RED, exactly 3 failures / 14: the existing 'applies needs_confirm alongside safe', plus my new pin case, plus my new combined case. The destructive CONTROL predicted to stay GREEN (insensitive to this mutation by design).",
          "observed": "EXACT MATCH — 'Tests 3 failed | 11 passed (14)', VITEST_EXIT=1, and precisely the three predicted test names. Control stayed green as predicted.",
          "mutationProvenOnDisk": "Anchored grep -cF counts read BEFORE any result: original 1 then 0, mutation 0 then 1; plus `git diff --stat` (1 insertion, 1 deletion) and the full hunk. The mutator REFUSES to write unless the anchor matches exactly once (exit 3), so a zero-hit no-op cannot masquerade as a run.",
          "rebuild": "None needed, justified by IMPORT FORM not assertion: both suites reach the gate as './artifact-boot-migration.js', a RELATIVE specifier inside the same package, which vite resolves to the .ts SOURCE — no exports field, no dist consulted. (The new suite's BARE '@objectstack/driver-sql' import does resolve through exports to dist and was built beforehand; it is not mutated by either ablation.) Both ablations predicted RED, so a mutation failing to reach the running code would surface as all-green rather than hide.",
          "restore": "trap RESTORE_FUNCTION EXIT INT TERM; restore is `git checkout THIS_BRANCH -- THE_GATE_PATH` (never `git checkout HEAD -- .`), verified with RESTORE_DIFF_LINES=0, original line present 1, mutation line present 0."
        },
        {
          "name": "B — the gate stops refusing ANYTHING (the false green the control exists for)",
          "mutation": "packages/cli/src/utils/artifact-boot-migration.ts:137  `d.category === 'destructive'` becomes `d.category === ('nonexistent_category' as any)` (a stale read of a renamed vocabulary)",
          "predicted": "RED, exactly 4 failures / 14: the two existing destructive cases, my CONTROL, and my combined case — and CRUCIALLY my pin case predicted to stay GREEN. Also predicted green: 'never applies a destructive change on its own authority' (the destructive entry is excluded from the safe half too by a separate unmutated filter, so the driver is never called and .every() over an empty array is vacuously true).",
          "observed": "EXACT MATCH — 'Tests 4 failed | 10 passed (14)', VITEST_EXIT=1, precisely the four predicted names, and the pin case is absent from the failure list, i.e. it passed. The vacuous-.every() prediction held too.",
          "whyItMatters": "This is the measured argument for the control being mandatory. A one-armed pin passes in full colour against a gate that refuses nothing whatsoever — it would have reported coverage of a boot decision no longer being made. The control is the arm that goes red there.",
          "mutationProvenOnDisk": "Same anchored grep -cF discipline, counts read before any result: original 1 then 0, mutation 0 then 1.",
          "restore": "Same trap-based restore; RESTORE_DIFF_LINES=0, verified, and `git status` clean afterwards."
        }
      ],
      "openQuestions": [],
      "out_of_scope_findings": [
        "NOT FILED — dedup channel unavailable from this seat, flagging for PM disposal rather than filing blind. Direct REST (api.github.com) returns 'GitHub access is not enabled for this session', and the MCP list/search issue tools are the banned GraphQL channel for dedup reads, so I could not run the required search-before-filing. Both items below are self-announcing on EVERY dispatch-gates run for every card, so the PM has near-certainly seen them; filing without a dedup read risked duplicates. (1) OBSERVATION: dispatch-gates reports 9 check families whose declared path population matches NOTHING in the tree — 'THE LAYOUT MOVED under a gate that still spells the old path — a real miss, worth triaging', all @objectstack/spec (check:api-surface, check:dual-source-exports, check:entry-nameability, check:export-origins, check:exported-any, check:meta-url-spelling, check:spec-changes, check:strictness-ledger, check:upgrade-guide). They score a quiet green for every card whether they still work or not. (2) OBSERVATION: check:type-check-debt reports @objectstack/runtime TEST_DEBT records 227 while tsc now measures 226 (-1); the gate itself calls this 'not an error' but notes 1 new error can land in that layer while the gate still reports success. Closed with `pnpm check:type-check-debt --lower`.",
        "NOT a finding, recorded so it is not mistaken for one: the sibling suite already contains a generic needs_confirm case built from a hand-stamped entry. It is correct and was left untouched; it simply cannot observe the emitter, which is exactly the gap this card fills.",
        "PROCESS OBSERVATION for the PM, no issue filed: the `<!-- os-dev-report -->` marker the dev contract prescribes does NOT survive this repo's comment sanitizer — measured on this card by reading the comment back immediately after writing it. Any seat that writes it and does not read back leaves a report the PM's marker scan cannot see."
      ]
    }

    Generated by Claude Code


    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

Labels

domain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they landpm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions