control board: brief for piece 3, the ledger, with Gate D (#1503)

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
2026-09-12 11:42:28 -05:00
co-authored by Claude Fable 5.1
parent c90ce3a836
commit 0334026e13
@@ -165,6 +165,79 @@ message reflects the reply. One real exchange is the pass.
finding (ms-communications rule), reported on the page and in the issue.
- Fleet seats are out: they are not registered (Jason, 2026-09-12).
## Piece 3: ledger (numbers for the rails)
Approved by Jason 2026-09-12 ("go for it") after Gate C. Purpose: replace
"it feels like the rails work" with numbers read from evidence that already
exists. Same shape: one package, one gate, tests, small commits on `refactor`.
Darkwing opens the issue and updates `CURRENT.md` when he picks it up.
### What it is
`node packages/ledger/src/cli.mjs --since YYYY-MM-DD [--until YYYY-MM-DD]
[--json] [--no-issues]`. Read-only. Prints two tables and a totals line to
stdout; `--json` prints the same as one object. Sources, in this order:
1. **Local git log on `refactor`** (no network): commits in range whose
subject carries `#N`. Nothing else in the message is parsed.
2. **Gitea issues** through `scripts/gitea-api.sh` (`repos/mosaicstack/stack`):
number, title, `created_at`, `closed_at`. Missing or unreadable
credentials: exit 2 with the reason, unless `--no-issues`, which prints the
issue table with hours-open as "unknown".
3. **Session logs** under `.pi/state/<seat>/sessions/*.jsonl` for the seats
in `agents/` (repo layout only, no fleet sources): entries with
`type: "message"` and `message.role: "user"` whose timestamp falls in range.
Table 1, one row per issue touched in range (a commit or a close in range):
issue, title (truncated), opened, hours open (or "open"), commits,
follow-ups (commits after the first one for that issue), seats (from the
session logs that mention `#N` in a user message; blank when none).
Table 2, one row per seat: user messages in range split three ways by the
first line of the text: **board** (preamble source session `control-board`),
**agent** (any other `[a:b -> c:d]` preamble), **human** (no preamble).
Totals line: issues closed, median hours open, commits, follow-ups per issue
(one decimal), human messages per closed issue (one decimal).
### What it does not claim
- Agent versus human authorship. Every commit and every issue carries
Jason's identity; the ledger does not guess.
- PR metrics. This work is issue-tagged commits on `refactor`; a PR count
would read zero and mean nothing.
- Gate results. Those are written by hand in this log.
- Quality. A follow-up commit is a count, not a verdict; the reader decides.
### Gate D (yes/no)
Jason runs the ledger for 2026-09-06 to 2026-09-12, picks the one number he
wants to move next week, and writes that sentence with the number into
`CURRENT.md`. Pass: the sentence exists, and its number came from the
ledger's output, not from memory.
### Tests
- Fixture git repository built in a temp dir with commits tagged `#1`,
`#2` (two commits, so one follow-up) and one untagged commit; table 1
counts match.
- Fixture session logs with one message of each kind (board, agent, human)
and one out of range; table 2 counts match.
- Fake `gitea-api.sh` on PATH returning fixture JSON; hours open computed
from `created_at`/`closed_at`; an open issue prints "open".
- Missing credentials: exit 2 with a message; with `--no-issues`, exit 0 and
"unknown" in the hours column.
- Empty range: both tables with zero rows, totals of zero, exit 0.
- `--json` output parses and carries the same numbers as the tables.
### Boundaries
- Read-only. No writes under the data root, no board changes, no new root
files, no schedule, no charts, no `packages/mosaic` changes.
- No fleet sources (`~/.mosaic`) and no network beyond the one Gitea call.
- If a number cannot be computed from evidence it prints "unknown", never a
guess.
## How the scanner decides state
The scanner reads the newest pi session log for each agent, plus whether