Skip to content

ledger totals: the node prices its counted lines at read time, kept on the book and rebuilt from the chain (H6; on H3 + M10) - #689

Draft
MattJackson wants to merge 10 commits into
predevfrom
q128-h6-read-time-totals
Draft

MattJackson wants to merge 10 commits into
predevfrom
q128-h6-read-time-totals

Conversation

@MattJackson

Copy link
Copy Markdown
Collaborator

Money: GET /admin/ledger/totals on a node prices its counted lines at read time, at the card in force at each line's own arrival. A signed back-dated correction moves the next read with no restart (audit root-R1 #6; BUSBAR-1.6.0.md section 7; ARCHITECT 2026-10-07 H6 retention ruling). Stacked on H3 (#687) and M10 (#688), both merged in. The first commit is labelled wip:; the proof below is what that label was waiting for, and the history is not rewritten (no force-push).

What money behaviour changed

  • Before: NodeLedger::booked_lines returned nothing, so derived_totals_rows always fell back to the book's settled balance, priced at settlement. A back-dated correction did not move the read until a restart re-derived the book.
  • Now Durability keeps the counted lines (counts, arrived_ms, balance and window, fee count) for the windows the book holds.
    • Lines are appended as each posting is journalled: settle_counted*, post_counts, and recovered holds.
    • Lines are rebuilt from the chain by the same replay that rebuilds the book.
    • The read prices them through totals_as_of and never replays the journal (M11).
    • NodeLedger reads the dated history off the book's own source (ROOT_CARD in production).
  • The read stays on the book's balance whenever a line cannot be priced in full: a figure with no counts behind it, a class nobody declared, or a line the card cannot price. It is never zero.
  • With M10 merged in, a refused balance's bucket-day row is withheld and named, whichever arm of the read priced it.
  • The unconstructed register and gate prose now say proven instance 2 (booked_lines) is fed.

Known limits (no ruling needed)

  • Retention: the book retires no window (Book::retain_from has no production caller), so the lines are kept for the life of the process: one line per counted unit, rebuilt from the whole chain at boot.
  • Restart reconciliation: a counts-era settlement with a figure but no counts is marked uncounted only in the live process, and a replay rebuilds it at 0. That one case reads differently before and after a restart, and it already shows up as a restart-reconciliation finding.

How it was proven

Latchkey cli-098c3bb9-d70e-4fef-8d31-f53592564de8 ran on f767f88 (H3, M10 and predev 1c4e1f0 merged):

  • fmt passed, and clippy -p busbar --all-targets -D warnings passed.
  • busbar (bin and every integration target except ledger_identity, which CI runs with the oracle clone): 1058 passed, 0 failed.
  • RED: with the booked-lines seam answering nothing and the history read from the process holder (predev's rule), these FAIL:
    • h6_a_back_dated_correction_moves_the_node_totals_on_the_next_read_without_a_restart
    • h6_the_node_totals_answer_the_same_bytes_before_and_after_a_restart
    • The guard h6_an_unpriceable_line_or_an_uncounted_figure_serves_the_balance_never_zero passes both before and after, as intended.
  • This PR's hop is the proof of record.

Oracle verdict

Strict replay against golden/1.5.5 over the money, ledger, usage, billing and admin audit/verify cells, base predev 1c4e1f0 against this head:

  • head: 68 PASS, 20 UNBASELINED, 0 FAIL
  • base: 68 PASS, 20 UNBASELINED, 0 FAIL
  • No cell moved, and no unsigned 1.5.5 cell moved. The admin.ops|GetLedgerTotals field set and render are unchanged; only the values a correction moves can move.

…ept on the book and rebuilt from the chain

GET /api/v1/admin/ledger/totals on a node answered the book's settled balance, a figure priced
at settlement: NodeLedger::booked_lines returned nothing, so derived_totals_rows always fell back.
A signed back-dated rate correction did not move the read until a restart re-derived the book
(audit root-R1 #6; BUSBAR-1.6.0.md §7).

Durability now keeps the counted lines (counts, arrived_ms, balance and window, fee count) for the
windows the book holds, appended as each posting is journalled (settle_counted*, post_counts,
recovered holds) and rebuilt from the chain by the same replay that rebuilds the book. The admin
read prices them through totals_as_of at the card in force at each line's own arrival; it never
replays the journal (M11). The book retires no window today, so neither do the lines.

A figure with no counts behind it, a class nobody declared, or a line the card cannot price
leaves the read on the book's balance, as derived_totals_rows documents. The reconciliation still
reads the balance at its own vintage. NodeLedger reads the dated history off the book's own
source (ROOT_CARD on a production node).

ARCHITECT 2026-10-07 H6 retention ruling.
…efused balance's row is withheld whichever arm of the totals read priced it
@MattJackson
MattJackson enabled auto-merge October 8, 2026 03:21
@MattJackson
MattJackson marked this pull request as draft October 8, 2026 03:27
auto-merge was automatically disabled October 8, 2026 03:27

Pull request was converted to draft

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

promote into predev: DENY @56788d02d: 0 failing test row(s), 13 DENY row(s)

DENY rows (13)

gate row detail
construction one-pick-site 3 production call site(s) of 'pick_among(' (ceiling 2): crates/busbar-kernel-egress/src/walk.rs:298; crates/busbar-llm/src/engine/exhaustion/fallback.rs:120; crates/busbar-llm/src/engine/pipeline.rs:8
kind-isolation kind-isolation:deps 3 finding(s), 93 shipped edge instance(s) over 30 class(es), 93 declaration(s); 56 '[[dep]]' row(s), 0 question(s): unlisted-dep-edge legacy -> plane busbar-llm -> busbar-plane-llm is a shipped edge w
kind-isolation kind-isolation:test-deps 3 finding(s), 35 test edge instance(s) over 21 class(es), 35 declaration(s); 20 '[[dep]]' row(s), 0 question(s): unlisted-dep-edge cleanliness -> legacy busbar-core-admin -> busbar-a2a is a test edge
kind-isolation kind-isolation:law0 12 hit(s) off the [[law0]] ceilings, 6 finding(s) over 14 neutral crate(s): law0-rise busbar × instance:secret 37 hit(s) against a ceiling of 35: this landing grew a neutral crate's instance vocabular
kind-isolation kind-isolation:law0-base 1 hit(s) grown over the merge-base d7929b6, 1 cell(s): law0-grown busbar × instance:store 2 -> 3 hit(s) over the same cell at the merge-base d7929b6 (its '[[law0]]' row 2 there): this branch wrote 1
kind-isolation-ship kind-isolation:deps 10 finding(s) over 93 shipped edge(s): ship-edge kernel -> hooks busbar-kernel -> busbar-hook-ranking is 'not-allowed': the architecture grants no kernel -> hooks edge, and the ship criterion is the a
kind-isolation-ship kind-isolation:test-deps 10 finding(s) over 35 test edge(s): ship-edge cleanliness -> export busbar-core-admin -> busbar-export-prometheus is 'not-allowed': the architecture grants no cleanliness -> export edge, and the ship
kind-isolation-ship kind-isolation:law0 12 hit(s) off the [[law0]] ceilings, 6 finding(s) over 14 neutral crate(s): law0-rise busbar × instance:secret 37 hit(s) against a ceiling of 35: this landing grew a neutral crate's instance vocabular
kind-isolation-ship kind-isolation:law0-base 1 hit(s) grown over the merge-base d7929b6, 1 cell(s): law0-grown busbar × instance:store 2 -> 3 hit(s) over the same cell at the merge-base d7929b6 (its '[[law0]]' row 2 there): this branch wrote 1
kind-isolation-ship kind-isolation:faces 2 finding(s) over 40 crate(s): foreign-entry crates/busbar busbar is kind 'root' and implements 'Transport' 1 time(s) in shipped source — the entry face of kind 'transport'. A trait implementation is
kind-isolation-ship kind-isolation:legacy-drain 3 finding(s): transitional-live qa/kind-isolation.toml 'busbar-a2a -> busbar-core-admin' (legacy drain: the retiring A2A engine's tests drive the admin surface that drained into the cleanliness tier)
ship-ready ship-ready:ship-twin 'kind-isolation-ship' is not green: kind-isolation:deps (a dependency the architecture does not grant is still in the graph); kind-isolation:test-deps (a dependency the architecture does not grant is
structure-lint structure-lint:plane-dup:unledgered 22 finding(s): PLANE-DUPLICATE (module): 'config.rs' — a2a:crates/busbar-a2a/src/a2a/config.rs decisions:crates/busbar-plane-decisions/src/config.rs voice:crates/busbar-voice/src/config.rs (the ledger
Denied because (4)
  • shard 1/4: gate:kind-isolation row kind-isolation:law0-base: red now, not red at base
  • shard 1/4: gate:kind-isolation-ship row kind-isolation:law0-base: red now, not red at base
  • shard 1/4: gate:kind-isolation row kind-isolation:law0: figure rose 11 -> 12
  • shard 1/4: gate:kind-isolation-ship row kind-isolation:law0: figure rose 11 -> 12

Judged against base d7929b661: 2 new red, 2 worse, 3 standing (excused).

New reds: gate:kind-isolation row kind-isolation:law0-base: red now, not red at base, gate:kind-isolation-ship row kind-isolation:law0-base: red now, not red at base

tests passed: 24408, failed: 0. Run: https://github.com/GetBusbar/busbar/actions/runs/38090575924 . Artifact verdict-56788d02d37d264f23bb47f574ba0d41e54d9b72 (failures.json, junit.xml, raw.log; 90 days).

…ests; drop shipper-seam tests for the removed shipper API
… value predev's Option<Outcome> signature uses
@MattJackson MattJackson added fixing and removed fixing labels Oct 10, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant