/** * Re-derive the `phase` label on ledger entries written before `phaseWindows` was * fixed. * * That function read the wrong field name, returned one window covering all time, * and every call site seeds `'implementation'` — so 18 of 19 instances had 100% of * their priced usage mislabelled. Fixing the reader corrects attribution going * forward; this corrects what is already on disk. * * Two paths, chosen per entry: * * - **Relabel** when the entry's coverage falls inside a single corrected window. * Post-purge that is 4,134 entries, and it needs no transcript at all. * - **Split** when coverage spans several windows. Six entries, all `metered` bulk * captures, holding 28.8% of all priced tokens — one spans 55.2h and 480.5M * tokens. These are re-read from their source transcript so each record lands in * the phase it actually occurred in. * * The split is arithmetic, not estimation: **bounded by the entry's own coverage * window**, all six reconcile to their transcripts to the digit. That bound is * load-bearing — one entry's cursor sits in a 633-record transcript while the entry * covers only 80 of them (5,132,819 of 119,105,507 tokens), so splitting on the * whole file would have inflated it 23×. * * Three invariants, each learned from a defect rather than assumed: * * 1. **Split output is zero-width** (`from === to`, the record's instant). * `applyPrecedence` reduces overlap only *across* fidelity ranks, so ~1,245 * same-fidelity entries with real windows would double-count each other. * 2. **A record inside two instances' claims goes to `unattributed.ndjson`**, never * to both. This is the `cost-claim-attribution` safeguard, and it is not * hypothetical: 377 records / 211.1M tokens in one split target sit inside two * claim windows. * 3. **Verification is on precedence-weighted dollars, not raw token sums.** Raw * sums can balance while weighted totals move — the exact shape of the defect * that reported a plausible total while double-counting 950 entries. * * Requirements: turn-scoped-cost/AC-9 … AC-15, AC-35 … AC-37, AC-40 * Design: design.md Part 2 */ import type { CostEntry, TokenUnits } from './types.js'; import type { ProviderEnv } from './providers/provider.js'; import { type ProvenanceIndex } from './provenance.js'; export interface RederiveReport { /** Entries whose phase label changed without touching a transcript. */ relabelled: number; /** Entries replaced by per-record splits. */ split: number; /** Split records written back to an instance ledger. */ splitRecords: number; /** Split records that fell inside no claim, or inside two — sent to unattributed. */ toUnattributed: number; /** Entries left alone because their transcript is gone (AC-12). */ unsplittable: string[]; /** Entries skipped because a human placed them (AC-40). */ manualSkipped: number; tokensBefore: number; tokensAfter: number; } interface PhaseWindowLite { phase: string; from: number; to: number; } /** Distinct phases an entry's coverage window touches. */ export declare function phasesSpanned(e: CostEntry, windows: PhaseWindowLite[]): string[]; /** * Re-derive every instance ledger. * * `provenanceFiles` maps a cursor to the transcript *path* containing it — a * different index from `provenance.ts`'s repo-identity map, built here because the * split needs the file itself. */ export declare function rederivePhases(projectRoot: string, claimTimeoutMinutes: number, env?: ProviderEnv, provenanceFiles?: ProvenanceIndex): Promise; /** Cursor uuid → the single transcript path containing it. */ export declare function buildCursorFileIndex(env: ProviderEnv): ProvenanceIndex; /** Sum priced tokens across every ledger plus unattributed (AC-35). */ export declare function projectTokenTotal(projectRoot: string): number; export type { TokenUnits }; //# sourceMappingURL=rederive.d.ts.map