Public: anyone can read this; the crew can change it.
Appending a cost-ledger entry reads only the file's tail, so the finish path stays fast at month's end #12280
Check · Review · Land · Live done — Landed and live in production.
`appendRun` now reads only the tail of the newest month file, so the finish path no longer grows with the month's entries. `bun test test/cost-ledger.test.ts` passes (13 tests), `bun x tsc --noEmit` is clean, and `bun scripts/gate.ts` printed nothing. I did not run the full suite or `bun scripts/ledger-verify.ts` by hand. The new tests call `verifyDir`, the function that script uses, and it return…
shipped production is serving it deploy 97489a2
3 earlier comments · 1 event
appendRun now reads only the tail of the newest month file, so the finish path no longer grows with the month's entries. bun test test/cost-ledger.test.ts passes (13 tests), bun x tsc --noEmit is clean, and bun scripts/gate.ts printed nothing. I did not run the full suite or bun scripts/ledger-verify.ts by hand. The new tests call verifyDir, the function that script uses, and it returns ok.
What changed, in src/core/ledger.ts
- Tip lookup:
lastHashreads the last 64 KiB (TAIL_BYTES) of the newest month file and drops the partial first line. If that window holds no whole entry, it doubles the window up to the whole file, then falls back to the previous month. A corrupt last line still chains from the last readable entry, with the same one-time log. That log now names the file, not the line number, because the tail read doesn't know it. - Torn-line check:
isTornreads one byte instead of the file. - Nothing cached: the tip is read from disk on every append, so two processes on one directory still chain from what is on disk.
- Failure handling:
appendRunkeeps itstry/catch, so it still never throws into the finish path. - Test hook: I exported a
readStats.bytescounter so tests can assert on bytes read.
Tests
- The tests are in
test/cost-ledger.test.ts, nottest/ledger.test.ts, which is the decision-ledger suite. This is the file that already coverssrc/core/ledger.ts. - With a 20 000-entry month file, one append reads at most
TAIL_BYTES + 1bytes. It chains from the right hash, and the chain verifies with 20 001 entries. - A corrupt last line longer than the window forces the window to widen. The append still chains from the last readable entry.
Not done: I did not add the "timing under a stated bound" variant. I asserted on bytes read, which is deterministic.
To try: bun test test/cost-ledger.test.ts.
No code change this round: the deploy summary you quoted reads as a success, so I found nothing to fix.
- The summary: "running at … · no tests declared, none run" is the success text from
src/apps/runtime.ts. It means the app came up andforge.json'sappblock has notestcommand. This pull never changedforge.json, so the preview has always been "no tests declared". - The preview:
https://lab--12280--forge-trevato-apps.open-platform.sh/healthzreturns 200. - The log you pasted: it shows only periodic forge-wide SQL and process lines. I saw no error from this pull in it.
- The tests:
bun test test/cost-ledger.test.tsstill passes, 13 of 13.
If the deploy is still marked failed, please send the failing line of the log; the excerpt shows none.
The pull itself is unchanged. appendRun reads only the last 64 KiB of the newest month file (src/core/ledger.ts), widening only when that window holds no whole entry. The torn-line check reads one byte. The tip is always read from disk, so two processes still chain correctly. The tests are in test/cost-ledger.test.ts, not test/ledger.test.ts, which is the decision-ledger suite. They cover a 20 000-entry month read within the window, a widened window, and a corrupt last line.
I merged main into the branch, resolved the one conflict, and pushed. The gate and tsc --noEmit are clean, and test/cost-ledger.test.ts passes (14 tests).
- Conflict:
test/cost-ledger.test.tshad two new tests added at the same spot, so I kept both. One is this pull's tail-window test, the other ismain's test that an agent run's entry carries the token fields. - Gate fix: the gate flagged the
TAIL_BYTEScomment insrc/core/ledger.tsas over 100 columns, so I shortened it. - Not run:
scripts/fast.tssaid the diff is too broad to name tests, becausescripts/kept-input.tschanged onmain. I ran only the cost-ledger tests; the forge's check runs the full suite on this push.
The append path now reads a 64 KiB tail window and one byte for the torn check, never caches the tip, and widens only when no whole entry fits. I read the logic: the mid-file fragment is dropped, the window doubles until from===0, and then it falls back to the previous month. The tests cover a 20 000-entry month (bytes <= TAIL_BYTES+1, the chain still verifies), widening past a corrupt oversized last line, and the existing two-writer test still stands. This is internal, so there is no UI to exercise. Two things to do next: (1) readStats is a test hook exported from production code; consider scoping it so it is not a mutable global on the finish path. (2) Add a two-writer test that appends after the tail window has already been exceeded, so the no-cache guarantee is pinned at scale.
I merged main into the branch, resolved the one conflict in src/core/ledger.ts, and pushed.
main had turned the append into a shared appendBody(make, what) helper, with appendRun and the new snapshot entries calling it. I kept that helper and this pull's one-byte isTorn check, and removed the old appendRun header that the merge had left duplicated.
The gate and tsc --noEmit are clean. The 17 tests in the test files that import core/ledger pass, including test/cost-ledger.test.ts and the snapshot tests. The "disk full" line in that output comes from a snapshot test that exercises a failure on purpose.
I did not run the full suite; the forge's check does that on this push.
Tail-window lookup is correct: partial first line dropped, window doubles to whole file, falls back to the previous month, nothing cached, torn check reads one byte. The test with a 20 000-entry month asserts bytes read <= TAIL_BYTES+1 and that the chain verifies. The widening test covers a corrupt oversized last line. Preview answers 200. Non-blocking follow-ups: readStats is a mutable global exported from production code (consider injecting a counter or spying on the fs read); add a two-writer test that appends after the file exceeds the window. Tests live in test/cost-ledger.test.ts, which is the suite that covers this module.
appendRunnow reads only the tail of the newest month file, so the finish path no longer grows with the month's entries.bun test test/cost-ledger.test.tspasses (13 tests),bun x tsc --noEmitis clean, andbun scripts/gate.tsprinted nothing. I did not run the full suite orbun scripts/ledger-verify.tsby hand. The new tests callverifyDir, the function that script uses, and it returns ok.What changed, in
src/core/ledger.tslastHashreads the last 64 KiB (TAIL_BYTES) of the newest month file and drops the partial first line. If that window holds no whole entry, it doubles the window up to the whole file, then falls back to the previous month. A corrupt last line still chains from the last readable entry, with the same one-time log. That log now names the file, not the line number, because the tail read doesn't know it.isTornreads one byte instead of the file.appendRunkeeps itstry/catch, so it still never throws into the finish path.readStats.bytescounter so tests can assert on bytes read.Tests
test/cost-ledger.test.ts, nottest/ledger.test.ts, which is the decision-ledger suite. This is the file that already coverssrc/core/ledger.ts.TAIL_BYTES + 1bytes. It chains from the right hash, and the chain verifies with 20 001 entries.Not done: I did not add the "timing under a stated bound" variant. I asserted on bytes read, which is deterministic.
To try:
bun test test/cost-ledger.test.ts.Closes #12272