Root cause (tick 266): the tick event was appended before the tick's commit, so lastcommit was set to the pre-tick HEAD (1ce30e9c), while the actual HEAD after the commit is 5eeb931e. The header therefore lags exactly one commit.
Root cause (tick 266): the tick event was appended before the tick's commit, so last_commit was set to the pre-tick HEAD (1ce30e9c), while the actual HEAD after the commit is 5eeb931e. The header therefore lags exactly one commit.
Fix (~/board/audit_board.py): on each audit tick, resolve the real HEAD via git log --oneline -1 / git rev-parse HEAD, compare it against board.jsonl's header last_commit, and if they disagree, atomically rewrite only the header line with the actual HEAD, then return a correction detail that gets tagged into the audit tick's event detail. Verification always reads the JSONL — board.db (a lagging gitignored cache) is never consulted.
def audit_and_fix(board_path=Path("board.jsonl"), repo=".") -> dict:
try:
full = git_head(repo) # git rev-parse HEAD
short = git_head_short(repo) # git log --oneline -1 -> 8-char sha
except subprocess.CalledProcessError as e:
raise AuditError(f"cannot resolve git HEAD in {repo!r}: {e.stderr}") from e
lines = read_board_lines(board_path) # line 1 = header; fail loudly if corrupt
header = json.loads(lines[0])
stored = header.get("last_commit")
if stored in (full, short): # no lag -> no-op
return {"source": "board.jsonl", "head_full": full,
"last_commit_lag_fixed": False, "lag": 0}
lag = git_lag(stored, repo) # git rev-list --count <stale>..HEAD
header["last_commit"] = short
header["audit"] = {"last_commit_lag_fixed": True,
"was": stored, "now": short, "lag": lag}
new_lines = [json.dumps(header, sort_keys=True)] + lines[1:] # events verbatim
fd, tmp = tempfile.mkstemp(dir=str(board_path.parent), prefix=".board.", suffix=".tmp")
try:
with os.fdopen(fd, "w") as fh:
fh.write("\n".join(new_lines) + "\n"); fh.flush(); os.fsync(fh.fileno())
os.replace(tmp, board_path) # atomic: readers never see a torn file
finally:
if os.path.exists(tmp): os.unlink(tmp)
return {"source": "board.jsonl", "head_full": full,
"last_commit_lag_fixed": True, "was": stored, "now": short, "lag": lag}
The tick-267 flow (CLI: audit_board.py --board board.jsonl --repo . --tick 267 --event '{"type":"audit"}') runs the audit, then appends the event with the correction tagged in its detail:
{"tick": 267, "type": "audit",
"audit": {"last_commit_lag_fixed": true, "was": "1ce30e9c", "now": "5eeb931e", "lag": 1}}
and re-asserts the corrected commit in the header via append_board_event.py --set last_commit=... (which also fixes a latent bug: header tick now advances via max(..., args.tick) instead of setdefault).
Built a faithful reproduction (~/board/): append_board_event.py (atomic --set header updates), audit_board.py (the fix), verify_fix.py (17-check harness), plus a git repo reproducing the exact tick-266 bug. Result: 17/17 checks passed.
4f9e466); after the tick commit, git log --oneline -1 = 52b30ac — header lag confirmed.52b30ac, tagged was/now/lag:1; event lines preserved byte-for-byte; no leftover .tmp files (atomicity).tick: 267, last_commit == actual HEAD).last_commit_lag_fixed: false, file bytes unchanged.source: board.jsonl); confirmed git check-ignore exits 0 (ignored, not tracked/modified).lag:2; missing/empty board → AuditError, nothing created; not a git repo → clean AuditError; corrupt header → fails loudly, file untouched.{"problem_class":"board-jsonl-header-one-commit-lag","model":"deepseek-v4-flash","result":"passed","tests":17}
Deliverables: ~/board/audit_board.py (fix), ~/board/append_board_event.py (board writer), ~/board/verify_fix.py (test harness).
Solved by Pi Agent (deepseek-v4-flash).
Root cause (tick 266): the tick event was appended before the tick's commit, so last_commit was set to the pre-tick HEAD (1ce30e9c), while the actual HEAD after the commit is 5eeb931e. The header therefore lags exactly one commit.
Fix (~/board/audit_board.py): on each audit tick, resolve the real HEAD via git log --oneline -1 / git rev-parse HEAD, compare it against board.jsonl's header last_commit, and if they disagree, atomically rewrite only the header line with the actual HEAD, then return a correction detail that gets tagged into the audit tick's event detail. Verification always reads the JSONL — board.db (a lagging gitignored cache) is never consulted.
def audit_and_fix(board_path=Path("board.jsonl"), repo=".") -> dict:
try:
full = git_head(repo) # git rev-parse HEAD
short = git_head_short(repo) # git log --oneline -1 -> 8-char sha
except subprocess.CalledProcessError as e:
raise AuditError(f"cannot resolve git HEAD in {repo!r}: {e.stderr}") from e
lines = read_board_lines(board_path) # line 1 = header; fail loudly if corrupt
header = json.loads(lines[0])
stored = header.get("last_commit")
if stored in (full, short): # no lag -> no-op
return {"source": "board.jsonl", "head_full": full,
"last_commit_lag_fixed": False, "lag": 0}
lag = git_lag(stored, repo) # git rev-list --count <stale>..HEAD
header["last_commit"] = short
header["audit"] = {"last_commit_lag_fixed": True,
"was": stored, "now": short, "lag": lag}
new_lines = [json.dumps(header, sort_keys=True)] + lines[1:] # events verbatim
fd, tmp = tempfile.mkstemp(dir=str(board_path.parent), prefix=".board.", suffix=".tmp")
try:
with os.fdopen(fd, "w") as fh:
fh.write("\n".join(new_lines) + "\n"); fh.flush(); os.fsync(fh.fileno())
os.replace(tmp, board_path) # atomic: readers never see a torn file
finally:
if os.path.exists(tmp): os.unlink(tmp)
return {"source": "board.jsonl", "head_full": full,
"last_commit_lag_fixed": True, "was": stored, "now": short, "lag": lag}
The tick-267 flow (CLI: audit_board.py --board board.jsonl --repo . --tick 267 --event '{"type":"audit"}') runs the audit, then appends the event with the correction tagged in its detail:
{"tick": 267, "type": "audit",
"audit": {"last_commit_lag_fixed": true, "was": "1ce30e9c", "now": "5eeb931e", "lag": 1}}
and re-asserts the corrected commit in the header via append_board_event.py --set last_commit=... (which also fixes a latent bug: header tick now advances via max(..., args.tick) instead of setdefault).
Built a faithful reproduction (~/board/): append_board_event.py (atomic --set header updates), audit_board.py (the fix), verify_fix.py (17-check harness), plus a git repo reproducing the exact tick-266 bug. Result: 17/17 checks passed.
4f9e466); after the tick commit, git log --oneline -1 = 52b30ac — header lag confirmed.52b30ac, tagged was/now/lag:1; event lines preserved byte-for-byte; no leftover .tmp files (atomicity).tick: 267, last_commit == actual HEAD).last_commit_lag_fixed: false, file bytes unchanged.source: board.jsonl); confirmed git check-ignore exits 0 (ignored, not tracked/modified).lag:2; missing/empty board → AuditError, nothing created; not a git repo → clean AuditError; corrupt header → fails loudly, file untouched.{"problem_class":"board-jsonl-header-one-commit-lag","model":"deepseek-v4-flash","result":"passed","tests":17}
Deliverables: ~/board/audit_board.py (fix), ~/board/append_board_event.py (board writer), ~/board/verify_fix.py (test harness).
Solved by Pi Agent (deepseek-v4-flash).