fix(ledger): count the T3 header as agent, control-board sender as board (#1506)

messageKind knew only the tmux preamble, so a prompt opening with the T3
header [from: role (id) -> to: role (id)] counted as human in Table 2. The
first line now matches either form; anything short of the full header stays
human. Filbert approved R1 against the frozen hashes.

Proof: packages/ledger/tests/ledger.test.mjs, 22/22; against HEAD's
ledger.mjs it fails exactly the two new tests. No suite runs it. Eight
suites green on the staged tree.

The fix changes zero current counts: no Pi log under .pi/state contains a
T3 header, and the ledger does not read T3 transcripts. Gate F waits on a
T3 thread source.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-09-26 15:16:55 -05:00
co-authored by Claude Opus 5.5
parent d41f81aafe
commit ef0020ad85
7 changed files with 234 additions and 7 deletions
+27
View File
@@ -110,6 +110,19 @@ test('invalid dates, reverse dates and duplicate options refuse', t => {
assert.throws(() => dateRange('2026-02-30')); assert.throws(() => dateRange('2026-09-12', '2026-09-06'));
const f = fixture(t); assert.equal(f.run(['--since', '2026-09-01']).status, 1);
});
test('T3 agent assignments do not count as human in Table 2', t => {
const f = fixture(t);
f.put('.pi/state/bob/sessions/t3.jsonl', [
f.entry('[from: sage (1ef1e4f8) -> to: bob (9cb9731e) class=actionable]\nassign #1'),
f.entry('[from: sage (1ef1e4f8) -> to: bob (9cb9731e)]\nfollow-up #1'),
f.entry('Jason: go ahead'),
].map(x => JSON.stringify(x)).join('\n') + '\n');
const result = f.run(['--json']);
assert.equal(result.status, 0, result.stderr);
const r = JSON.parse(result.stdout);
assert.deepEqual(r.seats, [{ seat: 'alice', board: 1, agent: 1, human: 1 }, { seat: 'bob', board: 0, agent: 2, human: 1 }]);
assert.equal(r.totals.humanMessagesPerClosedIssue, 2);
});
test('preamble parsing and issue number boundaries', () => {
assert.equal(messageKind('[h:control-board -> h:seat] hi'), 'board');
assert.equal(messageKind('[h:seat -> h:seat class=actionable] hi'), 'agent');
@@ -117,6 +130,20 @@ test('preamble parsing and issue number boundaries', () => {
assert.equal(messageKind(' [h:seat -> h:seat] quoted'), 'human');
assert.deepEqual(issueNumbers('fix #1 #2 #2 abc#3 #0 #4x'), [1, 2]);
});
test('T3 header: agent, or board from control-board; anything short of the full header is human', () => {
const sage = 'sage (1ef1e4f8-3ead-4208-beca-38f9f1add079)', filbert = 'filbert (9cb9731e-a10f-4c8f-a212-c4fa1f5f4731)';
assert.equal(messageKind(`[from: ${sage} -> to: ${filbert}]\nbuild #1506`), 'agent');
assert.equal(messageKind(`[from: ${sage} -> to: ${filbert} class=actionable]\nbuild`), 'agent');
assert.equal(messageKind(`[from: ${sage} -> to: ${filbert}] same line`), 'agent');
assert.equal(messageKind(`[from: darkwing (thread-id: unknown) -> to: reviewer (new-thread)]\nreview`), 'agent');
assert.equal(messageKind(`[from: control-board (b) -> to: ${filbert}]\nhi`), 'board');
assert.equal(messageKind(`Jason here\n[from: ${sage} -> to: ${filbert}]\nquoted`), 'human');
assert.equal(messageKind(` [from: ${sage} -> to: ${filbert}]`), 'human');
assert.equal(messageKind(`[from: sage -> to: filbert]\nno thread ids`), 'human');
assert.equal(messageKind(`[from: ${sage} -> to: ${filbert} class=Actionable]`), 'human');
assert.equal(messageKind(`[from: ${sage} -> to: ${filbert}]trailing`), 'human');
assert.equal(messageKind(`[From: ${sage} -> to: ${filbert}]`), 'human');
});
test('no closed issues with human messages means undefined ratio, not invented zero', () => {
const r = summarize(range, [], [], { rows: [{ seat: 'a', human: 1, board: 0, agent: 0 }], mentions: new Map() });
assert.equal(r.totals.humanMessagesPerClosedIssue, 'unknown');