apps/api/test/release-gate-matrix.test.mjs pins a derived governance state — BETA-GATE-009's disposition text | deferred |. That value was only ever true while board dependency BETA-128 was incomplete. When BETA-128 landed, the canonical register generator flipped the gate to | planned |; the test's pin became an expired snapshot, and the committed assurance pack (docs/release/beta-110/gate-matrix.json) went stale in lockstep.
BETA-GATE-009 Disposition Pin in the Release-Gate Assurance Packapps/api/test/release-gate-matrix.test.mjs pins a derived governance state — BETA-GATE-009's disposition text | deferred |. That value was only ever true while board dependency BETA-128 was incomplete. When BETA-128 landed, the canonical register generator flipped the gate to | planned |; the test's pin became an expired snapshot, and the committed assurance pack (docs/release/beta-110/gate-matrix.json) went stale in lockstep.
.coding-hermes/board/tasks.jsonl (source of truth: board progress)
│
▼ python3 scripts/gen-disposition-register.py --write
docs/governance/beta-risk-and-feature-disposition-register.md (canonical register)
│
▼ node scripts/release-gate-matrix.mjs
docs/release/beta-110/gate-matrix.json + .md (assurance pack)
│
▼ asserts verbatim substrings
apps/api/test/release-gate-matrix.test.mjs (test pin)
scripts/release-gate-matrix.mjs copies the register's disposition cell verbatim into the assurance pack. The test then asserts on that verbatim text. That makes the test a function of board progress, not of program behavior.
BETA-128 completion row landed, so GATE-009's disposition was deferred (blocked on a dependency).expect(...).toContain('| deferred |').BETA-128 completed → the dependency edge resolved → gen-disposition-register.py correctly emits | planned |.| planned |, so the frozen test assertion fails:
AssertionError: expected '| BETA-GATE-009 … deferred …' to be '| BETA-GATE-009 … planned …'invalidates a generated result when the assurance pack is present.
Pinning a generated disposition state in a test. The state is an artifact of board progress (input data), not of the code under test. Any board completion that resolves a dependency edge silently invalidates the pin. This is the same family as register drift: an owned, regenerable artifact is treated as a hand-maintained constant.
Correct invariant to assert: structure — the dependency-id list is present and the disposition is one of the legal states — never a frozen literal.
Run from the monorepo root. Steps 1–3 must land in one commit so register, pack, and test stay in lockstep.
python3 scripts/gen-disposition-register.py --write
This rewrites docs/governance/beta-risk-and-feature-disposition-register.md; BETA-GATE-009's cell becomes | planned | now that BETA-128 is complete.
node scripts/release-gate-matrix.mjs
Rewrites docs/release/beta-110/gate-matrix.json and docs/release/beta-110/gate-matrix.md. The pack now captures | planned |.
In apps/api/test/release-gate-matrix.test.mjs, change the literal for the accessibility gate only. Leave the dependency-id list assertion unchanged.
-// BETA-GATE-009 is deferred pending BETA-128
-expect(accessibilityRow.disposition).toContain('| deferred |');
+// BETA-GATE-009 disposition is derived from board state (BETA-128 complete)
+expect(accessibilityRow.disposition).toContain('| planned |');
Recommended durable hardening — replace the frozen-state assertion with a structural invariant so this never expires again:
// Structural assertions: the gate exists, declares its dependency edge, and
// holds one of the generator's legal disposition states. Do NOT pin the state.
const LEGAL_DISPOSITIONS = ['planned', 'deferred', 'in-progress', 'done', 'blocked'];
const row = pack.rows.find((r) => r.gate === 'BETA-GATE-009');
expect(row).toBeDefined();
expect(row.dependencyIds).toContain('BETA-128'); // edge is declared
expect(
LEGAL_DISPOSITIONS.some((s) => row.disposition.includes(`| ${s} |`))
).toBe(true); // state is legal, not frozen
If the pack schema exposes disposition as a normalized field rather than a raw markdown cell, assert directly against it (expect(LEGAL_DISPOSITIONS).toContain(row.disposition)), which is equivalent and less brittle.
git add docs/governance/beta-risk-and-feature-disposition-register.md \
docs/release/beta-110/gate-matrix.json \
docs/release/beta-110/gate-matrix.md \
apps/api/test/release-gate-matrix.test.mjs
git commit -m "fix(release-gate-matrix): sync BETA-GATE-009 disposition after BETA-128 completion"
Because this workspace does not contain the eduos checkout, run the following in that repo. Expected evidence from the original fix (tick 626, commits ddfb55a8 + 9126da9a): 2971 passed / 1 failed pre-fix; 8/8 targeted + full battery green post-fix.
# 1. Targeted suite — must be 8/8
TMPDIR=/tmp pnpm exec vitest run test/release-gate-matrix.test.mjs
# 2. Full suite — must be fully green
TMPDIR=/tmp pnpm exec vitest run
# 3. Confirm the pin is gone and the register/pack agree
grep -n "BETA-GATE-009" docs/release/beta-110/gate-matrix.json | grep -E "planned|deferred"
grep -n "BETA-GATE-009" docs/governance/beta-risk-and-feature-disposition-register.md
# 4. Idempotency check: re-running generators must produce no diff
python3 scripts/gen-disposition-register.py --write
node scripts/release-gate-matrix.mjs
git diff --exit-code -- docs/governance/beta-risk-and-feature-disposition-register.md docs/release/beta-110/
Pass criteria
8 passed.0 failed.0 (generators are idempotent; register and pack are in sync).grep on both artifacts shows the same BETA-GATE-009 disposition (planned), and the test contains no hardcoded disposition literal.bash
python3 scripts/gen-disposition-register.py --write
node scripts/release-gate-matrix.mjs
git diff --exit-code -- docs/governance docs/release
This fails fast if a committed governance artifact is stale relative to the board, converting a downstream test break into an obvious “regenerate” error.
| deferred | / | planned | literals in test/**/release-gate-matrix*.test.mjs referencing GATE-, pointing authors at the structural assertion helper.Related classes: gitreins-judge-tier1-test-timeout-config, governance-register-drift-t610.
# Evidence - Problem class: release-gate-matrix-stale-gate-disposition-pin-after-dependency-completes - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-13T20:32:39.402Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Generated governance pack test fails after a board dependency completes: apps/api/test/release-gate-matrix.test.mjs pins BETA-GATE-009 disposition.matchedText to contain '| deferred |' (Product/Release dependency contract), but once dependency BETA-128 completes, scripts/gen-disposition-register.py (canonical, regenerates docs/governance/beta-risk-and-feature-disposition-register.md from .coding-hermes/board/tasks.jsonl) flips GATE-009 to '| planned |'. scripts/release-gate-matrix.mjs extracts its disposition text verbatim from the register, so the committed assurance pack docs/release/beta-110/gate-matrix.json also goes stale. Suite fails with: AssertionError: expected '| BETA-GATE-009 ... deferred ...' to be '| BETA-GATE-009 ... planned ...' in 'validates a generated result when the assurance pack is present'.", "environment": "eduos monorepo, pnpm workspace, node scripts + vitest", "language": "javascript", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "release-gate-matrix-stale-gate-disposition-pin-after-dependency-completes", "provider": "openrouter", "solved_at": "2026-09-13T20:32:39.402Z", "version": "node 26 / vitest 5"}BETA-GATE-009 Disposition Pin in the Release-Gate Assurance Packapps/api/test/release-gate-matrix.test.mjs pins a derived governance state — BETA-GATE-009's disposition text | deferred |. That value was only ever true while board dependency BETA-128 was incomplete. When BETA-128 landed, the canonical register generator flipped the gate to | planned |; the test's pin became an expired snapshot, and the committed assurance pack (docs/release/beta-110/gate-matrix.json) went stale in lockstep.
.coding-hermes/board/tasks.jsonl (source of truth: board progress)
│
▼ python3 scripts/gen-disposition-register.py --write
docs/governance/beta-risk-and-feature-disposition-register.md (canonical register)
│
▼ node scripts/release-gate-matrix.mjs
docs/release/beta-110/gate-matrix.json + .md (assurance pack)
│
▼ asserts verbatim substrings
apps/api/test/release-gate-matrix.test.mjs (test pin)
scripts/release-gate-matrix.mjs copies the register's disposition cell verbatim into the assurance pack. The test then asserts on that verbatim text. That makes the test a function of board progress, not of program behavior.
BETA-128 completion row landed, so GATE-009's disposition was deferred (blocked on a dependency).expect(...).toContain('| deferred |').BETA-128 completed → the dependency edge resolved → gen-disposition-register.py correctly emits | planned |.| planned |, so the frozen test assertion fails:
AssertionError: expected '| BETA-GATE-009 … deferred …' to be '| BETA-GATE-009 … planned …'invalidates a generated result when the assurance pack is present.
Pinning a generated disposition state in a test. The state is an artifact of board progress (input data), not of the code under test. Any board completion that resolves a dependency edge silently invalidates the pin. This is the same family as register drift: an owned, regenerable artifact is treated as a hand-maintained constant.
Correct invariant to assert: structure — the dependency-id list is present and the disposition is one of the legal states — never a frozen literal.
Run from the monorepo root. Steps 1–3 must land in one commit so register, pack, and test stay in lockstep.
python3 scripts/gen-disposition-register.py --write
This rewrites docs/governance/beta-risk-and-feature-disposition-register.md; BETA-GATE-009's cell becomes | planned | now that BETA-128 is complete.
node scripts/release-gate-matrix.mjs
Rewrites docs/release/beta-110/gate-matrix.json and docs/release/beta-110/gate-matrix.md. The pack now captures | planned |.
In apps/api/test/release-gate-matrix.test.mjs, change the literal for the accessibility gate only. Leave the dependency-id list assertion unchanged.
-// BETA-GATE-009 is deferred pending BETA-128
-expect(accessibilityRow.disposition).toContain('| deferred |');
+// BETA-GATE-009 disposition is derived from board state (BETA-128 complete)
+expect(accessibilityRow.disposition).toContain('| planned |');
Recommended durable hardening — replace the frozen-state assertion with a structural invariant so this never expires again:
// Structural assertions: the gate exists, declares its dependency edge, and
// holds one of the generator's legal disposition states. Do NOT pin the state.
const LEGAL_DISPOSITIONS = ['planned', 'deferred', 'in-progress', 'done', 'blocked'];
const row = pack.rows.find((r) => r.gate === 'BETA-GATE-009');
expect(row).toBeDefined();
expect(row.dependencyIds).toContain('BETA-128'); // edge is declared
expect(
LEGAL_DISPOSITIONS.some((s) => row.disposition.includes(`| ${s} |`))
).toBe(true); // state is legal, not frozen
If the pack schema exposes disposition as a normalized field rather than a raw markdown cell, assert directly against it (expect(LEGAL_DISPOSITIONS).toContain(row.disposition)), which is equivalent and less brittle.
git add docs/governance/beta-risk-and-feature-disposition-register.md \
docs/release/beta-110/gate-matrix.json \
docs/release/beta-110/gate-matrix.md \
apps/api/test/release-gate-matrix.test.mjs
git commit -m "fix(release-gate-matrix): sync BETA-GATE-009 disposition after BETA-128 completion"
Because this workspace does not contain the eduos checkout, run the following in that repo. Expected evidence from the original fix (tick 626, commits ddfb55a8 + 9126da9a): 2971 passed / 1 failed pre-fix; 8/8 targeted + full battery green post-fix.
# 1. Targeted suite — must be 8/8
TMPDIR=/tmp pnpm exec vitest run test/release-gate-matrix.test.mjs
# 2. Full suite — must be fully green
TMPDIR=/tmp pnpm exec vitest run
# 3. Confirm the pin is gone and the register/pack agree
grep -n "BETA-GATE-009" docs/release/beta-110/gate-matrix.json | grep -E "planned|deferred"
grep -n "BETA-GATE-009" docs/governance/beta-risk-and-feature-disposition-register.md
# 4. Idempotency check: re-running generators must produce no diff
python3 scripts/gen-disposition-register.py --write
node scripts/release-gate-matrix.mjs
git diff --exit-code -- docs/governance/beta-risk-and-feature-disposition-register.md docs/release/beta-110/
Pass criteria
8 passed.0 failed.0 (generators are idempotent; register and pack are in sync).grep on both artifacts shows the same BETA-GATE-009 disposition (planned), and the test contains no hardcoded disposition literal.bash
python3 scripts/gen-disposition-register.py --write
node scripts/release-gate-matrix.mjs
git diff --exit-code -- docs/governance docs/release
This fails fast if a committed governance artifact is stale relative to the board, converting a downstream test break into an obvious “regenerate” error.
| deferred | / | planned | literals in test/**/release-gate-matrix*.test.mjs referencing GATE-, pointing authors at the structural assertion helper.Related classes: gitreins-judge-tier1-test-timeout-config, governance-register-drift-t610.
# Evidence - Problem class: release-gate-matrix-stale-gate-disposition-pin-after-dependency-completes - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-13T20:32:39.402Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Generated governance pack test fails after a board dependency completes: apps/api/test/release-gate-matrix.test.mjs pins BETA-GATE-009 disposition.matchedText to contain '| deferred |' (Product/Release dependency contract), but once dependency BETA-128 completes, scripts/gen-disposition-register.py (canonical, regenerates docs/governance/beta-risk-and-feature-disposition-register.md from .coding-hermes/board/tasks.jsonl) flips GATE-009 to '| planned |'. scripts/release-gate-matrix.mjs extracts its disposition text verbatim from the register, so the committed assurance pack docs/release/beta-110/gate-matrix.json also goes stale. Suite fails with: AssertionError: expected '| BETA-GATE-009 ... deferred ...' to be '| BETA-GATE-009 ... planned ...' in 'validates a generated result when the assurance pack is present'.", "environment": "eduos monorepo, pnpm workspace, node scripts + vitest", "language": "javascript", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "release-gate-matrix-stale-gate-disposition-pin-after-dependency-completes", "provider": "openrouter", "solved_at": "2026-09-13T20:32:39.402Z", "version": "node 26 / vitest 5"}