docs/issue-2037-fork-family-provenance-spec.md
docs/issue-2037-p0-local-corpus-findings.md (provisional Codex locks from ~/.codex forks; Ultra golden still open)45b68c34Sources/CodexBarCore/Vendored/CostUsage/ (Codex first); Pi only if the sanitized corpus proves the same shapeAfter #2059, the repository owner retained #2037 as the canonical tracker. A replacement must provide:
#2043 showed three failure modes to avoid:
This document is the canonical redesign. The sanitized corpus is design input, not only final QA (§10): the exact provenance key cannot be chosen responsibly until the logs answer which IDs survive copies. If logs contain no usable stable identity, an exact solution may be impossible and the product must ship an explicitly labeled conservative estimate (accountingQuality = contained, §6).
Identity contract (summary): use an ID as an event key only when the corpus proves event-level cardinality; request/message-scoped IDs that cover several snapshots need a copy-stable compound sequence or remain lineage hints. Otherwise use an exact pre-fork copied prefix under ancestry and a corpus-locked fork-boundary rule; never token-only dedupe; preserve post-fork distinct events; ambiguous or invalid graph states → no aggressive merge (§5). Fingerprint inputs must be copy-stable (§7); file-local ordinal is a tie-break only, not a fingerprint field unless the corpus proves it survives copying.
P1 vs P2 boundary:
| Phase | In scope | Out of scope |
|---|---|---|
| P1 | Same-session union → cross-window family closure → normalize events (incl. priority metadata) → deterministic family/provisional graph → provenance ledger + ownership/baseline continuity → corpus-proven per-event accounting → per-event pricing → two-tier atomic family cache → quality fields/reasons to report/UI | Per-lineage cumulative baselines as primary accounting; closing #2037 when any affected golden needs containment |
| P2 | Per-lineage baselines on unique events; demote watermark min-cap to ambiguous-only fallback | — |
Closing #2037: P1 may close the issue only if the sanitized corpus proves the P1 per-event accounting path and no affected golden family requires the containment fallback (accountingQuality = contained). provenanceQuality = incomplete alone does not block closure when a corpus-locked provisional-family rule produces accountingQuality = primary and matches the hand token/cost oracle over the available corpus. If any in-scope golden still needs containment (or event/lineage identity remains unproven), P2 is required before closing; P1 can still ship as a non-closing step with #2037 left open.
Relationship to #2066: file-local watermark + min-cap is a conservative fallback only when lineage identity is genuinely ambiguous — never the primary method, never sufficient alone to close #2037.
Fork children can embed or re-emit ancestor token_count history in separate JSONL files. Today each file is scanned largely independently:
forked_from_id + inherited cumulative baseline (#1164) helps when the child only exposes high total_token_usageCodexUsageRow keeps only turnID and a session-local eventIndex (plus day/model/tokens). That is insufficient for cross-file provenanceResult: the same completion can be charged once per file that carries a copy.
Ultra-style sessions can interleave multiple monotonic total_token_usage sequences in one file. A single global baseline recounts the high/low gap (see docs/issue-2037-ultra-fork-overcount-spec.md). Real, but not sufficient alone for §0.
| Term | Meaning |
|---|---|
| Normalized raw event | Lossless extraction of one log token_count (and related ids/metadata) before accounting (§4.2) |
| Same-session union | Build a provenance-safe union of active + archived views of the same sessionId before cross-session family work (§4.1) |
| Fork family | All sessions descending from the same root under forked_from_id ancestry; one reconciliation unit |
| Provisional family | Children sharing the same unavailable forked_from_id, grouped under a synthetic missing-parent node (§4.4) |
| Fork boundary | The single P0 corpus-locked rule that separates copied ancestor prefix from child-local work (§4.3.1) |
| Copied prefix | Ancestor events that appear in a descendant file before that child’s fork boundary |
| Event key | Stable identity for recognizing the same source event across family files |
| Lineage | Real task/agent/turn cumulative sequence after family dedupe — only if the fixture shows a reliable id (P2 primary) |
| Family ledger | Accepted event keys for a family (count once across the entire family) |
| Billing disposition | Whether an observed event emits tokens/cost (accepted) or is a copied duplicate (duplicate) |
| State disposition | Whether an observed cumulative snapshot seeds or advances lineage state even when it is non-billable (§4.6) |
| Billable owner | The single family occurrence allowed to emit tokens/cost for one accepted event key (§4.5.1) |
| Quality | Independent provenance + accounting trust signals (§6); not a single overloaded enum |
Conceptual orthogonality of “same-session archive dedupe” vs “cross-session fork dedupe” is true; the pipeline must still order them.
flowchart TD
enum[Metadata index / family closure]
same[Union same-session active/archive views]
extract[Extract normalized raw events per canonical session]
graph[Build fork-family graph + validate graph]
prov[Resolve event provenance across the family]
lineage[Account: P1 passthrough / P2 per-lineage]
price[Price each unique event individually]
quality[Assign provenanceQuality + accountingQuality]
cache[Replace family contribution atomically]
window[Filter accepted events to requested report window]
report[Propagate quality fields into CostUsage report / UI]
enum --> same --> extract --> graph --> prov --> lineage --> price --> quality --> cache --> window --> report
Ordering rules:
sessionId.scanSince / scanUntil (§4.3).countedTotals.Before constructing cross-session fork families:
sessionId (active partial + archived rollout, etc.).sessionId only, preserving order and occurrence multiplicity (ordered prefix/overlap matching, not an unordered fingerprint set).sessionId is an invalid state (§4.4), not a reason to pick whichever file was enumerated first.This is a hard prerequisite step, not an afterthought.
Each token event should retain at least:
| Field | Role |
|---|---|
| Session ID | Owning canonical session |
| Parent session ID | forked_from_id when present |
| Fork timestamp | Family ordering + pre-fork copy window |
| Stable event / request / message ID | Candidate provenance key only after corpus proof of event-level cardinality; namespace by ID kind |
| Turn ID | Attribution / priority join / optional lineage hint — never sole cross-completion key |
| Original event timestamp | Ordering + fingerprint |
| Ordinal within file | Tie-break only when timestamps collide — not a fingerprint input unless the corpus proves ordinals survive copying (insertions shift them) |
| Model and pricing date | Nonlinear / dated pricing |
Raw last_token_usage | Per-event signal (semantics TBD by corpus) |
Raw total_token_usage | Cumulative validation / lineage-local fallback |
| Canonical fingerprint of the source event | Fallback identity when explicit IDs are absent (§7); must use copy-stable fields only |
| Priority / turn metadata | Enough to reproduce standard vs priority pricing (logs_2 / turn priority flags, surcharge eligibility, etc.) |
Priority metadata must survive normalization so per-event pricing can split standard vs priority totals the way today’s priority overlay expects. Dropping it and re-deriving only from aggregates is a #2043-class pricing footgun.
Why: today’s CodexUsageRow is too lossy for cross-file provenance and for faithful per-event pricing.
root session
├── child A
│ └── grandchild
└── child B
All sessions descending from the same root are one reconciliation unit. Process parent-before-child when the graph is a DAG.
Family closure precedes date filtering. Build or refresh a lightweight metadata index (session ID, parent ID, fork timestamp, source paths, coarse event date range) across all relevant roots/cache entries. For any session that can contribute to the requested report, load the transitive family members and the ancestor events required to establish provenance and cumulative baselines even when those files or events fall outside the report window. Reconcile first, assign accepted usage to each event's original date, then apply scanSince / scanUntil. An out-of-window parent must not become an artificial “missing parent” merely because the report window starts later.
The fork boundary is normative provenance input, not a heuristic chosen independently by each parser path. P0 must select and document one corpus-proven rule, for example:
The selected rule becomes part of the parser/reconciliation fingerprint. A parent event is prefix-eligible only when it satisfies that locked rule (for timestamp rules, normally parentEventTimestamp <= boundary). If the boundary is missing, contradictory, or cannot distinguish copied prefix from post-fork work, do not guess aggressively: record ambiguousForkBoundary, set provenanceQuality = incomplete, and use the corpus-defined conservative path (accountingQuality = contained when containment is required).
Treat the following as invalid or ambiguous (no aggressive fingerprint merge; provenanceQuality ≠ complete, and often undercount-preferring):
| Condition | Handling |
|---|---|
Cycle in forked_from_id edges | Apply the normative timestamp-first forest algorithm below; reject cycle-closing edges; set provenanceQuality = incomplete |
| Conflicting parent claims | Two authoritative views/files for one logical sessionId claim different parents, or authoritative session metadata disagrees with another authoritative parent-edge source → accept no guessed parent edge; record conflictingParents; set provenanceQuality = incomplete. Non-authoritative inference must not create an edge. The cycle/forest algorithm runs only after each session has at most one accepted parent candidate |
| Stable-ID collisions | Same namespaced ID maps to multiple token events without a corpus-proven compound sequence, or maps to incompatible payloads → the ID is not event-unique; keep the events separate, set provenanceQuality = incomplete, and never silently overwrite or arbitrarily drop one |
| Missing parent | Group all children sharing the unresolved parent ID under one provisional family node; apply the deterministic policy below; set provenanceQuality = incomplete |
For each connected component, build the accepted forest from the complete currently known edge set — never from file enumeration order:
(timestampValidity, forkTimestamp ascending, childSessionId, parentSessionId).provenanceQuality = incomplete and record cycleBroken when quality reasons are persisted.This prioritizes the earliest physically reported fork relation while retaining session IDs as stable tie-breakers when timestamps are equal or unusable.
A missing parent ID is still relationship evidence. Create a synthetic node keyed by missing:<parentSessionId> and attach all children that reference it.
(valid forkTimestamp first, earliest timestamp, sessionId) as the billable representative; other sibling pre-fork streams are state-only. Set accountingQuality = contained.missing:<parentSessionId>, rebuild the real family from all member views, migrate copied-key ownership to the real ancestor, and atomically replace the provisional and any old sibling-local contributions. Newly discovered unique parent events may add cost; already-known copied keys may not.File-removal monotonicity invariant: removing a file from the available family corpus must never increase the family's attributed tokens or cost. It may stay equal (copied parent history remains represented once) or decrease (unique events disappeared), but must not rise because siblings began double-charging a copied prefix.
Accounting quality for provisional families is determined by the identity path, not merely by the parent's absence:
| Sibling-prefix evidence | Accounting result |
|---|---|
| Corpus-proven shared event keys or corpus-proven exact-prefix equality | accountingQuality = primary; record missingParent + provisionalFamily |
| Deterministic representative selected because streams disagree or identity/boundary is ambiguous | accountingQuality = contained; record missingParent + provisionalFamily + containmentFallback (and the relevant ambiguity reason) |
Thus provenanceQuality = incomplete does not imply containment by itself.
For every normalized event:
Tie-break: (timestamp, ordinal, session topological order).
Forbidden: token-only dedupe; file-local eventIndex as cross-file identity; shared Ultra/task id as the only key for many completions.
For each accepted family event key K, assign exactly one billableOwner:
K: the earliest real ancestor occurrence in parent-before-child order owns billing. Descendant occurrences are state-only duplicates.K: one deterministic provisional sibling occurrence owns billing (the canonical representative rule in §4.4.2). Other sibling occurrences are state-only duplicates.Ownership changes where a row is sourced, not its semantic identity, original timestamp, token vector, or price. Therefore a copied key remains billed once across parent disappearance/reappearance transitions.
Billable ownership applies only to the contribution for K; it does not shortcut baseline propagation. If grandparent and parent both contain K, the grandparent owns billing, but the child still seeds state by walking its actual parent chain and applying each fork-boundary snapshot in order. Intervening unique parent events must advance the child baseline even when earlier copied keys are grandparent-owned.
Parent restoration may change provenanceQuality from incomplete to complete only after the full real family is recomputed and has no remaining missing edges, collisions, cycle breaks, or ambiguous fork boundaries. Corpus goldens prove that transition; runtime assigns it from recomputed state rather than attempting to “check a golden.”
Only on unique family events:
P1: May emit one accounting event per ledger-accepted normalized event using last / total policy locked by corpus study, without full multi-run lineage state — still after provenance. If intra-file interleaving remains ambiguous, apply containment fallback and set accountingQuality = contained.
P1 still requires an explicit reducer per canonical logical session (and therefore one per provisional sibling), never one family-global baseline:
P1SessionAccountingState {
countedTotals
rawTotalsBaseline
inheritedTotalsRemaining
containmentTracker
}
Before unique child events are accounted, replay that session's copied-prefix occurrences through the state path with billingDisposition = duplicate: update/seed rawTotalsBaseline, consume inherited state where applicable, and leave countedTotals unchanged. Then process billable unique events. Intra-session interleaving may still latch that session's containment tracker and set accountingQuality = contained; it must not create or reuse a family-global watermark.
P2:
last_token_usage is proven per unique request, use it (optionally cross-checked against lineage-local total delta).accountingQuality = contained.Provenance decides whether an event is billed; it does not decide whether the event is an observed cumulative-state transition:
billingDisposition = accepted; it may seed/advance lineage state and emit its accounted delta.billingDisposition = duplicate; it emits zero tokens/cost but still seeds or advances the descendant's raw cumulative baseline.accountingQuality = contained.Thus a parent ending at 100M, followed by a child's first unique absolute total of 101M, contributes 1M — not 101M — even though copied parent events were removed from billing.
Keep #1164 inherited total resolution as the baseline-seeding mechanism where applicable. Authoritative cross-file billing dedupe remains §4.5; state continuity remains mandatory after dedupe.
After family provenance lands, containment runs only inside one canonical session's unique post-ledger stream:
family copies → provenance ledger → unique stream per canonical session
→ primary per-event/per-lineage accounting
→ session-local containment only if still ambiguous
Never apply one #2066 watermark across family members, and never let containment decide whether cross-file copies are duplicates. Provenance owns copy identity; containment is the final ambiguity guard for one already-deduplicated session stream.
Each unique event retains original model, pricing date, input, cached input, output, and priority/turn metadata. Price individually after deduplication so standard vs priority and other nonlinear tiers can be reproduced.
Both the pricing-catalog date and report-day bucket derive from the accepted event's original timestamp. Do not use scan time, cache-write time, fork time, parent restoration time, or the timestamp of a different copied occurrence.
Never subtract already-priced aggregates or price a collapsed lineage blob (#2043).
Use a two-tier derived cache rather than embedding every normalized event in the existing monolithic JSON cache.
Main manifest / family-result cache (small, eagerly decoded):
rootSessionId or provisional missing:<parentSessionId>)Per-file or per-canonical-session event sidecars (lazy, independently invalidatable):
“Lossless” here means lossless for the specified provenance/accounting inputs, not byte-for-byte preservation of source JSON. Source logs remain authoritative and every sidecar is disposable/rebuildable.
When one file changes:
Initially avoid clever incremental family reconciliation. Recompute the affected family.
When a source disappears, changes sessionId/parent, or moves between families, use the manifest to identify both the old and new affected families. Recompute both and update their contribution records in one in-memory cache transaction before the main cache is atomically saved. Write sidecars via temporary file + rename so an interrupted refresh cannot expose a half-written event set.
N; readers ignore unreferenced sidecars.N+1 in memory / temporary paths.N+1 manifest (family membership, contributions, rolled-up day totals, quality, sidecar digests) to a temporary file and atomically rename it last. The manifest rename is the commit point.N remains authoritative; partial N+1 sidecars are orphans and cannot affect totals.This prevents a crash during reparenting from mixing old family A contributions with new family B contributions. A monotonically increasing generation number plus sidecar content digests is sufficient; a separate complex checksum journal is not required.
Day-total atomicity: rolled-up day/model/cost maps are derived exclusively from the family contributions in the newly committed manifest generation. Never patch global day totals in place while ownership moves from family A to B. Generation N contains all old owners/totals; generation N+1 contains all new owners/totals; no reader may observe a mixed generation containing both A's old and B's new contribution for one key.
Do not mandate run-length encoding, pruning, compression, or a particular binary format before measuring the sanitized corpus. First record:
Lock acceptable budgets before implementation freeze. If needed, optimize with string interning, columnar arrays, timestamp/ordinal deltas, or compression while preserving every normative field and golden result.
Bump CodexParserHash, the normalized-event sidecar schema, and clear compatible producer keys when this ships.
| Prefer | Avoid |
|---|---|
| Namespaced ID proven one-to-one with an accounting event and copied into children | Request/message ID assumed event-unique without cardinality proof |
| Exact pre-fork copied prefix under ancestor relationship | Token-value-only dedupe |
| Separate post-fork events even if counts match | Shared task id as sole key |
| Ambiguous → keep separate + degrade quality | Silent overwrite on ID collision |
These are independent. A family can be graph-incomplete and still use containment fallback — a single enum loses that information.
Persist two fields (or an equivalent set of quality reasons):
enum CodexFamilyProvenanceQuality {
case complete // resolvable DAG, no conflicting parents / unhandled cycles
case incomplete // missing parent, cycle break, conflicting parents, etc.
}
enum CodexFamilyAccountingQuality {
case primary // corpus-proven per-event / per-lineage path (no containment)
case contained // watermark min-cap / containment fallback used
}
Persist a Set<CodexFamilyQualityReason> alongside the two fields. At minimum support and assert in goldens:
missingParent
provisionalFamily
cycleBroken
conflictingParents
stableIdCollision
fingerprintCollision
ambiguousForkBoundary
containmentFallback
The UI may collapse reasons into simpler copy, but cache/CLI/test output must preserve them so incomplete + contained is diagnosable rather than a black box.
Roll-up when aggregating days: worst provenance and worst accounting independently (complete < incomplete; primary < contained).
CostUsageDailyReport (and project breakdowns if present) expose rolled-up fields, e.g. provenanceQuality + accountingQuality (or derived isEstimate = either non-ideal).codexbar cost JSON): include both fields and quality reasons so agents/scripts do not treat estimates as exact.Exact UX copy TBD; the data path for both dimensions is required in P1 so quality is not documentation-only.
Used only for the pre-fork exact-copy path or as a secondary assist — not global token dedupe.
Copy-stable inputs only (finalize from corpus), e.g.:
last_token_usage when presenttotal_token_usage snapshot when presentturn_id, etc.) only as secondary componentsDo not include file-local ordinal in the fingerprint unless the corpus proves ordinals survive copying. Surrounding insertions in a child log can shift ordinals even when the event is the same ancestor row. Ordinal remains useful as a within-file or same-stream tie-break after candidates are matched by copy-stable fields — not as identity.
Fingerprint collision policy:
fingerprintCollision, set provenanceQuality = incomplete, and never merge or sum the payloads under one key| Phase | Scope | Closes #2037? |
|---|---|---|
| P0 | Spec; sanitized corpus; lock eventKey / exact-copy / identity contract | No |
| P1 | Same-session union → cross-window family closure → normalize (+ priority metadata) → deterministic family/provisional graph → provenance ledger + ownership/baseline continuity → corpus-proven per-event accounting → per-event pricing → two-tier atomic family cache → quality fields/reasons through report/UI | Only if corpus proves the P1 per-event path and no affected golden family has accountingQuality = contained. incomplete + primary may close when its provisional-family oracle matches; otherwise ship non-closing and require P2 |
| P2 | Per-lineage cumulative baselines; containment strictly fallback → clear remaining contained goldens | Required to close when P1 still needs containment on in-scope goldens |
| P3 | Pi parity if logs match | Separate if needed |
#2066 may merge earlier only as an explicit non-closing interim guard with owner approval and #2037 left open.
Corpus golden
provenanceQuality, accountingQuality — including combinations such as incomplete + contained).provenanceQuality = incomplete; copied prefix bills once and family does not inflate (quality/ownership focus).provenanceQuality = incomplete.sessionId with conflicting parents → provenanceQuality = incomplete.provenanceQuality = incomplete.accountingQuality = contained; no gap-billion inflation.#968 / #1062 / #1164 non-family regressions remain green.100M, copied child prefix 100M, first unique child total 101M → copied row bills zero but seeds state; child bills exactly 1M.sessionId → canonical logical stream preserves the union of unique events exactly once.fingerprintCollision is asserted.missingParent, provisionalFamily, cycleBroken, conflictingParents, stableIdCollision, fingerprintCollision, ambiguousForkBoundary, and containmentFallback where applicable.K; grandparent owns billing, while child baseline walks grandparent → parent → child and includes intervening unique parent growth.incomplete + primary; ambiguous representative path → incomplete + contained, with exact reasons asserted.conflictingParents; non-authoritative inference alone never creates an edge.Performance / storage acceptance (sanitized corpus): record the §4.8 metrics for cold cache, dirty-family refresh, and full rebuild; verify that loading one dirty family does not deserialize every event sidecar. Performance budgets must be agreed before implementation freeze, and optimized representations must remain golden-equivalent.
A sanitized Ultra / multi-fork corpus must answer:
turn_id identify a cumulative lineage, or only a turn span unsafe alone?last_token_usage per-request, cumulative, or sometimes replayed?sessionId contain unique suffixes that require unioning?Deliverables before implementation freeze: hand token + dollar oracles; documented namespaced event-key cardinality / exact-copy rules; the exact locked fork-boundary rule text; duplicated ancestor chain; missing-parent siblings; baseline-continuity oracle; parent disappear/restore ownership-migration oracle; ambiguous-boundary goldens; redacted before/after output; expected quality fields/reasons per scenario; cache/performance measurements and agreed budgets.
| Risk | Mitigation |
|---|---|
| No stable ID survives copies | Pre-fork exact-copy rule; else accountingQuality = contained / labeled estimate only |
| Request/message ID covers multiple token snapshots | Require event-level cardinality proof or a copy-stable compound sequence; otherwise use only as lineage hint |
| Over-dedupe | Identity contract; post-fork preserve; no token-only dedupe |
| Same-session unordered union collapses legitimate repeats | Ordered overlap alignment within one sessionId; preserve occurrence multiplicity and unmatched suffixes |
| Removing a parent increases cost | Provisional missing-parent family + file-removal monotonicity invariant + warm/cold regressions |
| Restoring a parent double-bills provisional keys | Per-key owner priority + atomic ownership migration; only new unique parent keys may add cost |
| Deduped prefix resets child cumulative baseline | Separate billing disposition from state disposition; copied observations seed state but bill zero |
| Earliest billable owner skips intervening parent state | Ownership controls billing only; baseline seeding walks the actual parent chain and every fork boundary |
| P1 “state-only” implemented as another global baseline | Explicit P1SessionAccountingState per canonical session/provisional sibling; no family-global watermark |
| Cycle result depends on file order | Timestamp-first accepted-forest algorithm over the whole component; stable ID tie-breaks |
| Wrong fork boundary over- or under-dedupes | Corpus-lock one boundary rule; ambiguous boundary records reason and uses conservative path |
| Out-of-window parent treated as missing | Build relevant family closure before report-date filtering |
| Same-session union drops an active/archive unique suffix | Union all same-session views before cross-session graph construction |
| Monolithic normalized-event JSON grows too large | Small manifest/family results + lazy disposable sidecars; benchmark before choosing compact encoding |
| Crash mixes old/new family ownership or day totals | Generation-qualified sidecars; manifest atomic rename as commit point; orphan GC |
| Family move patches day totals twice | Rebuild day maps solely from one committed generation's family contributions; never patch global totals in place |
| Copied occurrence changes price/report day | Preserve original event timestamp as both pricing-date and report-bucket source |
| Quality never reaches UI | §6 mandatory in P1 (both dimensions) |
| Single enum conflates provenance vs accounting | Dual fields / reason set (§6.1) |
| Same-session vs family ordering bugs | §3 hard order + test 11 |
| Conflicting authoritative parent claims delegated to cycle breaker | Accept no guessed edge; mark conflictingParents; run forest only after single-parent candidates are resolved |
| Treating #2066 as the fix | Fallback-only; leave #2037 open unless P1 close criteria met |
| Closing #2037 while goldens still use containment | Explicit P1 close gate (§0, §8); otherwise P2 required |
provenanceQuality = incomplete vs accountingQuality = contained (including when both apply)?incomplete + primary provisional family is acceptable, but any affected golden with accountingQuality = contained keeps #2037 open for P2?Strong basis for implementation once the fixture locks keys:
primary vs contained fork based on corpus-proven identityFile-local containment alone cannot close #2037. This scan-wide provenance design can — after the corpus locks the keying rules, and only when the close criteria in §0 / §8 are met.
Stop condition for prose: after rev 8, do not begin P1 or add speculative key schemes until P0 locks §10.1 (event-key cardinality), §10.9 (fork-boundary rule), and §10.10 (ownership migration through parent disappearance/reappearance).
Local P0 progress (2026-07-11, rev 2): docs/issue-2037-p0-local-corpus-findings.md locks §10.1 / §10.9 for ordinary Codex rollout forks and records scanner-integration results:
token_count event ID; ordered normalized (last,total) contiguous prefix under ancestry; timestamps rewritten on copy (exclude from fingerprint); leaf session_meta is authoritative.archived-fork-33ce-3869 + oracle tests are in-repo.#1164 inherited totals already match the parent-owns-prefix scanner-unit oracle (Issue2037ScannerIntegrationTests) — regression-locked, not a new ledger.total_token_usage drops; do not block cross-file work on drops.logs_2 join.