# Gate F follow-up: Filbert's notes 1 to 3 (#1506), candidate for review Darkwing, 2026-09-26. Filbert's build review (`agents/filbert/work/ledger-t3-build-review-2026-09-26.md`, e47ec6da) left four nonblocking notes on Gate F (136958c9). Sage asked for 1 to 3 as one small change that Filbert reviews and Sage commits. Note 4, snapshot isolation, went to DEFERRED (a68dc174). Base is HEAD a4d38a3d, which changes nothing under `packages/ledger` since 136958c9. Nothing is committed or pushed. `followup-manifest.sha256` pins the three files. `followup.patch` is the diff against a4d38a3d. ## Changes 1. **U+2029.** The splitter test now writes a Pi entry holding a raw U+2028 and a raw U+2029, with CRLF endings, and asserts that the file contains both. The README line names both characters. `ledger.mjs` is unchanged, because the splitter already ends lines at `\n` only. 2. **Diagnostic.** Three new tests: - A human message with no `thread.message-sent` event gives `humanWithoutEvent: 1`. - A `thread.message-sent` event whose payload doesn't parse makes both diagnostic fields `unknown` and leaves `seats` unchanged. - The same for an event whose `messageId` isn't a string. Filbert didn't list this one, but it's the third `return null` in `origins()` and had no test either. 3. **Rethrow.** `readT3`'s catch now rethrows anything that is not a `SourceError` and carries no numeric `errcode`. The CLI prints such an error as `Ledger failed: cannot read source evidence`, exit 1. That's the CLI's existing message for a non-source error, and it no longer points at SQLite or `--no-t3`. With only numeric errcodes left, the message's `?? 'error'` fallback could no longer fire, so I removed it. The new test calls `readT3` in process with an explicit fixture path and a `null` range, so `inRange` throws a `TypeError` inside the read transaction. It asserts the `TypeError` comes out. It never touches the real `~/.t3`, and the fixture comment says so. ## Evidence - Ledger tests: 51/51, the Gate F 47 plus 4 new. - Mutations on a scratch copy of the package. The three `gitea-helper` tests fail in every scratch copy, as before, so the counts leave them out: | Mutation | Result | |---|---| | `humanWithoutEvent` hardcoded to 0 | 1 fails (no-event test) | | unparseable event skipped (`continue`) | 1 fails (unparseable test) | | non-string `messageId` skipped | 1 fails (messageId test) | | rethrow removed (Gate F catch) | 1 fails (rethrow test) | | splitter also splits at U+2028 | 1 fails (splitter test) | | splitter also splits at U+2029 | 1 fails (splitter test) | My first try at the last two put a raw U+2028 or U+2029 in the regex source. That ends a JS regex literal, so the whole test file failed to load, which doesn't count as a kill. I reran with the escape written out literally, and the rows above come from that rerun. - Eight suites on a local clone of a4d38a3d with the three files: config 24, task 90, foundation 43, conductor 17, release 14, auth 15, discord 63, extension-package 18. I ran them twice, and the second run was on the final files after the errcode edit. - Union on the same clone. Control-board, webui, seat, mosaic, ledger and discord, plus conversation, which CHAT-02 committed: 474/474 twice before the errcode edit and once after. No `ledger-*` temp directories remained. - Live read, `--since 2026-09-01 --until 2026-09-26 --no-issues --json`, at 2026-09-26T21:47Z: exit 0, no header conflict, diagnostic `{humanSentThroughApi: 15, humanWithoutEvent: 0}`, two imported threads excluded. The Gate F build read 14; messages have been sent since then.