The code fix was already committed and every real test passed (packages/shared 68/68, apps/web 656/656). The only failure was in apps/api: release-gate-matrix.test.mjs → generated-appendix-differs-from-register.
Written to ~/gitreins-tier1-stale-generated-appendix.md.
generated-appendix-differs-from-register)The code fix was already committed and every real test passed (packages/shared 68/68, apps/web 656/656). The only failure was in apps/api: release-gate-matrix.test.mjs → generated-appendix-differs-from-register.
This is not a code regression. The generated artifacts (disposition register appendix + gate-matrix pack) were regenerated before the task-close rows 52822758 and a848bba5 were appended to the board. At judge time the canonical event log had moved on, so the checked-in appendix was stale relative to the register. The docs-only commit under evaluation touched none of these files, so it inherited a tree-state ordering defect from the previous tick.
Fix: re-run the regen chain after all board appends, confirm the appendix diff is exactly the completion flips of the rows closed that tick, commit it, then judge the settled tree.
The disposition register is the canonical source of truth. The generated appendix and the gate-matrix pack are derived artifacts. For the release-gate check to be stable, this ordering must hold:
board appends (task-close events) -> regen derived artifacts -> commit -> judge
The failing tick violated it. The sequence was effectively:
regen derived artifacts -> commit code fix -> board appends (52822758, a848bba5) -> judge
So the board/register gained two completion events after the last regeneration. The appendix still described the pre-close state, and generated-appendix-differs-from-register fired because it diffs the generated appendix against the current register.
apps/api failed only on generated-appendix-differs-from-register.packages/shared and apps/web were fully green.Together these prove the failure is a tree-state ordering artifact, not a behavioral regression introduced by the diff being evaluated. A judge that does not distinguish "diff caused it" from "diff inherited a dirty derived-state tree" will flag the wrong change.
| Row id | Event | Landed after |
|---|---|---|
52822758 |
task-close | last artifact regen |
a848bba5 |
task-close | last artifact regen |
Both are completion flips that the appendix must reflect. Because they landed late, the appendix's expected diff never included them.
Run the regen chain after every board append in the tick, on the same working tree. Do not reorder the steps.
# 0. Preconditions: all board appends for this tick are already done.
# Confirm the board is settled (no inline editor / pending write).
git status --porcelain
# 1. Regenerate the disposition register appendix from canonical events.
python3 scripts/gen-disposition-register.py --write
# 2. Validate it against the register (this is what the gate test checks).
python3 scripts/gen-disposition-register.py --validate
# 3. Regenerate the gate-matrix pack.
node scripts/release-gate-matrix.mjs
# 4. Inspect the appendix diff. It MUST contain exactly the completion flips
# of the rows closed this tick (here: 52822758 and a848bba5) and nothing else.
git --no-pager diff -- \
docs/disposition-register-appendix.md \
release-gate-matrix.pack.json
# 5. Commit ONLY the regenerated artifacts.
git add docs/disposition-register-appendix.md release-gate-matrix.pack.json
git commit -m "chore(regenerate): refresh disposition appendix + gate-matrix after task-close 52822758, a848bba5"
# 6. Judge the settled tree.
gitreins task complete
Adjust the exact artifact paths in step 4/5 to your repo layout (
docs/...,reports/..., etc.). The invariant — board appends before regen — is the part that matters.
The appendix diff should be additive-only for the closed rows. A useful check:
# Show only the row ids touched by the appendix diff.
git --no-pager diff -U0 -- docs/disposition-register-appendix.md \
| grep -E '^[+-]' \
| grep -vE '^(\+\+\+|---)' \
| grep -oE '(52822758|a848bba5)' | sort -u
Expected output is exactly:
52822758
a848bba5
If the diff touches any other row, stop: a stale regen has been mixed with unrelated changes, and you should reset the derived files and redo steps 1–3.
Add a pre-judge check so this class of failure can never reach the gate. Put it at the top of the tick-completion path, before gitreins task complete:
#!/usr/bin/env bash
# scripts/prejudge-board-freshness.sh
# Fail if the board was appended after the last derived-artifact regen.
set -euo pipefail
REGISTER_LOG="${REGISTER_LOG:-.eduos/disposition-register.jsonl}"
APPENDIX="${APPENDIX:-docs/disposition-register-appendix.md}"
if [ ! -f "$REGISTER_LOG" ] || [ ! -f "$APPENDIX" ]; then
echo "prejudge: missing register log or appendix; run the regen chain first" >&2
exit 1
fi
# Newest board append vs. newest regen commit touching the appendix.
last_append=$(git log -1 --format=%ct -- "$REGISTER_LOG" 2>/dev/null || echo 0)
last_regen=$(git log -1 --format=%ct -- "$APPENDIX" 2>/dev/null || echo 0)
if [ "$last_append" -gt "$last_regen" ]; then
echo "prejudge: board appends are newer than the generated appendix." >&2
echo " board: $REGISTER_LOG ($(date -d @$last_append 2>/dev/null || echo $last_append))" >&2
echo " regen: $APPENDIX ($(date -d @$last_regen 2>/dev/null || echo $last_regen))" >&2
echo " Run: python3 scripts/gen-disposition-register.py --write && --validate && node scripts/release-gate-matrix.mjs" >&2
exit 1
fi
# Belt and braces: the generated output must equal the committed file.
python3 scripts/gen-disposition-register.py --validate
node scripts/release-gate-matrix.mjs --check
echo "prejudge: board is settled and derived artifacts are fresh"
Wire it in so it is impossible to judge a stale tree:
# .git/hooks/pre-commit (or the CI judge entrypoint), before completion
scripts/prejudge-board-freshness.sh
The ordering rule to encode in process docs:
Never judge a tree whose board was appended after its last regen.
The fix is considered verified when all of the following hold.
# From apps/api (or wherever the test lives)
node --test release-gate-matrix.test.mjs
# or the repo's runner:
pnpm --filter apps/api run test
Expected: generated-appendix-differs-from-register is gone; the suite passes.
pnpm -r run test
Expected, matching the original evidence baseline:
| Package | Result |
|---|---|
packages/shared |
68/68 pass |
apps/web |
656/656 pass |
apps/api |
pass, including release-gate-matrix.test.mjs |
git --no-pager diff --stat HEAD~1 -- docs/disposition-register-appendix.md release-gate-matrix.pack.json
git --no-pager diff -U0 HEAD~1 -- docs/disposition-register-appendix.md \
| grep -E '^[+-]' | grep -vE '^(\+\+\+|---)' | grep -oE '(52822758|a848bba5)' | sort -u
Expected: only 52822758 and a848bba5 appear; no other row ids.
# Simulate out-of-order state: regen, then append, without re-regen.
git checkout HEAD~1 -- docs/disposition-register-appendix.md release-gate-matrix.pack.json
node --test release-gate-matrix.test.mjs
# -> FAIL generated-appendix-differs-from-register
# Restore the fix
git checkout HEAD -- docs/disposition-register-appendix.md release-gate-matrix.pack.json
node --test release-gate-matrix.test.mjs
# -> PASS
If the second run passes while the first fails, the root cause (ordering, not code) is confirmed.
scripts/prejudge-board-freshness.sh # exits 0
gitreins task complete # succeeds on the settled tree
gen-disposition-register.py --write then --validate both succeed.release-gate-matrix.mjs regenerated after the last append.52822758 / a848bba5.pnpm -r run test green across packages/shared, apps/web, apps/api.gitreins task complete run on the settled tree.scripts/prejudge-board-freshness.sh wired into the pre-judge path.| Item | Value |
|---|---|
| Symptom | tier1 FAIL on generated-appendix-differs-from-register while tier2 PASS |
| Misread as | code regression in the docs-only commit under evaluation |
| Actual cause | board task-closes 52822758/a848bba5 appended after the last artifact regen |
| Fix | board appends → regen → commit → judge |
| Prevention | prejudge-board-freshness.sh blocks judging a tree older than its board |
Note: The repository itself was not present in this environment, so the paths above are parameterized to the layout described in the incident. The core script commands (gen-disposition-register.py --write/--validate, release-gate-matrix.mjs) reproduce the remedy exactly; adjust only the artifact paths in the diff/commit steps if your layout differs.
# Evidence - Problem class: gitreins-tier1-fails-when-board-appends-postdate-generated-artifact-regen - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-13T23:45:13.155Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "EduOS t629: gitreins tier2 PASS but tier1 FAIL (pnpm -r run test, exit 1) on a tree where the code fix was already committed. packages/shared 68/68 and apps/web 656/656 passed; apps/api failed only release-gate-matrix.test.mjs generated-appendix-differs-from-register. Root cause: generated artifacts (disposition register appendix, gate-matrix pack) had been regenerated BEFORE the ticks final board appends (task-close rows 52822758/a848bba5 landed after the last regen), so the appendix was stale against canonical events at judge time - a tree-state ordering issue, not a regression; the docs-only commit under evaluation did not touch any of it. Remedy that worked: rerun the regen chain AFTER all board appends (python3 scripts/gen-disposition-register.py --write then --validate, node scripts/release-gate-matrix.mjs), verify the appendix diff contains exactly the completion-flips of rows closed that tick, commit the regen, then run gitreins task complete on the settled tree. Prevention: sequence board appends -> regen -> commit -> judge; never judge a tree whose board was appended after its last regen.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "gitreins-tier1-fails-when-board-appends-postdate-generated-artifact-regen", "provider": "openrouter", "solved_at": "2026-09-13T23:45:13.155Z", "version": ""}Written to ~/gitreins-tier1-stale-generated-appendix.md.
generated-appendix-differs-from-register)The code fix was already committed and every real test passed (packages/shared 68/68, apps/web 656/656). The only failure was in apps/api: release-gate-matrix.test.mjs → generated-appendix-differs-from-register.
This is not a code regression. The generated artifacts (disposition register appendix + gate-matrix pack) were regenerated before the task-close rows 52822758 and a848bba5 were appended to the board. At judge time the canonical event log had moved on, so the checked-in appendix was stale relative to the register. The docs-only commit under evaluation touched none of these files, so it inherited a tree-state ordering defect from the previous tick.
Fix: re-run the regen chain after all board appends, confirm the appendix diff is exactly the completion flips of the rows closed that tick, commit it, then judge the settled tree.
The disposition register is the canonical source of truth. The generated appendix and the gate-matrix pack are derived artifacts. For the release-gate check to be stable, this ordering must hold:
board appends (task-close events) -> regen derived artifacts -> commit -> judge
The failing tick violated it. The sequence was effectively:
regen derived artifacts -> commit code fix -> board appends (52822758, a848bba5) -> judge
So the board/register gained two completion events after the last regeneration. The appendix still described the pre-close state, and generated-appendix-differs-from-register fired because it diffs the generated appendix against the current register.
apps/api failed only on generated-appendix-differs-from-register.packages/shared and apps/web were fully green.Together these prove the failure is a tree-state ordering artifact, not a behavioral regression introduced by the diff being evaluated. A judge that does not distinguish "diff caused it" from "diff inherited a dirty derived-state tree" will flag the wrong change.
| Row id | Event | Landed after |
|---|---|---|
52822758 |
task-close | last artifact regen |
a848bba5 |
task-close | last artifact regen |
Both are completion flips that the appendix must reflect. Because they landed late, the appendix's expected diff never included them.
Run the regen chain after every board append in the tick, on the same working tree. Do not reorder the steps.
# 0. Preconditions: all board appends for this tick are already done.
# Confirm the board is settled (no inline editor / pending write).
git status --porcelain
# 1. Regenerate the disposition register appendix from canonical events.
python3 scripts/gen-disposition-register.py --write
# 2. Validate it against the register (this is what the gate test checks).
python3 scripts/gen-disposition-register.py --validate
# 3. Regenerate the gate-matrix pack.
node scripts/release-gate-matrix.mjs
# 4. Inspect the appendix diff. It MUST contain exactly the completion flips
# of the rows closed this tick (here: 52822758 and a848bba5) and nothing else.
git --no-pager diff -- \
docs/disposition-register-appendix.md \
release-gate-matrix.pack.json
# 5. Commit ONLY the regenerated artifacts.
git add docs/disposition-register-appendix.md release-gate-matrix.pack.json
git commit -m "chore(regenerate): refresh disposition appendix + gate-matrix after task-close 52822758, a848bba5"
# 6. Judge the settled tree.
gitreins task complete
Adjust the exact artifact paths in step 4/5 to your repo layout (
docs/...,reports/..., etc.). The invariant — board appends before regen — is the part that matters.
The appendix diff should be additive-only for the closed rows. A useful check:
# Show only the row ids touched by the appendix diff.
git --no-pager diff -U0 -- docs/disposition-register-appendix.md \
| grep -E '^[+-]' \
| grep -vE '^(\+\+\+|---)' \
| grep -oE '(52822758|a848bba5)' | sort -u
Expected output is exactly:
52822758
a848bba5
If the diff touches any other row, stop: a stale regen has been mixed with unrelated changes, and you should reset the derived files and redo steps 1–3.
Add a pre-judge check so this class of failure can never reach the gate. Put it at the top of the tick-completion path, before gitreins task complete:
#!/usr/bin/env bash
# scripts/prejudge-board-freshness.sh
# Fail if the board was appended after the last derived-artifact regen.
set -euo pipefail
REGISTER_LOG="${REGISTER_LOG:-.eduos/disposition-register.jsonl}"
APPENDIX="${APPENDIX:-docs/disposition-register-appendix.md}"
if [ ! -f "$REGISTER_LOG" ] || [ ! -f "$APPENDIX" ]; then
echo "prejudge: missing register log or appendix; run the regen chain first" >&2
exit 1
fi
# Newest board append vs. newest regen commit touching the appendix.
last_append=$(git log -1 --format=%ct -- "$REGISTER_LOG" 2>/dev/null || echo 0)
last_regen=$(git log -1 --format=%ct -- "$APPENDIX" 2>/dev/null || echo 0)
if [ "$last_append" -gt "$last_regen" ]; then
echo "prejudge: board appends are newer than the generated appendix." >&2
echo " board: $REGISTER_LOG ($(date -d @$last_append 2>/dev/null || echo $last_append))" >&2
echo " regen: $APPENDIX ($(date -d @$last_regen 2>/dev/null || echo $last_regen))" >&2
echo " Run: python3 scripts/gen-disposition-register.py --write && --validate && node scripts/release-gate-matrix.mjs" >&2
exit 1
fi
# Belt and braces: the generated output must equal the committed file.
python3 scripts/gen-disposition-register.py --validate
node scripts/release-gate-matrix.mjs --check
echo "prejudge: board is settled and derived artifacts are fresh"
Wire it in so it is impossible to judge a stale tree:
# .git/hooks/pre-commit (or the CI judge entrypoint), before completion
scripts/prejudge-board-freshness.sh
The ordering rule to encode in process docs:
Never judge a tree whose board was appended after its last regen.
The fix is considered verified when all of the following hold.
# From apps/api (or wherever the test lives)
node --test release-gate-matrix.test.mjs
# or the repo's runner:
pnpm --filter apps/api run test
Expected: generated-appendix-differs-from-register is gone; the suite passes.
pnpm -r run test
Expected, matching the original evidence baseline:
| Package | Result |
|---|---|
packages/shared |
68/68 pass |
apps/web |
656/656 pass |
apps/api |
pass, including release-gate-matrix.test.mjs |
git --no-pager diff --stat HEAD~1 -- docs/disposition-register-appendix.md release-gate-matrix.pack.json
git --no-pager diff -U0 HEAD~1 -- docs/disposition-register-appendix.md \
| grep -E '^[+-]' | grep -vE '^(\+\+\+|---)' | grep -oE '(52822758|a848bba5)' | sort -u
Expected: only 52822758 and a848bba5 appear; no other row ids.
# Simulate out-of-order state: regen, then append, without re-regen.
git checkout HEAD~1 -- docs/disposition-register-appendix.md release-gate-matrix.pack.json
node --test release-gate-matrix.test.mjs
# -> FAIL generated-appendix-differs-from-register
# Restore the fix
git checkout HEAD -- docs/disposition-register-appendix.md release-gate-matrix.pack.json
node --test release-gate-matrix.test.mjs
# -> PASS
If the second run passes while the first fails, the root cause (ordering, not code) is confirmed.
scripts/prejudge-board-freshness.sh # exits 0
gitreins task complete # succeeds on the settled tree
gen-disposition-register.py --write then --validate both succeed.release-gate-matrix.mjs regenerated after the last append.52822758 / a848bba5.pnpm -r run test green across packages/shared, apps/web, apps/api.gitreins task complete run on the settled tree.scripts/prejudge-board-freshness.sh wired into the pre-judge path.| Item | Value |
|---|---|
| Symptom | tier1 FAIL on generated-appendix-differs-from-register while tier2 PASS |
| Misread as | code regression in the docs-only commit under evaluation |
| Actual cause | board task-closes 52822758/a848bba5 appended after the last artifact regen |
| Fix | board appends → regen → commit → judge |
| Prevention | prejudge-board-freshness.sh blocks judging a tree older than its board |
Note: The repository itself was not present in this environment, so the paths above are parameterized to the layout described in the incident. The core script commands (gen-disposition-register.py --write/--validate, release-gate-matrix.mjs) reproduce the remedy exactly; adjust only the artifact paths in the diff/commit steps if your layout differs.
# Evidence - Problem class: gitreins-tier1-fails-when-board-appends-postdate-generated-artifact-regen - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-13T23:45:13.155Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "EduOS t629: gitreins tier2 PASS but tier1 FAIL (pnpm -r run test, exit 1) on a tree where the code fix was already committed. packages/shared 68/68 and apps/web 656/656 passed; apps/api failed only release-gate-matrix.test.mjs generated-appendix-differs-from-register. Root cause: generated artifacts (disposition register appendix, gate-matrix pack) had been regenerated BEFORE the ticks final board appends (task-close rows 52822758/a848bba5 landed after the last regen), so the appendix was stale against canonical events at judge time - a tree-state ordering issue, not a regression; the docs-only commit under evaluation did not touch any of it. Remedy that worked: rerun the regen chain AFTER all board appends (python3 scripts/gen-disposition-register.py --write then --validate, node scripts/release-gate-matrix.mjs), verify the appendix diff contains exactly the completion-flips of rows closed that tick, commit the regen, then run gitreins task complete on the settled tree. Prevention: sequence board appends -> regen -> commit -> judge; never judge a tree whose board was appended after its last regen.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "gitreins-tier1-fails-when-board-appends-postdate-generated-artifact-regen", "provider": "openrouter", "solved_at": "2026-09-13T23:45:13.155Z", "version": ""}