Trueup v2 Optimization with Evidence Bundle

Continue building Trueup in this repository. Read README.md, hackathon.md, CHANGELOG.md and src/ first. This session is recorded by Entire and published: work only inside this repository and never mention anything outside it. Run every command in the foreground (no background commands; long timeouts). Do not git commit or push. Never print or write key values. No absolute paths from this machine in committed files. Memory is limited: one library at a time. Model: claude-sonnet-4.6 / claude-haiku only; the spending guard stays on, and keep this whole task under about $5 of new model spend (check the key's spend before and after).
Task: v2 from v1's traces, the evidence bundle, and the judge path.
- v1 vs v2. v1 is the planner and fixer as they are now. Read v1's own neatlogs traces through the neatlogs Public API or MCP server (docs.neatlogs.com; use NEATLOGS_API_KEY and NEATLOGS_PROJECT from the environment; if an endpoint is missing or paid-only, record exactly that). Find where v1 wasted tokens, retried, or failed (for example the fixer's rejected tries and the planner's 6 of 9 first-try rejections). Make targeted v2 changes, each citing the trace or session id that motivated it. Then run the same targets under v2 (planner on openllmetry and neatlogs with --max 3; the fixer on one finding that took several tries), and compare v1 and v2: first-try acceptance, tries per fix, model tokens and cost, wall time, answer-key recall. Tag the runs v1/v2 in neatlogs. Write the table to hackathon.md with the session ids. Report honestly if v2 is not better.
- Evidence bundle under
evidence/: per finding (scenario JSON, truth log, spans JSON, minimal repro, before/after table, dedupe log, graph output, upstream draft link), per run (run id, versions, neatlogs session id), the withdrawn leads with reasons, the v1/v2 table, andmanifest.jsonwith the sha256 of every file. Keep it small (trim spans to what the checks need). trueup verify: recomputes every headline number (findings by library and severity, withdrawn by rule, fixes proven, before/after counts, v1/v2 numbers) fromevidence/alone, checks every sha256, and fails if any byte changed. No keys, no network. Tests prove it fails on a changed byte.trueup report: builds a single static HTML evidence pagedocs/index.htmlfrom the bundle (plain, readable, light and dark, phone width; no external scripts). Headline table first, then one finding start to finish (crashed run shown as OK -> found -> double-checked -> fixed -> proven), then all findings, withdrawn leads, upstream drafts (marked "not yet posted").- A GitHub Action (
action.ymlat the repo root) that runstrueup auditin CI, and anentire-trueupconsole script soentire trueup ...runs the CLI as an Entire plugin. - The judge path must work from a fresh clone with no keys:
uv sync,uv run pytest -q,uv run trueup verify,uv run trueup sweep openllmetry --case <the double-span case>. Time it from a fresh clone in a temp dir and record the times.
Update CHANGELOG.md, hackathon.md and the README status. Plain words, no filler. uv run pytest -q must pass. Finish with a summary including the v1/v2 table and the spend.
The session hit a usage limit partway through. Your uncommitted work is still in the tree. Check what is done against the original task, then finish the rest, running every command in the foreground. Keep the spend cap and the other rules. End with the summary the original task asked for.
All six parts are done. uv run pytest -q passes (320 tests) and uv run trueup verify passes. Nothing was committed, pushed or posted.
Spend: the key went from $5.9869 to $7.2802, so this task used $1.29. Only claude-sonnet-4.6 was called, and the $15 guard stayed on.
Reading v1's traces: the neatlogs Public API refuses NEATLOGS_API_KEY with HTTP 401. Its docs say "Ingest credentials do not authorize Public API reads", and they don't say whether it is paid-only. The neatlogs MCP server does accept the key, so a new trueup traces <session-id> command reads traces through it. It has limits: field selection answers "not_enabled", and fetching traces by time answers HTTP 409, so keyword search is the only way to list traces. Search found only 9 traces from v1's 12 model runs. Most of v1's numbers therefore came from the local run records, and the traces added the model's reply text.
v1 vs v2 (v1 is the code as it was at the start of this session; both ran today and are tagged variant:v1 / variant:v2 in neatlogs):
| job | version | sessions | first try accepted | tries per item | calls (JSON retries) | tokens in / out | cost | wall time | answer-key recall | traces found in neatlogs |
|---|---|---|---|---|---|---|---|---|---|---|
| planner | v1 | 20261011T013638Z-plan-openllmetry, 20261011T013725Z-plan-neatlogs | 2 of 6 | 1.83 | 7 (0) | 57,686 / 2,671 | $0.21 | 42 s | 2 of 23 | 0 of 2 |
| planner | v2 | 20261011T013704Z-plan-openllmetry, 20261011T013748Z-plan-neatlogs | 3 of 6 | 1.67 | 6 (0) | 59,681 / 2,281 | $0.21 | 40 s | 3 of 23 | 0 of 2 |
| fixer | v1 | 20261011T013927Z-fix-openinference | 0 of 1 | 2 | 4 (2) | 108,886 / 13,389 | $0.53 | 253 s | fixed | 1 of 6 |
| fixer | v2 | 20261011T014344Z-fix-openinference | 0 of 1 | 2 | 2 (0) | 49,438 / 4,900 | $0.22 | 334 s | fixed | 1 of 6 |
Costs are tokens at list price. "Answer-key recall" is new: there was no answer key in the repo, so I defined it as the 23 checked findings for openllmetry and neatlogs.
v2 is not clearly better overall:
- Fixer: v2 fixed the finding with half the calls and about 58% lower cost, because it no longer retries replies that open with prose instead of JSON. Both versions still needed 2 tries, and v2 took longer because its first try got further into the checks.
- Planner: one more first-try acceptance in a sample of six is noise. v2 also accepted one bad scenario,
p-an-async-stream-tools, where the app crashes with its ownAttributeError. - Tracing: flushing after each worker did not help. neatlogs search finds none of the planner sessions, in either version.
Evidence bundle and commands:
trueup fix <id> --reprovere-applies each committed patch with no model call and sweeps all 57 scenarios again. All three recorded before/after results reproduced, with 0 new mismatches.evidence/has 159 files in 376 KB, covering 29 findings: neatlogs 14, OpenInference 6, OpenLLMetry 9; 13 high and 16 medium. It also holds the 29 withdrawn leads (2 failed the latest-release-and-main rule, 27 were Trueup's own false alarms), 6 disputed groups and the v1/v2 data.trueup verifychecks every sha256, re-runs the comparer on each finding's saved truth and spans, and recomputes every number. Tests show it fails on one changed byte, an extra or missing file, an edited number with a matching manifest, or edited spans.trueup reportwritesdocs/index.htmlonly from a bundle that verifies.- CI and Entire:
action.ymlrunstrueup auditin CI. Theentire-trueupscript works:entire trueup verifyran through Entire 0.11.4. I also addedtrueup sweep --case, and CI now runsverifytoo.
Judge path, timed from a fresh copy with no keys and an empty uv cache:
| step | exit | time |
|---|---|---|
uv sync | 0 | 2 s (246 MB downloaded) |
uv run pytest -q | 0 | 28 s |
uv run trueup verify | 0 | under 1 s |
uv run trueup sweep openllmetry --case ag-run | 1 (a lead) | 16 s |
The sweep shows the double count: 2 real calls, 4 LLM spans. Exit 1 there means a lead was found. Since nothing can be committed, the "clone" was made from the files a commit would contain, inside the git-ignored .trueup/.
The v1/v2 table with session ids, the judge times, the CHANGELOG and the README status are updated.
Someone else added files to the tree while I worked: business/, src/trueup/demo.py (a trueup demo command) and some README edits. I left them as they are and added my parts alongside them.