Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions .claude/skills/baton/hooks/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,15 @@ def lane_from_event(event):
return {"subagent_type": subagent_type, "task_id": task_id}


# The distinctive marker of a lane line — shared by lane_line() (writer) and lane_count() (reader) so the
# close-out count is derived from the SAME lines it displays and cannot drift from them. No other line the
# ledger writes (header, close-out, verdicts) contains this phrase.
_LANE_MARK = "lane spawned:"


def lane_line(lane, ts):
tid = f" · task `{lane['task_id']}`" if lane.get("task_id") else ""
return f"- {ts} · lane spawned: `{lane['subagent_type']}`{tid}"
return f"- {ts} · {_LANE_MARK} `{lane['subagent_type']}`{tid}"


# ---- Stop: idempotent closeout ------------------------------------------------------------------
Expand All @@ -144,8 +150,28 @@ def sensitive_triaged(path=TRIAGED_SEAMS_PATH):
return classes


def lane_count(path=LANE_SPAWNS_PATH):
"""How many real lanes the machine sidecar recorded this session (authoritative spawn count)."""
def _own_lane_lines(ledger_path=LEDGER):
"""How many lane lines ledger.py has written to its OWN trail this session — the count that MUST match
the lines shown directly above the close-out (they are literally those lines)."""
try:
return sum(1 for line in open(ledger_path).read().splitlines() if _LANE_MARK in line)
except OSError:
return 0


def lane_count(ledger_path=LEDGER, spawns_path=LANE_SPAWNS_PATH):
"""Lanes recorded this session, for the close-out. Reconciles TWO observers so the number is never
LOWER than the lane LINES printed right above it (the reported bug direction):
- ledger.py's OWN recorded lane lines (what the reader sees), and
- the forge-proof sibling ledger `lane_spawns.jsonl` (record_lane_spawn.py), which may hold spawns
seen when ledger's PostToolUse handler did not fire.
Returns the MAX. This fixes the real-test bug where reading ONLY the sibling ledger printed
"lanes recorded: 0" while ledger.py had just written N lane lines (the sibling had not fired): the count
is now never lower than the lines present, and a sibling-only observation is still not lost."""
return max(_own_lane_lines(ledger_path), _sibling_spawns(spawns_path))


def _sibling_spawns(path=LANE_SPAWNS_PATH):
try:
return sum(1 for line in open(path).read().splitlines() if line.strip())
except OSError:
Expand Down
29 changes: 29 additions & 0 deletions .claude/skills/baton/hooks/ledger_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,35 @@ def read_ledger():
check("read_event normalization is dict-or-empty (non-dict -> {})",
all(isinstance(x, dict) for x in [{}]), True)

print("G. close-out count is derived from ledger's OWN lines — never contradicts them")
with tempfile.TemporaryDirectory() as t:
# Run ONLY ledger.py (record_lane_spawn.py NOT involved), so lane_spawns.jsonl never exists and the
# count cannot borrow from the sibling ledger — exactly the real-integration-test scenario that
# printed "lanes recorded: 0" above N lane lines. Don't pre-seed lane_spawns.jsonl: the coupling
# must not be able to hide.
lanes = ["triage", "implementer", "code-reviewer"]
for lane in lanes:
subprocess.run([sys.executable, LEDGER_PY],
input=json.dumps({"hook_event_name": "PostToolUse", "tool_name": "Task",
"tool_input": {"subagent_type": lane}}),
capture_output=True, text=True, cwd=t)
subprocess.run([sys.executable, LEDGER_PY], input='{"hook_event_name":"Stop"}',
capture_output=True, text=True, cwd=t)
body = open(os.path.join(t, ".agents", "runs", "ledger.md")).read()
check("only ledger.py ran -> no sibling lane_spawns.jsonl",
os.path.exists(os.path.join(t, ".agents", "runs", "lane_spawns.jsonl")), False)
check("3 lane lines written", body.count("lane spawned:"), 3)
check("close-out count MATCHES the 3 lines (no 0-vs-N contradiction)",
"lanes recorded this session: 3" in body, True)
with tempfile.TemporaryDirectory() as t:
# Reconciliation: a sibling-only observation (ledger's PostToolUse didn't fire, but record_lane_spawn
# did) is still counted — never lower than either source.
os.makedirs(os.path.join(t, ".agents", "runs"))
seed_jsonl(os.path.join(t, ".agents", "runs", "lane_spawns.jsonl"),
[{"subagent_type": "s1"}, {"subagent_type": "s2"}])
os.chdir(t)
check("sibling-only spawns still counted (max of both sources)", L.lane_count(), 2)

if failures:
print(f"\nLEDGER SELFTEST FAILED ({failures})")
sys.exit(1)
Expand Down
Loading