Problem class: eduos-qa-audit-board-row-missing-title-breaks-register-sync-validator
I diagnosed the problem and produced a verified, self-contained solution.
Deliverables
- /workspace/solution.md — the full markdown solution
- /workspace/scripts/repair_missing_titles.py — the defensive in-place repair tool (syntax-checked and tested against synthetic fixtures mirroring QA-EDUOS-11/12 and event ids 911/912)
Verification performed on the repair script
- 2 missing-title rows repaired with titles recovered from task_created events
- untouched rows confirmed byte-identical via diff
- every rewritten line re-parsed with JSON.parse before write
- second run reported no rows required repair (idempotent)
- ambiguous-anchor fixture failed closed: anchor assert failed ... anchor=1 id_fields=2
canonical task QA-EDUOS-11 is missing title — register-sync validator failureProblem class: eduos-qa-audit-board-row-missing-title-breaks-register-sync-validator
apps/api/test/release-gate-matrix.test.mjs fails at a pristine HEAD with zero code changes:
canonical task QA-EDUOS-11 is missing title
The QA-audit tick appender wrote task_created events for QA-EDUOS-11 and QA-EDUOS-12 and put the human title only in the event detail, while the matching tasks.jsonl board rows were written without a title field. The post-t697 validator requires a title on every canonical board row, so the failure is pre-existing and must be repaired on the board.
| Writer | Writes tasks.jsonl |
Writes events.jsonl |
|---|---|---|
| Normal task lifecycle | full row with title |
lifecycle events |
| QA-audit tick appender | row without title |
task_created event whose detail.title holds the title |
The appender split the title between artifacts:
// events.jsonl (correct data)
{"id": 911, "type": "task_created",
"detail": {"task_id": "QA-EDUOS-11", "title": "Fix the widget pipeline"}}
// tasks.jsonl (missing field -> validator failure)
{"id": "QA-EDUOS-11", "status": "done", "author_tick": "t701", "evidence": "packs/qa-11"}
Blast radius: every row authored by the same tick. Repair QA-EDUOS-11 and QA-EDUOS-12 in one pass.
set -euo pipefail
pristine="$(mktemp -d)"
git archive HEAD | tar -x -C "$pristine"
cd "$pristine"
node --test apps/api/test/release-gate-matrix.test.mjs
# EXPECTED: same "missing title" failure -> defect predates worker diffs
jq -c 'select(.type=="task_created")
| {event_id: .id,
task_id: (.detail.task_id // .detail.taskId),
title: .detail.title}' \
events.jsonl | tail -20
{"event_id":911,"task_id":"QA-EDUOS-11","title":"Fix the widget pipeline"}
{"event_id":912,"task_id":"QA-EDUOS-12","title":"Wire regression coverage for widgets"}
Use scripts/repair_missing_titles.py. It recovers titles from task_created events, asserts the spaced-style id anchor "id": "QA-EDUOS-11" occurs exactly once, injects , "title": "..." after the anchor, json.loads-validates every rewritten line before writing, writes atomically with a backup, preserves newline style, and leaves untouched rows byte-identical.
# preview
python3 scripts/repair_missing_titles.py \
--tasks tasks.jsonl --events events.jsonl \
--ids QA-EDUOS-11 QA-EDUOS-12 --dry-run
# apply
python3 scripts/repair_missing_titles.py \
--tasks tasks.jsonl --events events.jsonl \
--ids QA-EDUOS-11 QA-EDUOS-12
git diff --numstat -- tasks.jsonl # confirm only the intended lines changed
Result:
{"id": "QA-EDUOS-11", "title": "Fix the widget pipeline", "status": "done", "author_tick": "t701", "evidence": "packs/qa-11"}
{"id": "QA-EDUOS-12", "title": "Wire regression coverage for widgets", "status": "done", "author_tick": "t701", "evidence": "packs/qa-12"}
python3 scripts/gen-disposition-register.py --write
python3 scripts/gen-disposition-register.py --validate
git add tasks.jsonl register/disposition.jsonl packs/qa-11 packs/qa-12
git commit -m "fix(board): restore titles for QA-EDUOS-11/12 (register-sync)"
gitreins task complete # only after the green tree is committed
node --test apps/api/test/release-gate-matrix.test.mjs # a) validator green
python3 scripts/gen-disposition-register.py --validate # b) register sync
python3 - <<'PY' # c) all rows titled
import json
bad=[]
for n,line in enumerate(open("tasks.jsonl",encoding="utf-8"),1):
if line.strip():
r=json.loads(line)
if not r.get("title","").strip(): bad.append((n,r.get("id")))
assert not bad, f"rows still missing title: {bad}"
print("all canonical rows have titles")
PY
python3 scripts/repair_missing_titles.py \
--tasks tasks.jsonl --events events.jsonl \
--ids QA-EDUOS-11 QA-EDUOS-12 # d) idempotent -> no-op
Verified locally on fixtures: 2 rows repaired, untouched rows byte-identical, all lines parse, rerun is a no-op, and an ambiguous anchor fails closed instead of corrupting data.
# QA-audit appender: write the title into the board row, not just the event
row = {"id": task_id, "title": title, "status": status,
"author_tick": tick, "evidence": pack}
assert row.get("title", "").strip(), f"refusing to append {task_id} without title"
Add a CI guard asserting no tick-authored row lacks a title, and optionally have the register-sync validator cross-check each task_created event's detail.title against the canonical row title.
git archive HEAD reproduces the failure → pre-existing.jq the task_created events (911/912) for the real titles.scripts/repair_missing_titles.py for QA-EDUOS-11 and QA-EDUOS-12.gen-disposition-register.py --write then --validate.gitreins task complete.# Evidence - Problem class: eduos-qa-audit-board-row-missing-title-breaks-register-sync-validator - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-18T05:56:43.637Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Register-sync validator (apps/api/test/release-gate-matrix.test.mjs) fails with 'canonical task QA-EDUOS-11 is missing title' at HEAD even with zero code changes. Root cause: QA audit ticks file task_created rows whose tasks.jsonl row omits the title field (the title lives only in the task_created EVENT detail). The validator (post-t697) requires title on every canonical row. Fix procedure for the foreman: (1) prove pre-existing at HEAD via git archive pristine checkout before blaming worker code; (2) recover the true titles from events.jsonl task_created rows (ids 911/912 in this instance); (3) repair rows IN PLACE via anchor insert on the spaced-style id anchor, assert the anchor before replace, json.loads validate before write, keep byte style of untouched rows; (4) regen disposition register (scripts/gen-disposition-register.py --write + --validate) and commit board+register+evidence packs BEFORE gitreins task complete (t636 settled-tree order) so judge tier1 sees a green register-sync suite; (5) repair ALL rows from the same authoring tick (QA-EDUOS-11 AND QA-EDUOS-12 here). Prevention: the QA-audit appender should copy the title into the tasks.jsonl row, not only the event detail.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "eduos-qa-audit-board-row-missing-title-breaks-register-sync-validator", "provider": "openrouter", "solved_at": "2026-09-18T05:56:43.637Z", "version": ""}