board-completion-lost
Solution written to ~/solution-board-completion-lost.md and verified against the real boardctl tool in a scratch repo.
Root cause. A tick is two non-atomic durable writes: git commit (work lands on origin/Beta), then boardctl update <id> --status complete --commit-hash <sha>. If the process dies in between, git is the ground truth but the board row stays pending/in_progress. The scheduler then treats the open row as a dispatch candidate — risking duplicate work.
Fix (t565 reconciliation-as-pick). Two scripts, both included in the doc:
- detect-lost-completions.sh — read-only; joins open board rows (boardctl list --json) against git log --all --grep=<id|title>; exits 10 when a lost completion is found. Wire this at the top of every tick before dispatch.
- reconcile-lost-completion.sh — verifies the row is open, the SHA exists/ancestors HEAD, is contained in origin/Beta, and mentions the task id; then closes the existing row via boardctl update ... --status complete --commit-hash <real-sha>. It only ever calls update; it never creates or re-dispatches.
Verified in /tmp/boardlab/repo:
- Reproduced: TASK-777 left in_progress with commit 3227e11 on HEAD.
- Detector fired LOST ... id-in-commit, exit 10.
- Reconciler closed it with the exact SHA; task_completed event appended; doctor = OK.
- Post-reconcile detector exit 0; negative tests (already-complete, missing row, bogus SHA, unmerged divergent commit) all abort without touching dispatch.
Recommended hardening (also in doc §4.3): include the board JSONL files in the same commit as the work, so "commit exists" and "row closed" are equivalent.
# Evidence - Problem class: board-completion-lost - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-21T12:56:56.055Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Board row left pending while the implementing commit already exists on origin Beta (prior tick crashed between commit and board close). Detection: grep resolved tasks.jsonl titles vs recent git log --grep; recovery = t565 reconciliation-as-pick: verify scope/commit first, close the existing row with the real commit_hash, never re-dispatch the work.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "board-completion-lost", "provider": "openrouter", "solved_at": "2026-09-21T12:56:56.055Z", "version": ""}Solution written to ~/solution-board-completion-lost.md and verified against the real boardctl tool in a scratch repo.
Root cause. A tick is two non-atomic durable writes: git commit (work lands on origin/Beta), then boardctl update <id> --status complete --commit-hash <sha>. If the process dies in between, git is the ground truth but the board row stays pending/in_progress. The scheduler then treats the open row as a dispatch candidate — risking duplicate work.
Fix (t565 reconciliation-as-pick). Two scripts, both included in the doc:
- detect-lost-completions.sh — read-only; joins open board rows (boardctl list --json) against git log --all --grep=<id|title>; exits 10 when a lost completion is found. Wire this at the top of every tick before dispatch.
- reconcile-lost-completion.sh — verifies the row is open, the SHA exists/ancestors HEAD, is contained in origin/Beta, and mentions the task id; then closes the existing row via boardctl update ... --status complete --commit-hash <real-sha>. It only ever calls update; it never creates or re-dispatches.
Verified in /tmp/boardlab/repo:
- Reproduced: TASK-777 left in_progress with commit 3227e11 on HEAD.
- Detector fired LOST ... id-in-commit, exit 10.
- Reconciler closed it with the exact SHA; task_completed event appended; doctor = OK.
- Post-reconcile detector exit 0; negative tests (already-complete, missing row, bogus SHA, unmerged divergent commit) all abort without touching dispatch.
Recommended hardening (also in doc §4.3): include the board JSONL files in the same commit as the work, so "commit exists" and "row closed" are equivalent.
# Evidence - Problem class: board-completion-lost - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-21T12:56:56.055Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Board row left pending while the implementing commit already exists on origin Beta (prior tick crashed between commit and board close). Detection: grep resolved tasks.jsonl titles vs recent git log --grep; recovery = t565 reconciliation-as-pick: verify scope/commit first, close the existing row with the real commit_hash, never re-dispatch the work.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "board-completion-lost", "provider": "openrouter", "solved_at": "2026-09-21T12:56:56.055Z", "version": ""}