@bglocation/tune-context 2.5.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +80 -0
- package/README.md +138 -22
- package/bin/tune-context +1 -1
- package/bin/tune-context.mjs +167 -51
- package/cli/archive.mjs +322 -0
- package/cli/entry-point.mjs +73 -0
- package/cli/experiment.mjs +33 -1
- package/cli/version.mjs +118 -0
- package/eval/adoption-report.mjs +11 -5
- package/eval/analyze.mjs +141 -38
- package/eval/prompt-metrics.mjs +39 -18
- package/eval/report.mjs +56 -45
- package/eval/verdict.mjs +357 -33
- package/package.json +1 -1
- package/skills/token-efficiency/SKILL.md +1 -1
- package/skills/tune-context/SKILL.md +4 -2
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "tune-context",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Context-efficiency configurator for Claude Code: doctrine skills (token-efficiency, caveman, cdd, phase-workflow) plus a tune-context configurator that scaffolds a lean CLAUDE.md, subagents, settings and hooks.",
|
|
13
|
-
"version": "
|
|
13
|
+
"version": "3.0.0",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Szymon Walczak",
|
|
16
16
|
"url": "https://bglocation.dev"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "tune-context",
|
|
3
3
|
"displayName": "tune-context",
|
|
4
4
|
"description": "Context-efficiency configurator for Claude Code: doctrine skills (token-efficiency, caveman, cdd, phase-workflow) plus a tune-context configurator that scaffolds a lean CLAUDE.md, subagents, settings and hooks.",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "3.0.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Szymon Walczak",
|
|
8
8
|
"url": "https://bglocation.dev"
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,85 @@ All notable changes to `@bglocation/tune-context` are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.0.0] — 2026-10-15
|
|
9
|
+
|
|
10
|
+
EPIC-010 — the meter of the cost measurement comes from the same installed
|
|
11
|
+
version as the switch, and every artifact names the code that computed it.
|
|
12
|
+
**The major bump is for one deliberately breaking change** — the eval data
|
|
13
|
+
contract (see Changed): rows without a meter stamp and `analyze@1` aggregates
|
|
14
|
+
are refused. Everything else is additive.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`tune-context eval <step>`** (TASK-095) — `prompt-metrics`, `analyze`,
|
|
19
|
+
`verdict`, `report`, `adoption-report`, named after their modules, no aliases,
|
|
20
|
+
from any directory. Until now the meter existed only as `node eval/<file>.mjs`,
|
|
21
|
+
a relative path that resolves in a checkout of this repo and nowhere else, so
|
|
22
|
+
the switch (from `PATH`) and the meter (from `cwd`) could be two versions.
|
|
23
|
+
Everything after the step goes verbatim to the step's own parser:
|
|
24
|
+
`tune-context eval prompt-metrics --help` prints the step's usage. The two
|
|
25
|
+
doors print the same bytes, held to a baseline written before the refactor.
|
|
26
|
+
- **`tune-context eval verdict`** (TASK-096) — the verdict as JSON on stdout (the
|
|
27
|
+
object the report embeds) and one line per gate on stderr, ending in the
|
|
28
|
+
finding. Exit 0 for every verdict, a withheld one included. `verdict` and
|
|
29
|
+
`report` read their input through one function, so the page cannot accept what
|
|
30
|
+
the verdict refuses.
|
|
31
|
+
- **The meter stamp** (TASK-097) — rows carry `meter_version`, the aggregate
|
|
32
|
+
`analyzed_by`, the verdict `judged_by`: the bare release number for an npm
|
|
33
|
+
install, `<version>+<sha12>` for a checkout, `.dirty` for uncommitted changes in
|
|
34
|
+
`eval/ cli/ hooks/ bin/ package.json`, `+unknown` where git could not answer.
|
|
35
|
+
A version number alone would not do: under `npm link` a fix committed before
|
|
36
|
+
the release carries the number of the bug it fixes. `prompt-metrics` prints
|
|
37
|
+
`meter <stamp>` as the first line of its summary.
|
|
38
|
+
- **A seventh verdict gate, `meter_mixed`** (frozen by the PO on 2026-10-15) — the
|
|
39
|
+
whole chain (paired rows, `analyzed_by`, `judged_by`) must carry exactly one
|
|
40
|
+
stamp that names code; `.dirty`, `+unknown`, a second stamp or a step with none
|
|
41
|
+
hold the verdict, and the reason names the step that brought the stranger.
|
|
42
|
+
The six earlier thresholds are untouched.
|
|
43
|
+
- **`tune-context experiment archive --out <dir>`** (TASK-098) — the series'
|
|
44
|
+
transcripts (every session on an `exp/…` branch) and the ledger, copied out of
|
|
45
|
+
reach of Claude Code's retention, with `manifest.json` and `SHA256SUMS`
|
|
46
|
+
(`shasum -a 256 -c`). Append-only: a grown file replaces its copy, any other
|
|
47
|
+
change refuses the run. A destination inside a git work tree that git does not
|
|
48
|
+
ignore is refused — a transcript is the whole conversation.
|
|
49
|
+
- **`--ledger <path>`** for `prompt-metrics` and `analyze`, so an archive
|
|
50
|
+
recounts as `branch+ledger` on any machine.
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- **The eval data contract — deliberately breaking** (EPIC-010, PO decision 4).
|
|
55
|
+
Schemas are now `tune-context/analyze@2`, `tune-context/verdict@2` and
|
|
56
|
+
`tune-context/verdict-thresholds@2`. `analyze` refuses a whole input containing
|
|
57
|
+
a single row without `meter_version` (exit 1, nothing on stdout, file and line
|
|
58
|
+
on stderr), and `verdict`/`report` refuse an `analyze@1` aggregate. Scripts
|
|
59
|
+
that feed rows or aggregates written by an earlier version get exit 1. That is
|
|
60
|
+
the point: unstamped data cannot say which meter computed it, and the way back
|
|
61
|
+
to it is recomputing the transcripts — from an `experiment archive`, with the
|
|
62
|
+
current version or with `npx -p @bglocation/tune-context@<version>`.
|
|
63
|
+
- **The cost evaluation runbook and procedure** run the series from a registry
|
|
64
|
+
release (`npm link` only for an unreleased fix, from a clean commit), record
|
|
65
|
+
the meter stamp before the first pair, and archive after every arm.
|
|
66
|
+
- **Skills** — `token-efficiency` and `tune-context` point at
|
|
67
|
+
`tune-context eval adoption-report` instead of `node eval/…`, which never
|
|
68
|
+
resolved outside this repo, and `tune-context` names
|
|
69
|
+
`tune-context eval prompt-metrics` as the source of token cost, where it said
|
|
70
|
+
only OTEL with a collector had it. Skills are copies: **re-run
|
|
71
|
+
`tune-context init` after upgrading** to get them.
|
|
72
|
+
- **`adoption-report` points at `tune-context eval prompt-metrics`** (TASK-100) —
|
|
73
|
+
in `--help` and in the report's closing note on cost, instead of
|
|
74
|
+
`eval/prompt-metrics.mjs`, a path that resolves only in a checkout of this repo.
|
|
75
|
+
Likewise `analyze` — its `--help` and the last line of its summary — points at
|
|
76
|
+
`tune-context eval verdict` instead of a task number that is not in the package.
|
|
77
|
+
|
|
78
|
+
### Fixed
|
|
79
|
+
|
|
80
|
+
- **A reader that leaves early is a normal end for every eval step** — `report`
|
|
81
|
+
writing a long page, or `analyze` an aggregate of many tasks, into `| head`
|
|
82
|
+
ended in an `EPIPE` stacktrace; only `prompt-metrics` and `verdict` had a
|
|
83
|
+
guard. One guard now, called by each step.
|
|
84
|
+
- **`report --out` into a directory that does not exist** is one line and exit 1,
|
|
85
|
+
not a stacktrace.
|
|
86
|
+
|
|
8
87
|
## [2.5.0] — 2026-09-03
|
|
9
88
|
|
|
10
89
|
### Fixed
|
|
@@ -655,6 +734,7 @@ action needed on upgrade.
|
|
|
655
734
|
(`explore-cheap`, `reviewer`), the PreCompact / SessionStart / PostToolUse hook
|
|
656
735
|
suite, and the adoption report (`eval/adoption-report.mjs`).
|
|
657
736
|
|
|
737
|
+
[3.0.0]: https://gitlab.com/bglocation/tune-context/-/tags/v3.0.0
|
|
658
738
|
[2.5.0]: https://gitlab.com/bglocation/tune-context/-/tags/v2.5.0
|
|
659
739
|
[2.4.0]: https://gitlab.com/bglocation/tune-context/-/tags/v2.4.0
|
|
660
740
|
[2.3.1]: https://gitlab.com/bglocation/tune-context/-/tags/v2.3.1
|
package/README.md
CHANGED
|
@@ -6,7 +6,8 @@ scaffolds a cost-optimal, high-quality setup (lean CLAUDE.md + skills + subagent
|
|
|
6
6
|
+ settings + hooks + MCP).
|
|
7
7
|
|
|
8
8
|
**Self-contained** — zero dependency on any host repo. Ships two ways: a Claude
|
|
9
|
-
Code **plugin** and an **npm CLI** (`tune-context init`
|
|
9
|
+
Code **plugin** and an **npm CLI** (`tune-context init`; plus `verify`, and
|
|
10
|
+
`experiment` and `eval` for measuring the doctrine's cost), both under
|
|
10
11
|
`@bglocation`. Incubated inside the [`code-search-mcp`](https://gitlab.com/bglocation/code-search-mcp)
|
|
11
12
|
project, now extracted to its own repository.
|
|
12
13
|
|
|
@@ -16,9 +17,9 @@ tune-context/
|
|
|
16
17
|
├── skills/ # token-efficiency, caveman, cdd, phase-workflow, tune-context (+ templates/)
|
|
17
18
|
├── agents/ # explore-cheap, reviewer (model right-sizing)
|
|
18
19
|
├── hooks/ # PreCompact state-doc, SessionStart re-inject, adoption log, context reminder + hooks.json
|
|
19
|
-
├── bin/ + cli/ # `tune-context init` — deterministic
|
|
20
|
-
├── eval/ # adoption-report.mjs (the rag-usage analog), prompt-metrics.mjs, pricing.mjs, arm.mjs, analyze.mjs, verdict.mjs, report.mjs
|
|
21
|
-
└── scripts/ # verify-pack, smoke-init, sync-tune-context (dev/maintenance)
|
|
20
|
+
├── bin/ + cli/ # `tune-context init | verify | experiment | eval` — deterministic, no model in the loop
|
|
21
|
+
├── eval/ # adoption-report.mjs (the rag-usage analog) + mcp-probe.mjs, prompt-metrics.mjs, pricing.mjs, arm.mjs, analyze.mjs, verdict.mjs, report.mjs
|
|
22
|
+
└── scripts/ # verify-pack, verify-plugin, smoke-init, sync-tune-context (dev/maintenance)
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
## Quick start
|
|
@@ -165,7 +166,7 @@ claude --plugin-dir /path/to/tune-context # or a local che
|
|
|
165
166
|
```
|
|
166
167
|
|
|
167
168
|
The plugin manifest (`.claude-plugin/plugin.json`) and hook wiring
|
|
168
|
-
(`hooks/hooks.json`, via `${CLAUDE_PLUGIN_ROOT}`) register the
|
|
169
|
+
(`hooks/hooks.json`, via `${CLAUDE_PLUGIN_ROOT}`) register the five hook events
|
|
169
170
|
automatically; `bin/tune-context` is added to `PATH` while the plugin is enabled.
|
|
170
171
|
The marketplace catalog is [`.claude-plugin/marketplace.json`](.claude-plugin/marketplace.json).
|
|
171
172
|
|
|
@@ -246,7 +247,7 @@ by `tune-context init`), not here.
|
|
|
246
247
|
## Measuring adoption (does the doctrine actually get used?)
|
|
247
248
|
`hooks/adoption-log.mjs` logs one JSONL line per event to
|
|
248
249
|
`~/.claude/tune-context/adoption.jsonl` by default (under `CLAUDE_CONFIG_DIR`, the
|
|
249
|
-
override's `tune-context/adoption.jsonl` — see above), and `eval
|
|
250
|
+
override's `tune-context/adoption.jsonl` — see above), and `tune-context eval adoption-report`
|
|
250
251
|
turns that into a report — code search (semantic RAG vs the Grep tool, with
|
|
251
252
|
shell-plumbing greps kept out of the RAG share), doctrine skills, subagents, and
|
|
252
253
|
re-read churn — closing with an *earned* verdict: withheld below a PO-frozen floor of
|
|
@@ -321,10 +322,10 @@ instead of printing a number the doubling would have faked.
|
|
|
321
322
|
`~/.claude/tune-context/adoption.jsonl` by default (created on first write; under
|
|
322
323
|
`CLAUDE_CONFIG_DIR` it's the override's `tune-context/adoption.jsonl` instead).
|
|
323
324
|
|
|
324
|
-
4. **Read the report** any time, from
|
|
325
|
+
4. **Read the report** any time, from any directory:
|
|
325
326
|
```bash
|
|
326
|
-
|
|
327
|
-
|
|
327
|
+
tune-context eval adoption-report # default log location
|
|
328
|
+
tune-context eval adoption-report --log <path>
|
|
328
329
|
```
|
|
329
330
|
|
|
330
331
|
**Stop / opt out:** delete the `PostToolUse` + `SubagentStart` entries from
|
|
@@ -340,7 +341,45 @@ contracts-first edits, delegation — is only partly visible too: no `Skill` cal
|
|
|
340
341
|
but its trace shows up in the report's edit:read ratio, so the verdict never
|
|
341
342
|
reads zero `Skill` calls as "unused."
|
|
342
343
|
|
|
343
|
-
###
|
|
344
|
+
### The meter, from any directory (`tune-context eval`)
|
|
345
|
+
|
|
346
|
+
The cost measurement has two halves: the **switch** (`tune-context experiment`,
|
|
347
|
+
below) and the **meter** — `prompt-metrics → analyze → verdict → report`. Both
|
|
348
|
+
are one command, so both come from one installed version:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
tune-context eval --help # the steps
|
|
352
|
+
tune-context eval <step> --help # that step's flags
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
The step names are the module names (`eval/<step>.mjs`), one to one, no aliases.
|
|
356
|
+
Everything after the step belongs to the step — `tune-context eval
|
|
357
|
+
prompt-metrics --help` prints the step's usage, not the CLI's.
|
|
358
|
+
|
|
359
|
+
**Every artifact names the code that computed it.** Rows carry `meter_version`,
|
|
360
|
+
the aggregate `analyzed_by`, the verdict `judged_by` — a *meter stamp*, which
|
|
361
|
+
identifies code rather than a release number:
|
|
362
|
+
|
|
363
|
+
| stamp | when |
|
|
364
|
+
| --- | --- |
|
|
365
|
+
| `3.0.0` | an npm install — immutable on the registry, rebuilt on demand with `npx -p @bglocation/tune-context@3.0.0` |
|
|
366
|
+
| `3.0.0+0123456789ab` | a checkout of this repo, clean in `eval/ cli/ hooks/ bin/ package.json` |
|
|
367
|
+
| `3.0.0+0123456789ab.dirty` | the same with uncommitted changes there (untracked files included) |
|
|
368
|
+
| `3.0.0+unknown` | a checkout git could not read |
|
|
369
|
+
|
|
370
|
+
A release number alone is not enough: under `npm link` a fix committed before
|
|
371
|
+
the release carries the same number as the bug it fixes, which is how EPIC-009's
|
|
372
|
+
verdict ended up computed by a meter nobody could name. Rows without a stamp —
|
|
373
|
+
anything from before this contract — are refused, not converted: the way back to
|
|
374
|
+
old data is recomputing the transcripts (see
|
|
375
|
+
[`experiment archive`](#keeping-the-evidence-tune-context-experiment-archive)).
|
|
376
|
+
|
|
377
|
+
*Developer path, from a checkout of this repo only:* `node eval/<step>.mjs` is
|
|
378
|
+
the same code behind a second door — same parser, same bytes on stdout — and
|
|
379
|
+
stamps `<version>+<sha>` (`.dirty` while you have uncommitted changes, which
|
|
380
|
+
holds every verdict by design). A measurement runs through `tune-context eval`.
|
|
381
|
+
|
|
382
|
+
### Per-prompt metrics from transcripts (`tune-context eval prompt-metrics`)
|
|
344
383
|
|
|
345
384
|
The adoption report reads *our* hook log. `prompt-metrics` reads the transcripts
|
|
346
385
|
**Claude Code writes itself** (`<config dir>/projects/<slug>/<session>.jsonl`) and
|
|
@@ -348,11 +387,14 @@ emits one JSONL row per prompt — time, tokens split by cache class, model,
|
|
|
348
387
|
effort, tools, delegations, and cost in dollars (`eval/pricing.mjs`):
|
|
349
388
|
|
|
350
389
|
```bash
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
390
|
+
tune-context eval prompt-metrics # every transcript
|
|
391
|
+
tune-context eval prompt-metrics --transcript <path.jsonl> # one session
|
|
392
|
+
tune-context eval prompt-metrics --dir <dir> > prompts.jsonl # JSONL on stdout
|
|
354
393
|
```
|
|
355
394
|
|
|
395
|
+
The first line of the summary on stderr is `meter <stamp>` — the line a
|
|
396
|
+
measurement series copies into its run journal before the first pair.
|
|
397
|
+
|
|
356
398
|
Rows go to stdout, a run summary to stderr — so a pipe stays clean. Two things
|
|
357
399
|
that source makes possible and a hook cannot: it works for **past** sessions, and
|
|
358
400
|
it works when tune-context is switched **off** (a disabled hook records nothing,
|
|
@@ -456,17 +498,67 @@ The measurement protocol that uses this — paired runs on `exp/<task>/<arm>`
|
|
|
456
498
|
branches, frozen prompt script, verdict gates — is
|
|
457
499
|
[COST_EVALUATION_PROCEDURE.md](COST_EVALUATION_PROCEDURE.md).
|
|
458
500
|
|
|
459
|
-
###
|
|
501
|
+
### Keeping the evidence (`tune-context experiment archive`)
|
|
502
|
+
|
|
503
|
+
Claude Code deletes transcripts after `cleanupPeriodDays` (30 by default). A
|
|
504
|
+
meter bug found after that cannot be fixed by recomputing — it voids the series.
|
|
505
|
+
So the series' transcripts are archived **after every arm**, together with the
|
|
506
|
+
ledger that assigns them to arms:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
tune-context experiment archive --out ~/tune-context-archive/series-1
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
- **What goes in:** every transcript with at least one entry on an `exp/…`
|
|
513
|
+
branch — including branches that only mimic the convention, so a recount can
|
|
514
|
+
reject them by name — and the ledger, in the config dir's own layout, plus
|
|
515
|
+
`manifest.json` (hash, size, branches, Claude Code versions per file) and
|
|
516
|
+
`SHA256SUMS`, checkable with `shasum -a 256 -c SHA256SUMS`. Ordinary work is
|
|
517
|
+
not archived: it is not evidence, and it is your whole history.
|
|
518
|
+
- **Append-only.** An unchanged file is skipped; a file that grew (a resumed
|
|
519
|
+
session, a longer ledger) replaces its copy and is named; any other change
|
|
520
|
+
refuses the whole run and leaves the archive as it was. A file Claude Code has
|
|
521
|
+
since deleted stays — that is what the archive is for. Re-running costs
|
|
522
|
+
nothing.
|
|
523
|
+
- **A transcript is the whole conversation** — contents of the files read,
|
|
524
|
+
command output, possibly secrets. A destination inside a git work tree that
|
|
525
|
+
git does not ignore is refused, with no flag to override it: add it to
|
|
526
|
+
`.gitignore` or archive outside any repository.
|
|
527
|
+
|
|
528
|
+
Recomputing from the archive works after the originals are gone. `--ledger`
|
|
529
|
+
points both steps at the archived ledger — without it the rows come out
|
|
530
|
+
`exp_arm_source: branch`, which the paired pool does not accept:
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
tune-context eval prompt-metrics --dir ~/tune-context-archive/series-1/projects \
|
|
534
|
+
--ledger ~/tune-context-archive/series-1/tune-context/experiment.jsonl > rows.jsonl
|
|
535
|
+
tune-context eval analyze --rows rows.jsonl \
|
|
536
|
+
--ledger ~/tune-context-archive/series-1/tune-context/experiment.jsonl > aggregate.json
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
To recompute with the meter that computed the original rows instead of the
|
|
540
|
+
current one, run every step with that version:
|
|
541
|
+
`npx -p @bglocation/tune-context@<version> tune-context eval …`.
|
|
542
|
+
|
|
543
|
+
### Per-arm aggregate (`tune-context eval analyze`)
|
|
460
544
|
|
|
461
545
|
Rows are data, not an answer. `analyze` turns a stream of them into one
|
|
462
546
|
aggregate per arm — and stops one step short of ruling which arm won, because
|
|
463
547
|
the thresholds that would do the ruling are frozen by the PO, not by the model:
|
|
464
548
|
|
|
465
549
|
```bash
|
|
466
|
-
|
|
467
|
-
|
|
550
|
+
tune-context eval prompt-metrics > rows.jsonl
|
|
551
|
+
tune-context eval analyze --rows rows.jsonl > aggregate.json # summary on stderr
|
|
552
|
+
tune-context eval prompt-metrics | tune-context eval analyze > aggregate.json # or through a pipe
|
|
468
553
|
```
|
|
469
554
|
|
|
555
|
+
- **No stamp, no aggregate.** One row without `meter_version` refuses the
|
|
556
|
+
**whole** input — exit 1, nothing on stdout, file and line of the first three
|
|
557
|
+
on stderr. A file mixing old and new rows is a pasted-together input, and
|
|
558
|
+
skipping the old ones would hand the verdict an aggregate that looks complete.
|
|
559
|
+
Rows from *different* meters are aggregated (the per-pair costs stay visible
|
|
560
|
+
for planning a repeat); the verdict holds on them.
|
|
561
|
+
|
|
470
562
|
- **Two populations, never one number.** The paired measurement (branch
|
|
471
563
|
`exp/<task>/<arm>`, everything else held constant) and ordinary daily work
|
|
472
564
|
(attributed by the ledger, nothing held constant) come out as two separate
|
|
@@ -491,23 +583,47 @@ node eval/analyze.mjs --rows rows.jsonl > aggregate.json # summary on stderr
|
|
|
491
583
|
the effect being looked for. The per-pair, per-arm cost is carried so the two
|
|
492
584
|
can be separated arithmetically (procedure §11b).
|
|
493
585
|
|
|
494
|
-
### The
|
|
586
|
+
### The verdict as data (`tune-context eval verdict`)
|
|
495
587
|
|
|
496
|
-
|
|
497
|
-
|
|
588
|
+
May this run say "one arm is cheaper"? Seven gates answer, each with its own
|
|
589
|
+
frozen threshold, and every gate is evaluated every time — a run that fixes one
|
|
590
|
+
problem and reruns should not pay a run to learn about the second:
|
|
498
591
|
|
|
499
592
|
```bash
|
|
500
|
-
|
|
501
|
-
node eval/report.mjs --aggregate aggregate.json --out report.html
|
|
502
|
-
node eval/report.mjs --aggregate aggregate.json --rejected-runs journal.json --out report.html
|
|
593
|
+
tune-context eval verdict --aggregate aggregate.json --rejected-runs journal.json > verdict.json
|
|
503
594
|
```
|
|
504
595
|
|
|
596
|
+
stdout is the verdict as JSON — the same object the report embeds — and stderr
|
|
597
|
+
one line per gate (measured against threshold, and why it holds) ending in the
|
|
598
|
+
finding: `withheld: N gate(s) hold — …`, `no-difference` or `cheaper-arm: <arm>`.
|
|
599
|
+
Exit 0 for every verdict, a withheld one included; exit 1 only for input that
|
|
600
|
+
cannot be read.
|
|
601
|
+
|
|
505
602
|
`--rejected-runs` carries what the run journal recorded as §7 invalidations
|
|
506
603
|
(`[{ session, task, arm, prompts, reason }]`). **Omitting it is not the same as
|
|
507
604
|
passing `[]`**: the first says nobody asked the journal, and the verdict is held
|
|
508
605
|
for exactly that reason — a rejection share whose denominator is unknown is a
|
|
509
606
|
quietly dropped sample wearing a passing gate.
|
|
510
607
|
|
|
608
|
+
The seventh gate, `meter_mixed` (frozen by the PO on 2026-10-15), passes only
|
|
609
|
+
when the whole chain — the paired rows, `analyzed_by`, `judged_by` — carries
|
|
610
|
+
exactly one stamp, and one that names code: not `.dirty`, not `+unknown`. A
|
|
611
|
+
series therefore runs from a release, or from a clean commit.
|
|
612
|
+
|
|
613
|
+
### The report (`tune-context eval report`)
|
|
614
|
+
|
|
615
|
+
One HTML file — data embedded as JSON, charts as inline SVG, no network and no
|
|
616
|
+
build step, so it opens from `file://` on a machine with nothing installed:
|
|
617
|
+
|
|
618
|
+
```bash
|
|
619
|
+
tune-context eval report --aggregate aggregate.json --out report.html
|
|
620
|
+
tune-context eval report --aggregate aggregate.json --rejected-runs journal.json --out report.html
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
It reads its input exactly as `eval verdict` does — one reader, so the page can
|
|
624
|
+
never accept what the verdict refuses — and names the stamp of every step of the
|
|
625
|
+
chain in its header. `--rejected-runs` means the same as above.
|
|
626
|
+
|
|
511
627
|
- **A withheld verdict is the page, not a footnote.** When a gate holds, the
|
|
512
628
|
arm-comparison chart is *not drawn at all* — not greyed out, not captioned. Two
|
|
513
629
|
bars side by side assert "this is a comparison" more loudly than any caption
|
package/bin/tune-context
CHANGED