Skip to content

Extraction report reads extraction-quality*.json (#151); token-burn cost/windows/effort + last-record usage fix (#296) - #325

Closed
MendixMau wants to merge 3 commits into
masterfrom
feat/extraction-quality-and-token-burn
Closed

MendixMau wants to merge 3 commits into
masterfrom
feat/extraction-quality-and-token-burn

Conversation

@MendixMau

@MendixMau MendixMau commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

#151: the extraction report ignored extraction-quality*.json

Problem. extractor-quality-loop.md treats the validator's extraction-quality.json as the proof that an extraction reached the 95% gate. bin/extraction-report.sh, the Stage 1 surface, never read that file. In one field build a 100% score on 7 dimensions showed up as "2 pass, 18 skipped", with no quality section at all.

Fix (bin/extraction-report.sh).

  • The report now looks for extraction-quality*.json in four places: analysis/<src>/ (the location the skill names), analysis/<src>/extracted/ (where the field project wrote it), and the same two inside the KB.
  • Each file it finds gets its own section. The dimension table has one row per key in the file's dimensions, showing score, found / expected, gaps and a flag. Dimension keys come from the file, so a non-code source shows its own dimensions.
  • Above the table: overall score against threshold, passed, selfGraded and confirmedZeros. The same data is in --json (extractionQuality) and there is one summary line in the prose output.
  • Verdicts only apply rules that the skill states and that can be seen in the file itself:
    • fault: selfGraded together with passed:true ("self-graded pass"); passed:true with a score under its own threshold; a 0/0 dimension that is not in confirmedZeros (the Suspicious-Zero Gate); unparseable JSON.
    • manual: the run is not passed, or it is self-graded and not passed.
    • pass: otherwise.
  • A score file inside <kb>/extracted/ is no longer read as an item array. On master it shows up as "would not parse" and weakens the zero corroboration.
  • skills/extractor-quality-loop.md: one short "Where it is read" pointer. This is an on-demand skill, so the baseline word budget is untouched.

Missing score file (review fix): a code-extracted or mixed KB with no score file gets a manual section, "not produced yet — no shipped pipeline writes a validator; build one per extractor-quality-loop.md, or waive the gate". It is not a fault, because nothing mandatory produces the file (CLAUDE.md rule 5). A score file that is present but wrong is still a fault. Documents KBs get skipped. The gate and the exit code are unchanged.

Golden input: there is no captured extraction-quality*.json anywhere in this repo, because no pipeline ships a validator. The fixture reads the example object from the skill's "Output Format" section at run time, so it is not copied in. The variants are mutations of that object. A field run against a real validator output is still owed.

#296: token-burn cost, windows, agent time, human prompts

Fix (bin/token-burn.sh), all opt-in.

  • --rates FILE gives cost per model and usage class. Prices are per Mtok, the file holds model globs (first match wins, * as catch-all), and the currency label defaults to EUR. No price is built in. A model with no matching line is printed UNPRICED and the total is marked incomplete; it is never counted as 0.
  • --windows FILE takes <start> <name> lines (each window runs until the next one starts) and prints per window: messages, headline tokens, cache-read, cost, agent time and prompts.
  • --effort / --gap MIN (default 5):
    • Agent working time is the sum of main-thread gaps of at most N minutes, leaving out gaps that end at a human prompt.
    • Human prompts are counted by origin.kind == "human", falling back to non-< text for older records. Tool results, isMeta, compact summaries, task notifications, peer messages and sidechain records are excluded, and each uuid counts once.
  • Output is aggregates only; no prompt text is ever printed. --brief is unchanged.

Unchanged without the flags. Before the last-record fix below was added, screen, --json and --brief output matched master byte for byte, both on the captured fixture and on a live transcript. Fixture T9 asserts that no new section or key appears.

Field run on a real multi-day Claude Code transcript (harness 2.1.29x): the script counted 89 human prompts, and an independent count of origin.kind=="human", non-meta records also gave 89. It also reported 5h25m agent time, plus a windows table and a cost table at made-up rates.

Fixture honesty. The captured fixture has no main-thread human prompts. I tried to capture and scrub a real session transcript, but that commit was refused. So tests/wave2/fixtures/token-burn/effort/s-3.jsonl is hand-built from a profile of real record shapes; only keys and value kinds were read. CAPTURE.md says this plainly. Agent time on the captured s-1 main thread is real, and every expected number was computed independently.

Lead-requested fix: usage per message.id = per-counter max over its records

The header claimed that every streamed record of a message carries the same usage. That is false for subagent transcripts: early records carry a partial output_tokens. The script now keeps the first record's time and the last record's usage.

Before → after:

  • Captured fixture, subagent model: output 60 → 1,559; headline 59,858 → 61,357; stage 2 69k → 70k.
  • Live multi-agent session: subagent output 37,791 → 84,915.
  • Main thread: identical first to last, so main-thread numbers do not change.

Up to 7× undercount was measured elsewhere on 2.1.296. Token counts on subagent-heavy projects will rise after sync. Fixture expectations and CAPTURE.md were re-derived. Found while promoting #171 (measured-claims §7).

Checks

  • tests/wave2/test-token-burn.sh bin/token-burn.sh: 63/63 pass. On master: 29 fail. The new zero-usage case fails on the first commit of this PR.
  • tests/wave2/test-extraction-quality-report.sh bin/extraction-report.sh (new): 27/27 pass. On master: 22 fail.
  • check-scripts, check-portability, check-no-private-citations, leak guard (LEAKGUARD_ALLOW_NO_DENYLIST=1) and check-pr-discipline origin/master: all clean. The forbidden-word grep over added lines returns 0 hits.
  • Routing was not touched, so render-routing --check was not needed.
  • Full suites were not run.

Field-run status

  • token-burn: field run done (above).
  • extraction-report: owed. It needs a real KB with a real validator output; I did not touch the field project.

Review round (head 1a73996):

  • A missing score file is now manual instead of a fault.
  • token-burn takes each usage counter's max across a message's records, so a later record with empty usage cannot erase real numbers. The fixture case msg_e1 has a second, zero-usage record.
  • The reused order variable is renamed (win_names, msg_order).
  • The skill now has a comment noting that a fixture parses its example JSON.

Closes #151
Closes #296

🤖 Generated with Claude Code

https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw


Generated by Claude Code


Generated by Claude Code

…ost, windows, agent time, prompts (#296)

extraction-report.sh: find every extraction-quality*.json at analysis/<src>/,
analysis/<src>/extracted/ and inside the KB; one row per dimension the file
declares; selfGraded+passed, passed under its own threshold, an unconfirmed 0/0
dimension and unparseable JSON are faults. A code-extracted/mixed KB with no
score file now shows one fault section (documents KB: skipped). The score file
in <kb>/extracted/ is no longer misread as an item array. No captured validator
output exists in the repo; the fixture reads the skill's documented example.

token-burn.sh: opt-in --rates FILE (per-Mtok prices from the project, none
built in; unmatched models UNPRICED), --windows FILE, --effort/--gap (agent
working time from main-thread gaps, human prompt count by origin.kind).
Aggregates only. Output without the new flags is byte-identical (verified on the
captured fixture and a live transcript, before the last-record fix; T9 asserts). Field run on a real
multi-day transcript: 89 human prompts, matching an independent count.

token-burn.sh: usage per message.id now from its LAST record (subagent
output was undercounted: fixture 60 -> 1559 output tokens, a live multi-agent
session 37,791 -> 84,915). Found while promoting #171.

Closes #151
Closes #296

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
- extraction-report.sh: a code-extracted/mixed KB with no extraction-quality*.json
  is MANUAL ("not produced yet — no shipped pipeline writes a validator"), not a
  fault: nothing mandatory produces the file. Present-but-wrong files stay faults.
- token-burn.sh: per message.id take each usage counter's max over its records,
  so a later record with empty/zero usage cannot erase real numbers (fixture
  case added: fails on the previous commit). Window-name list renamed.
- extractor-quality-loop.md: comment that a fixture parses the example JSON.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
…rn capture notes

Confirmation review of #325: the skill paragraph still said a missing score file
shows as a fault; capture notes said "last record" where the code takes the
per-field max. Wording applied as the reviewer specified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
MendixMau pushed a commit that referenced this pull request Oct 13, 2026
…rt and last-record usage (closes #151 #296)

Review: MERGE (after fix round + confirmation; last skill-wording fix applied verbatim as the reviewer specified) — missing score file is MANUAL (no producer yet), main-thread token totals unchanged, subagent undercount fixed.
MendixMau added a commit that referenced this pull request Oct 14, 2026
)

Every constituent PR was reviewed by a fresh agent that did not write it (CLAUDE.md "Merging a PR"); only MERGE verdicts joined. CI green on each constituent head and on this head (40814fc); full suites run on the branch.

Review lines (per merged PR):
- #315 context trim — MERGE; 360/385 removed lines found verbatim in their new homes, rest read by hand; baseline 79,997 -> 76,259.
- #316 independent-review + word-budget rules — MERGE-AFTER-FIX (3 rounds), fixes applied as specified.
- #318 page-fidelity displayed text — MERGE after fix round + confirmation.
- #317 interaction-model skill (#291 part 1) — MERGE after fix round + confirmation; no client names leaked.
- #321 PROOF-OF-LOOK per section; final-look not due before derived Stage 6 — MERGE after fix round + confirmation.
- #307 graph-sweep guard; per-BRD conformance ledgers — MERGE after two fix rounds + confirmation.
- #325 extraction-quality section; token-burn fixes — MERGE after fix round + confirmation.
- #310 e2e OQL by alias, negative-span liveness, APP-DOWN, #302 credential regression fix — MERGE after fix round + 3 confirmations.
- #323 inbox promotions — MERGE after fix round + confirmation.
- #308 lint gate BLIND on reverted stock rules — MERGE (FIRST-SEEN reverted) after two fix rounds + confirmation.
- #328 pipefail + grep -q flake — MERGE; 0/30 flakes vs 15/100 on master. Follow-up: run-all resolves bin/lib/ subjects (reviewed, MERGE).
- #319 test-stack-up command-line ownership proof — MERGE after fix round + 2 confirmations.
- #327 change-intake core + on-demand detail; CRLF-safe change log — MERGE after fix round + confirmation.
- #320 follow-ups to #290/#292; Dispatching split out — MERGE after fix round + confirmation.
- #326 status delivery rounds (existing-app mode) — MERGE after fix round + confirmation.
- #312 lint batch 4: SEC014, toolkit CONV009 — MERGE after review + 2 fix rounds + confirmation; field run on mxcli 0.26.
- #324 exec.sh change classes, shared stale-lock takeover, SEC014 note echo — MERGE (joint review); follow-up owed for a takeover race after a mid-takeover crash.
- #322 MxTest after-startup guard, mxcli-run pid check — MERGE (joint review).
- #314 doctor property-set exit code; sync announces its bound — MERGE (joint review).
- #330 review follow-ups (exit 4/5 docs, pipefail, NUL strip) — MERGE-AFTER-FIX, fixes applied; takeover re-check dropped.

Field runs are still owed for most instruments in this batch (stated in each PR).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
@MendixMau

Copy link
Copy Markdown
Owner Author

Landed on master as part of the integration squash #329 (commit 31e90ae), after independent review — its Review: line is in that commit message. Closing as superseded.


Generated by Claude Code

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

Labels

None yet

Projects

None yet

2 participants