Commit graph

110 commits

Author SHA1 Message Date
039e4191dd CB-WP-0006 T09: retrospective — a real instrument, hardened three times
The question was whether M-D1-MUT is a real instrument or a name-counter
with extra steps, given that writing a weak mutation is as easy as writing
a strong one.

It is real, but only because it was hardened three times in one pass. Five
controls now stand between a mutation and a red verdict — the mutation
must apply, the baseline must be green, the tree must be restored and
verified, the failure must match a stated reason, and that stated reason
must be absent from passing output — and every one of them exists because
its failure actually occurred. The last is the sharpest: the FA guard
needed a guard, because my first AM-2 expect was "AM-2", which the passing
report contains.

Generalizable: an instrument that measures whether other instruments work
needs more controls than the instruments it measures. M-D1-MUT carries
five; dep-weight and rule-coverage carry one each. That asymmetry is the
cost of a meta-instrument, and a project adding one should budget for it.

A worse failure mode than CB-WP-0005 predicted: a mutation can become weak
without anyone touching it. AM-6's went SURVIVED when T04 moved its gate
from debug to release — nothing about the row, the mutation or the code
changed, only the headroom. Mutation strength is coupled to measurement
conditions, so a mutation is not a write-once artifact. CB-WP-0005 T08's
stronger remedy — mutations written by someone other than the author — was
NOT tested and should not be assumed unnecessary: EXPECT-VACUOUS covers
the cheap failure, not the expensive one AM-6 demonstrated.

The "removes the manual path" test is settled as a predictor of cost, not
of worth. mutation-check fails it outright and produced six defects
nothing else would have found.

Prediction error collapsed: 4-5x, then 2.5x, now small — because this pass
predicted per task, as a mechanism, with the alternative named. Both
branches are outcomes someone must defend, so the prediction cannot be
dodged. AM-3 and AM-4c took the second branch and are better resolved for
it than if a number had been forced.

No InnerLoop change. v1.4's mutation requirement is one pass old and
changing it before a second use would be the invention-in-isolation INTENT
warns about — the same argument used to amend K14 four hours earlier.

Named next candidate: specs/SessionShape.md. SS-01..SS-05 have been stated
since CB-WP-0003 and none has ever been enforced. This pass ran at 2.5x
the context ceiling its own spec sets and nothing said a word.

CB-WP-0006 status -> done, 9/9.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 13:40:58 +02:00
ce353adde8 CB-WP-0006 T08: control loop — 4 of 14 to 8 of 14, and a cost regression
Test 1: the enforced count rose. AM-2, AM-6, AM-9 and AM-11 moved from
unmutatable to red; AM-7's hash clause was re-earned so it is 2/3 rather
than 1/3. Kernel spec->code link 15/18 -> 18/18, names only.

Both denominators are stated. 8 of 14 is 57%, but four rows cannot be
enforced — AM-3 blocked on an artifact, AM-4c withdrawn, AM-5 declared
ungated by the spec, AM-10 withdrawn — so it is 8 of 10 enforceable. The
14 stays the headline and AM-4c stays in it on purpose: a score improved
by deleting the question is not an improvement.

Test 2: one row regressed and was caught. Moving AM-6's gate from debug to
release turned its mutation SURVIVED, because 4,000 black_box iterations
were calibrated against debug's 3.4x headroom and are invisible against
release's 20x. The generalizable finding is that a weak mutation is not a
fixed property of a row — it can become weak when the row's measurement
conditions change, without the row, the mutation or the code being
touched. Final SURVIVED count: 0.

Test 3 is now mechanical rather than asserted. mutation-check gained an
EXPECT-VACUOUS verdict: if a row's expect string appears in PASSING
output, the FA guard would accept any failure at all, so the row is
reported broken rather than red. Final run: 0 vacuous expects across 14
rows. The control exists because the failure happened — my first expect
for AM-2 was "AM-2", which appears in the passing report and would have
accepted a compile error as proof of enforcement.

The cost result is a refutation, not a win. Mechanical share rose to 50%,
the highest ever recorded and above the 38% baseline that motivated
CB-WP-0004. That is not a tooling regression: environment setup and task
closes are still at zero two passes on. It is the other half of CB-WP-0004
T06's finding arriving in force — text patching (45 turns, $13.79) and
orientation (19 turns, $10.97) never had their manual path removed, and a
code-heavy pass is exactly where that spends.

Mean context 493,486 against a 200,000 target, up from 315,170. SessionShape
has stated SS-01..SS-05 since CB-WP-0003 and none has ever been enforced —
the only acceptance-adjacent numbers in this project with no gate at all,
in a pass whose entire subject was ungated numbers.

Numbering corrected: the workplan said CB-EV-0004, which CB-WP-0005
already used. This is CB-EV-0005.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 13:39:12 +02:00
c51c7c9b47 chore: stop tracking tools/__pycache__
Some checks failed
ci / check (push) Failing after 3s
Bytecode was committed and kept reappearing as a dirty-tree false
positive after every tool run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 12:49:17 +02:00
f78bef5d8b chore: mark CB-WP-0006 T07 done (measured: 20 responses, $12.06, opus-5)
Some checks failed
ci / check (push) Failing after 4s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 12:47:28 +02:00
327bda64ab CB-WP-0006 T07: implement K18, amend K14
Two rules, two different answers, which is the point of a task phrased
"implement, or amend and say why".

K18 is implemented. "Criterion benches driving the same scenario format at
scale" was false — the bench hardcoded its commands and never touched
ScenarioFile, while MetricsAndScenarios §3 pointed at a benchmarks/
directory containing only baselines/. benchmarks/synthetic-3p.yaml now
holds the workload and both the bench and bench_shape read it: the
workload is data, not code.

A second defect surfaced while fixing the first. After the bench switched
to the file, bench_shape still hardcoded the same sequence, so the
workload existed twice — deleting end_round from the YAML broke bench-test
while bench_shape kept passing. Duplicated-fact drift in executable form.
Both now read the same include_str! and deleting a command breaks both.

Explicitly not claimed: this does not unblock AM-3. AM-3's baseline is a
declarative game object — moves, turn order, rules. synthetic-3p.yaml is a
command list; the rules live in games/ground. Marking it as AM-3's subject
would compare a script to a game definition, which is the category error
AM-3 is blocked on. The file says so in its own header, where the next
person will be tempted.

K14 is amended. CommitWindow had zero non-test users and GROUND enforces
the same contract inline. Wiring GROUND through it was rejected: it would
change the serialized shape of `selections`, which four scenario files
assert by dot-path and every state hash depends on, for the sole benefit
of making a sentence literally true.

The deciding argument is INTENT's, not convenience: abstractions are
extracted from working games rather than invented in isolation, and no
concept becomes canonical until it survives a second concrete use.
CommitWindow was invented before any game needed it and has survived none.
Imposing it on GROUND would manufacture the first use rather than discover
it. So K14 states what is actually guaranteed, CommitWindow is marked
provisional in the source, and it carries a delete-by date of 2026-12-31.

Kernel spec->code link 16/18 -> 18/18, stated with the caveat the gate
prints every run: that is about names, not assertions.

Two self-tests broke and both broke correctly. rule-coverage's gate test
hardcoded "unlinked rules exist today" and failed when the last one was
linked; it now computes that and asserts the gate fails iff rules are
unlinked. facts' text check rejected k_unlinked once it became
legitimately empty; empty now renders as "(none)" and the check
distinguishes absent from empty.

M-D1-MUT: 8 of 14, unchanged — K14 and K18 are kernel rules, not
acceptance rows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 12:47:16 +02:00
4e82a520b3 chore: mark CB-WP-0006 T06 done (measured: 20 responses, $6.63, opus-5)
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 11:05:45 +02:00
1edadac9a2 CB-WP-0006 T06: K10 replay bundles, --replay, and AM-7 re-earned
INTENT design decision 8 of 10, unimplemented for six passes. cb-sim had
no flag parsing at all, so --replay had nowhere to go.

The bundle is manifest + commands.log + initial.snapshot + expected.yaml,
dev-only behind the scenarios feature and charged to AM-4b. The command
stream goes through the K11 framing built in T05, so a truncated bundle is
detected rather than replayed short — the two tasks compose rather than
duplicating.

The reviewer's D2 correction was real: this was not "a directory of four
files". Pass carried only the end state, RunOutcome::Failed was a
formatted String, and scenario.rs created an EventLog, appended to it and
never read it. All three had to change.

The first round trip failed to reproduce, and the cause is worth keeping:
state_hash_hex over a serde_json::Value is a different canonical form than
over the typed aggregate — Value's map is key-sorted, a struct serializes
in declaration order. The bundle was written with one basis and verified
with the other. A round trip written to recompute its own comparison value
would have PASSED this bug; it failed because the recorded hash came from
the producing process, which is control 2's entire purpose.

make replay-test implements ADR-0005 §6's four controls, 14/14: a
committed deliberately-failing fixture outside the corpus with covers: []
so it neither fails `make sim` nor inflates AM-1; a tampered recorded hash
must fail; a log short by one byte and a corrupted length prefix must be
rejected; and a mutated manifest seed must fail — which bites only because
replay re-derives the initial state from seed+setup and checks it against
the recorded snapshot, since restoring from the snapshot alone would leave
the seed inert. Plus a control on the controls: the bundle must still
replay after every mutation is reverted.

AM-7's hash-identical clause is re-earned. The probe records a hash per
per-game segment and replays each from its own genesis; folding from the
wrong seed now fails. That is the clause ADR-0005 §4 withdrew as
mutation-proven inert. The scaling >= 0.9x clause is still unenforced, so
AM-7 stays PARTIAL — reported, not rounded up.

Kernel coverage 15/18 -> 16/18. facts-check immediately caught the spec's
copy of that number going stale, on a number that moved the same hour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 11:05:37 +02:00
6037467478 chore: mark CB-WP-0006 T05 done (measured: 14 responses, $4.46, opus-5)
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:51:01 +02:00
98c6cd24c3 CB-WP-0006 T05: K9's assertion, K11's format, and the AM-11 suites
K11 is implemented: crates/cb-events/src/store.rs, magic + version header,
4-byte little-endian length prefix, append-only. Reimplemented not
assimilated per ADR-0005 §2 — no new dependency, and AM-4a/AM-4b are
unchanged at 246,250 / 317,021 because nothing entered the graph.

The operative clause is "detected", so corruption is tested rather than
assumed: a tail short by one byte, a half-written length prefix, a length
prefix corrupted to claim more than the file holds, foreign magic, and a
future format version are each rejected with a distinct error. A reader
that accepts a truncated tail is worse than no format, because it silently
returns a short history that looks complete.

AM-11 is earned. LogStore has two impls — MemLogStore and FileLogStore —
driven through ONE conformance(). The trait carries raw/set_raw precisely
so the corruption controls live in the shared suite: a format contract
that only one impl enforces is not a contract. The same shape is
retro-fitted to KernelRng, which is what AM-11 actually names: ChaChaRng
and NullRng now pass one suite asserting bounds, draw(1) == 0, determinism
across fresh instances, and shuffle preserving the multiset. They were
previously exercised by two separate tests, which is why "met, narrow" was
never earned and ADR-0005 §4 downgraded it.

K9 gets the assertion it did not have: snapshot at seq N + events N+1..M
must equal the from-genesis fold, hash-compared, on GroundState,
single-seed on purpose — AM-7's probe folds a multi-seed log, which is not
a replay of anything, and that defect is not repeated. Two positive
controls: the log must exceed 50 events, and the mid-log snapshot must
differ from the end state or "apply the remainder" is vacuous.

Proof it works: the exact mutation that SURVIVED in CB-WP-0005 — making
Snapshot::take discard its EventSeq — now fails on the K9 assertion.

AM-11's mutation breaks NullRng::draw to return its bound and the shared
suite fails. That is what M-D4-SWAP claims — either impl substitutable —
and exactly what two separate per-impl tests could never demonstrate.

M-D1-MUT: 7 -> 8 of 14. CB-EV-0001's scoreboard is refreshed: AM-2, AM-5
and AM-9 added, AM-6 moved to enforced, and the headline total corrected
from 4 to 8 — it had gone stale inside the same workplan that produced it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:50:52 +02:00
5f7d9015d9 Fix the AM-5 instrument to measure quietly; the breach was not real
Some checks failed
ci / check (push) Failing after 3s
T03 reported AM-5 at 87.0 s / 61.3 s and called it a 45% breach of the
60 s target. Re-measured with the fixed instrument on a quiet machine:

  load before measuring: 0.14 per CPU over 8 CPUs — quiet
  dev toolchain (default features)         37.3 s  [ok  target <= 60 s]
    best of 3: 37.3, 42.9, 46.2  (spread 1.24x)
  shipped runtime (--no-default-features)  41.2 s  [ok  target <= 60 s]
    best of 3: 41.2, 50.8, 54.2  (spread 1.32x)

AM-5 is MET with 1.6x headroom. The 87.0 s was measured while the machine
was busy with mutation-check and cargo builds — a timing measurement under
contention measures the contention.

That is the same error class as AM-6's, committed two tasks later in the
same session by the same author, in the row immediately after the one
where it was diagnosed. Knowing the failure mode did not prevent it; only
building the guard did. That is the InnerLoop v1.2 design-goal argument
holding up under a third instance: optimize for cheap correction, because
prevention keeps not converging.

The instrument now refuses to measure above 0.5 load per CPU, takes the
best of 3, and warns when the spread exceeds 1.25x. Best, not worst: a
build-time ceiling asks whether the machine can do it in 60 s, the mirror
of AM-6's best-of-N for a throughput floor. The spread warning fired on
the shipped-runtime samples — consecutive clean builds degrade 37.3 ->
46.2 — so a quiet machine is not a uniform one either.

The escalation to a maintainer decision is withdrawn: there is no breach.
The build profiling done while the breach was believed real is recorded in
the log rather than acted on — 174 s of CPU work at only 3.2x parallelism
on 8 cores, a ~22 s serial proc-macro chain, lto=thin worth ~6 s, and
pinning ppv-lite86 to drop zerocopy making it worse (23 -> 25 crates).
With 1.6x headroom there is nothing to buy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:37:25 +02:00
ca832329e7 Move CB-WP-0006 delivery notes to a log; keep the plan a plan
Some checks failed
ci / check (push) Failing after 4s
loop-lint failed at 427 lines. Third workplan in this project to cross the
~400-line loadability limit, which makes it structural rather than a
formatting nuisance: delivery notes accumulate in a file whose job is to
state intent, and every remaining task would push it further over.

history/260801-cb-wp-0006-log.md now holds the per-task narrative; the
workplan keeps a one-line pointer per task. 427 -> 270 lines, with room
for T05-T09. T08 draws on both.

Same fix shape as CB-WP-0005, where the limit exposed a circular reference
rather than mere length. A gate that keeps finding real structure is worth
more than its stated rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:07:58 +02:00
959dc39cf0 chore: mark CB-WP-0006 T04 done (measured: 22 responses, $10.06, opus-5)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:06:22 +02:00
db6445ae37 CB-WP-0006 T04: withdraw AM-4c; and fix where AM-6 is measured
AM-4c is withdrawn from the acceptance table and retained as a reported
diagnostic. GameKernel §5a carries the argument.

The ratio has no monotone better direction. INTENT's rule is "own the
semantics, assimilate the implementation": rising can mean owning
semantics properly or reimplementing what should have been assimilated;
falling can mean leverage or dependency bloat. A target requires knowing
which way is better. It is also redundant — AM-4a/AM-4b bound the
denominator and AM-2 bounds own-source density, so AM-4c is a ratio of two
already-targeted quantities.

Measured at withdrawal: 1,426 own lines per 100k third-party (shipped),
1,107 (dev). make dep-weight now prints both, labelled diagnostic — the
row was never actually reported before.

M-D1-MUT keeps AM-4c in its denominator on purpose and says so in the
output. Dropping it would move the score 7/14 -> 7/13 without enforcing
anything: a score improved by deleting the question.

Decided before Phase B deliberately, since ADR-0005 predicts own-source
growth that will move this ratio; deciding after would be the retarget
§Step 4 forbids.

A T01 correction found here. The AM-6 gate failed inside `make all` at
38,753 ev/s against 341,280 in isolation — a 9x drop, because cargo test
runs binaries and threads concurrently. A throughput assertion inside a
parallel harness measures contention, not throughput. T01's measurement
was valid; its gate placement was not.

Fixed by running it only where valid — #[ignore] plus `make am6` in
release with --test-threads=1, now 2.0M ev/s at 20.2x headroom — and not
by lowering the target, which T01 forbade. My first attempt did drift that
way, adding a debug "sanity floor" of 50,000, and was backed out: a second
threshold is still a second chance to tune.

The mutation then went SURVIVED on the first run after the move. 4,000
black_box iterations were calibrated against debug's 3.4x headroom and are
invisible against release's 20x. Raised to 100,000; back to red. A weak
mutation is not a fixed property of a row — it can become weak when the
row's measurement conditions change.

Tier S (amends one row, creates no capability), chaos d4=2, no override.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:06:00 +02:00
83db14f2e2 chore: mark CB-WP-0006 T03 done
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:59:40 +02:00
e7312b3b8a CB-WP-0006 T03: measure AM-5 and AM-9; AM-5 breaches
AM-9 is met and gated: 13.4 MB peak RSS against a 64 MB target, 4.8x
headroom, in `make all` via --fast. CB-EV-0001's "very unlikely to bind"
was right, but it is now measured rather than assumed, and verified red by
a property mutation (a 300 MB allocation in the workload).

AM-5 is BREACHED on both readings, on the machine the spec names:

  dev toolchain (default features)        87.0 s  [FAIL target <= 60 s]
  shipped runtime (--no-default-features) 61.3 s  [FAIL target <= 60 s]

bnt-lap001, 8 cores — a direct comparison, not a directional one. A row
declared "recorded not gated" and never recorded fails its own target by
45% on first measurement.

The tool reports and exits 0 because the spec says the row is ungated.
Gating it is a spec change needing an ADR; a tool that promotes itself is
how a target starts binding without anyone deciding it should. So AM-5
stays unmutatable — for the accurate reason now — and the breach is raised
as a maintainer decision: speed the build, move the target by ADR (arguing
why 60 s was wrong rather than why 87 s is convenient), or withdraw the
row.

The measurement itself had a real bug, found only by cross-validation.
getrusage(RUSAGE_CHILDREN) is a high-water mark across every reaped child,
so it attributed cargo's memory to the workload and reported 38.2 MB for a
run that used 12.3 MB — a 3x over-report that was plausible, passed its
target, and would have been published. Fixed with os.wait4, which returns
that specific child's rusage, and the self-test now cross-checks against
/usr/bin/time -v.

That is the false-accusation shape in the measurement layer rather than
the mutation layer: an instrument confidently reporting a number it had
not earned.

Also: the clean build measures into a throwaway CARGO_TARGET_DIR rather
than running `cargo clean`, so measuring the metric does not cost several
minutes of rebuild afterwards. A metric that punishes its own measurement
gets measured once and never again.

M-D1-MUT: 6 -> 7 of 14.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:59:32 +02:00
fd67813876 chore: mark CB-WP-0006 T02 done
Some checks failed
ci / check (push) Failing after 4s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:38:24 +02:00
1c3019c7e8 CB-WP-0006 T02: instrument AM-2; report AM-3 blocked, with the argument
Both rows were unmutatable for the same stated reason. They resolved
differently, and the difference is the point.

AM-2 is instrumented and enforced — tools/size-metrics.py, in `make all`:

  AM-2: 27.2 LOC/rule  [ok  target <= 40]  (1.47x headroom)
        1,575 impl lines / 58 rules

Tests are excluded because AM-2 asks what a rule costs, not how much it is
exercised; lib.rs is ~18% test code and including it would have flattered
the number. This matters because AM-2 is AM-1's anti-gaming pair: 100%
rule coverage means nothing if the rules are trivially small, and AM-1 has
been reported met since CB-WP-0001 with its pair uninstrumented.

Verified red by a property mutation — ~800 lines of filler injected into
the impl, pushing the ratio past 40 — not a threshold tweak. The expect
string is the precise failure signature "FAIL target <= 40"; my first
attempt used "AM-2", which also matches passing output and would have
made the FA guard vacuous.

AM-3 is BLOCKED, not uninstrumented, and that is a finding rather than a
deferral. It measures LOC to express the CB-RES-0001 synthetic game on our
kernel, against a boardgame.io baseline of ~36 LOC for a declarative 3p
commit/reveal game object. That artifact has never been built: games/
contains only ground, and benches/synthetic.rs drives GROUND rather than
defining a synthetic game. Measuring GROUND's 1,575 impl lines against a
36-line synthetic game object would compare two different games and call
the difference a D1 result.

So the tool ships the measurement — a marker-delimited region, self-tested
— and reports the row blocked, naming the missing artifact. A number would
have been worse than a blank. It stays unmutatable and still counts
against M-D1-MUT per ADR-0005 §1: a row that cannot fail asserts nothing,
however good the reason.

M-D1-MUT: 5 -> 6 of 14.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:38:15 +02:00
a12861b85c chore: mark CB-WP-0006 T01 done
Some checks failed
ci / check (push) Failing after 4s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:32:23 +02:00
c43754f0fe CB-WP-0006 T01: assert the AM-6 throughput target
Nothing in the workspace compared any number to 100,000 events/s while
the evidence file reported "AM-6 | met, 16.5x". Now a test does — a test,
not a bench, because Criterion reports throughput and asserts nothing,
which is why this row measured nothing for six passes.

Measured on bnt-lap001: 341,280 ev/s in debug (3.4x the target), ~2.4-3.1M
in release. The spec target holds even in an unoptimized build, so the
gate needs no cfg split and runs in the ordinary `make test`.

The trap this task named — loosening a flaky timing assertion until it
never fires — is avoided by construction. The threshold is the spec value,
untouched; the constant says lowering it requires an ADR; and the failure
message repeats that, states measured headroom, and names reference
figures, so an agent hitting a red AM-6 is told not to tune it in the
place they are actually reading. Robustness comes from best-of-N, not from
a lower bar: a throughput floor asks whether the machine is capable, so
transient load should not fail the build.

Two positive controls in the test: a run that applied fewer than 50,000
events, or measured zero elapsed time, fails rather than scoring as
infinite throughput.

Verified by a PROPERTY mutation — 4,000 black_box iterations injected into
GroundState::fold, the hot path — not a threshold tweak, which would only
prove the comparison runs.

And the FA class found last pass is now gated. mutation-check rows gained
an `expect` field: the mutant's output must contain the row's stated
failure string or the verdict is WRONG-REASON, not red. Without it a
mutation that merely failed to compile would credit its row with an
assertion it does not have. Verified by pointing expect at a string the
verifier never prints and watching the verdict flip. This is remedy (2)
from the CB-WP-0005 retrospective, built a task earlier than planned
because the class it guards is the newest and most dangerous.

M-D1-MUT: 4 -> 5 of 14.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:32:16 +02:00
ba7c2f88ae chore: regenerate WORK-RECORDS.md (fix-consistency)
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:25:49 +02:00
custodian-sync
a791ed25e4 chore(consistency): sync task status from DB [auto]
Some checks failed
ci / check (push) Failing after 3s
Updated by fix-consistency on 2026-07-31:
  - update .custodian-brief.md for clay-borg
2026-07-31 18:25:22 +02:00
dfd0d6d56b Activate CB-WP-0006; T01 in progress
Some checks are pending
ci / check (push) Waiting to run
Workplan status proposed -> in_progress, T01 (assert the AM-6 throughput
target) todo -> progress. Local files are source of truth; the hub
reflects them after sync.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:25:11 +02:00
245d7fd3e5 Condense CB-WP-0005 T02's note to a pointer at CB-EV-0004
Some checks failed
ci / check (push) Failing after 3s
loop-lint failed at 406 lines, third time this file has bumped the ~400
limit. Trimming again would have been the wrong fix: the T02 delivery note
duplicated CB-EV-0004 Test 2 almost verbatim, which is a duplicated fact
in the workplan that most recently added a duplicated-fact gate.

The workplan now holds task definitions and short outcomes; the evidence
file holds the analysis. 395 lines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:08:28 +02:00
3f3d437417 Fix a fabricated UUID suffix in CB-WP-0005 T06
Some checks failed
ci / check (push) Failing after 3s
When condensing Phase C I rewrote the task block from an earlier
truncated console line (774b1c8a...) and invented the remaining 28 hex
digits rather than looking them up. fix-consistency C-03 caught it:
'state_hub_task_id 774b1c8a... not found in DB'.

Real id fetched from the hub and substituted. Worth recording as its own
commit: this is a hand-typed identifier in a repo whose central finding
is that hand-typed values are worthless, and the only reason it did not
persist is that a gate compares the file against the database.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:07:30 +02:00
592cd23836 chore: mark CB-WP-0005 T07/T08 done; workplan closed
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:06:09 +02:00
2fe577213c CB-WP-0005 T08: attribution note — one commit, one task id
T07 and T08 shipped in one commit, so CA-08 attributed the substantive
work of both to T07 and left T08 with no bucket. task-done refused to
close it rather than invent a number, which is the contract working as
designed. Recorded in the workplan.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:06:02 +02:00
fd19f4e878 CB-WP-0005 T07/T08: control loop, retrospective, InnerLoop v1.4
T07 — CB-EV-0004. 70 responses, $16.03.

Test 1 MET: 1 spec -> 2, 58 rules -> 76, 1 source file -> 10, and the new
denominator came in at 83% naming K10/K14/K18. The stated failure mode — a
widened denominator reporting the same percentage — did not occur.

Test 2 UNMET: M-D1-MUT 4 of 14 against >=10, and wrong about what as well
as how much. The diagnosis was three absent kernel rules; the measurement
found eight rows with no instrument at all.

Test 3: quality held, and widening surfaced far more than the seven known
defects — six further uninstrumented rows, HDN #7 (rule-coverage's
self-test green while the tool was broken), a stale $248.46 invisible to
facts-check because it was untagged, and a fifth error class.

The clean test CB-WP-0004 was owed is now run, on a pass that used the
tools without building them. The two categories whose tools removed the
manual path are at 0 turns two passes on; the two that merely offered a
better option are now the entire mechanical cost of a pass. Absolute
mechanical cost fell $51.76 -> $6.13. The mechanism holds.

Reported because nothing else would: SH-3 batching is 0.0% this pass — 67
tool calls across 67 responses — against a 20% target, and mean context
315,170 against 200,000. SessionShape has stated these since CB-WP-0003
and none has ever been enforced.

T08 — the retrospective answers its question: yes, a weak mutation is the
new grep, and it is worse, because it fails in the opposite direction.
mutation-check's first run produced two SURVIVED verdicts and both were
the author's own no-op mutations.

The fifth error class: false accusation. HDN, TA, SSB and DFD all
under-report — a real problem passes. FA over-reports: it publishes the
claim that working code is broken, sends the next pass to fix something
that is not broken, and is more credible than the truth because it arrives
with a measurement attached. Thirteen instances, five classes, six passes,
and the newest class is one that hardening created.

One correction to CB-WP-0004 T06: "a gate only pays if it removes the
manual path" is a predictor of whether a gate saves money, not a criterion
for whether it is worth having. mutation-check fails that test and
produced the most valuable findings of the pass.

InnerLoop v1.4: where a claim rests on numbers, the adversarial reviewer
must read the assertion behind each quoted number and mutate it.
Re-running the command that prints a number is not verification of that
number. Second verification step to inherit the author's blindness; both
fixes replace re-derivation with adversarial execution.

CB-WP-0005 status -> done, 5 of 8 tasks, 3 cancelled into CB-WP-0006.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 18:05:31 +02:00
e2a2957b3a Fix loadability, and put the deferred analysis where the work is
Some checks failed
ci / check (push) Failing after 3s
loop-lint failed: CB-WP-0005 was 401 lines against a ~400 limit, one over,
after the status-vocabulary note. Pushed before checking — my error; the
gate caught it on the next run.

The fix is not a trim. The gate exposed a circular reference I had
created: CB-WP-0005's cancelled tasks held the full analysis while
CB-WP-0006 pointed back at them for detail, so the live workplan deferred
to a cancelled one. The analysis now lives in CB-WP-0006 Phase B next to
the work, and CB-WP-0005 keeps a forward pointer per task. One copy,
single source of fact, and the reference points forward.

CB-WP-0006 T05 and T06 gain the detail that moved: K9's mutation proof and
what its acceptance property actually is, K11's detection clause and
budget attribution, and the D2 correction the reviewer forced — the bundle
is not "a directory of four files" but a change to the runner's data flow,
because scenario.rs creates an EventLog, appends to it and never reads it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 17:58:36 +02:00
56c0c34d2c Sync hub IDs for CB-WP-0006; T04-T06 cancel not deferred
Some checks failed
ci / check (push) Failing after 3s
The hub's task status vocabulary is wait|todo|progress|done|cancel, so
'deferred' was rejected by fix-consistency. 'cancel' is the accurate one:
these task records are superseded and equivalents live in CB-WP-0006
T05-T07. The work is deferred, not abandoned, and the workplan says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 17:56:55 +02:00
74d9b8ef73 CB-WP-0006: instrument the acceptance table, then implement
Some checks failed
ci / check (push) Failing after 3s
Scoped to what CB-WP-0005 T02 measured rather than what CB-RES-0004
predicted. The survey diagnosed three absent kernel rules; M-D1-MUT found
that 8 of 14 acceptance rows have no instrument behind them at all.

Phase A instruments the unmeasured rows, starting with AM-6 — nothing in
the workspace compares any number to 100,000 events/s, the project's
headline throughput claim, reported as "met, 16.5x". Phase B carries the
kernel work deferred unstarted from CB-WP-0005 Phase C. That order is the
point: the previous pass built a fix before its instrument existed and
the instrument then contradicted the fix's premise.

T01 names its own trap up front: a timing assertion in CI is flaky by
nature and the usual response is to loosen it until it never fires, which
would reproduce exactly the defect being fixed. The threshold is pinned
far below measured, states its headroom in the failure message, and
loosening requires an ADR.

T04 notes an incentive worth blocking early: ADR-0005 predicts own-source
growth from Phase B, so AM-4c will move against us. Setting its threshold
after seeing that movement is the retarget InnerLoop §Step 4 forbids, so
it is set before Phase B or not at all.

T09 asks the question this pass is positioned to answer: M-D1-MUT does
not remove a manual path — writing a weak mutation is exactly as easy as
writing a strong one and the harness cannot tell them apart. CB-WP-0005
produced two no-op mutations on its first attempt, so the failure mode is
demonstrated. And it fails in the dangerous direction: a weak mutation
does not miss a defect, it indicts working code.

CB-WP-0005 T04-T06 are marked deferred rather than deleted, with their
analysis intact, so the trail from prediction to contradiction to re-plan
stays readable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 17:55:46 +02:00
ed91ec198e chore: mark CB-WP-0005 T03 done (measured: 7 responses, $1.49, opus-5)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 17:54:41 +02:00
145611e3b6 CB-WP-0005 T03: correct the record, and defer Phase C
Four verdicts in evidence/CB-EV-0001 corrected in place with a dated
note, per ADR-0005 §4: AM-7 replay split (timing met, hash-identical
withdrawn), AM-10 withdrawn as written and restated as AM-10' (the K6
determinism lint it actually measured), AM-11 downgraded to unmet, and
AM-1b added to the scoreboard it was missing from.

The scoreboard gains an Enforced column carrying M-D1-MUT, because a row
can be measured and still enforce nothing and the table had no way to say
so. AM-6 now reads "met, 16.5x" alongside "not enforced — nothing
compares any number to 100,000".

A fifth correction surfaced that ADR-0005 did not list: AM-12 still read
$248.46, the figure CB-WP-0002 disproved and corrected to $93.15 four
workplans ago. It was stale in the evidence file ever since — untagged,
and therefore invisible to facts-check. Now tagged. A duplicated-fact
instance that survived the gate built to catch duplicated facts, because
that gate only checks copies that opted in. Recorded for T07.

GameKernel §5 carries the AM-10 withdrawal and AM-11 downgrade inline so
a reader of the spec cannot reach the old claim.

Phase C is deferred before starting, per the stop condition T02 wrote and
the maintainer's decision. It is scoped to five rules; the measurement
says eight acceptance rows have no instrument at all. Building it as
written would proceed on a diagnosis the instrument had just
contradicted. T04-T06 stay in the file with their analysis intact and
move to CB-WP-0006.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 17:54:29 +02:00
4e8d89f482 chore: mark CB-WP-0005 T02 done
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 17:27:30 +02:00
e25bac3af3 CB-WP-0005 T02: M-D1-MUT — 4 of 14 acceptance rows are enforced
For each acceptance row in GameKernel §5, invert the property and require
the verifying command to go red. adapted:mutation-testing, with the
denominator changed from source lines to acceptance rows.

  M-D1-MUT: 4/14 rows enforced
    PARTIAL       2   (AM-7, AM-8 — some clauses live, some inert)
    unmutatable   8   (no property to invert, reason stated per row)
    SURVIVED      0

Two corrections to our own numbers. There are 14 rows, not the twelve
ADR-0005 and CB-WP-0005 both asserted — AM-4 splits into a/b/c. And the
prediction of 9-of-12 (75%) becomes >=10 of 14; measured 4 (29%), badly
unmet. No target moved in this commit.

The second correction matters more. My first run reported two SURVIVED
rows and both were my own no-op mutations: `pub struct NullRng;` ->
`pub struct NullRng {}` is semantically identical, and renaming
max_age_days does nothing because CA-17 reads it with a default of 90.
Both would have been published as "this row asserts nothing" — a false
accusation against code that is fine. Replaced with real inversions (a
per-construction counter in the ChaCha seed; reverting AC-9's output
resolution to the first-wins bug it was fixed for), after which both go
red. T08 asks whether writing a weak mutation is the new grep. It is,
demonstrably, on the first attempt.

The finding is larger than the workplan assumed. 8 of 14 rows are
unmutatable — AM-2, AM-3, AM-4c, AM-5, AM-6, AM-9, AM-10, AM-11 have no
instrument at all. AM-6 is the sharpest: nothing in the workspace
compares any number to 100,000 events/s, the headline throughput claim.
The problem is not three unimplemented rules, it is that more than half
the acceptance table has nothing behind it.

Harness controls: a stale find-string reports HARNESS-BROKEN rather than
scoring the baseline as the mutant; a red baseline reports inconclusive
rather than red; the tree is restored in a finally and the restoration is
verified. Not in `make all` — it rebuilds the workspace once per row.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 17:27:23 +02:00
235412423a chore: mark CB-WP-0005 T01 done (measured: 29 responses, $7.42, opus-5)
Some checks failed
ci / check (push) Failing after 4s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 16:54:25 +02:00
bb35fcb168 CB-WP-0005 T01: spec->code link over every numbered spec and every crate
AGGREGATE becomes a list of source roots and rule patterns become
per-spec, so the link runs over every numbered spec x every crate rather
than GroundRules.md x games/ground/src/lib.rs.

The prediction held on the first run:

  AM-1b kernel spec->code link: 15/18 (83%) across 10 source files
    unlinked: K10 K14 K18

Kernel rules are link-only by design, and the output says so: they are
kernel invariants with no aggregate, setup preset or command vocabulary,
so scenarios/kernel/*.yaml with covers: [K11] would be a tag in a
directory the runner cannot dispatch. Claiming scenario coverage for them
is the inflation this gate exists to prevent.

Per ADR-0005 §5 the kernel arm reports without feeding the exit code
until 2026-08-31, then binds — the date in the tool, not in prose, with
days remaining printed every run, because open-ended "gate it later" is
how AM-4's targets went unratified for four workplans. The self-test
asserts the gate returns 0 before that date and 2 after.

The zero-rules positive control is replicated on the new denominator: a
kernel regex that stops matching aborts rather than printing 0/0 as
though it were 100%.

The self-test passed while the tool was completely broken. A print(
inside say() became say(), so every real `make coverage` died with
RecursionError while --self-test reported all-ok — it only ever called
kernel_arm(quiet=True) and never executed the reporting path. The control
named the behaviour and did not assert it, which is precisely what this
workplan is about. Fixed by exercising the loud path and asserting it
prints, then verified by re-breaking say() and confirming both new checks
go red. Seventh instance of the harness-does-nothing shape, in the tool
written to find that shape.

Also caught by its own gate: a self-test label that printed "0 K-ids"
beside a passing ">5" assertion, because the detail string rebuilt the
pattern with different escaping. A label that contradicts its own check
is worse than no label.

k_rules, k_linked and k_unlinked are registered facts under facts-check.
A limit of that checker is recorded rather than patched: it is
line-based, so a tagged value that prose-wraps fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 16:54:07 +02:00
bd4423a8e4 Sync hub IDs for CB-WP-0005 and work-record index
Some checks failed
ci / check (push) Failing after 4s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 14:25:54 +02:00
1bbbfc846f ADR-0005 + CB-WP-0005: count assertions, not names
Some checks failed
ci / check (push) Failing after 3s
Decides the response to CB-RES-0004 and its adversarial review.

Decision 1 — a third coverage metric, M-D1-MUT: one mutation per
acceptance row, and the suite must go red. adapted:mutation-testing, with
the denominator deliberately changed from source lines to acceptance
rows, because the failure mode here is not an untested branch but a
headline number backed by nothing. A row for which no mutation can be
written is recorded unmutatable WITH THE REASON and counts against the
metric — a row nobody can invert asserts nothing.

Decision 2 — reimplement the log framing rather than assimilate it. This
is the deliberate exception to "own the semantics, assimilate the
implementation": that rule exists so we do not reimplement HARD things.
SHA-256 and ChaCha cost 12 crates and are kept. A length prefix is not in
that category. Budget attribution corrected per the review: the durable
log is shipped runtime (AM-4a, 1.5% headroom), the bundle writer is
dev-only behind the scenarios feature (AM-4b, 9.4%). The survey wanted it
both ways without saying so.

Decision 3 — a LogStore port with a real shared conformance suite, and
the same shape retro-fitted to KernelRng. M-D4-SWAP is a bool over "the
same conformance suite" and no suite exists, so AM-11 was never earned.

Decision 4 — correct three committed verdicts in evidence/CB-EV-0001:
AM-7 split (timing met, hash-identical withdrawn), AM-10 withdrawn as
written (no cb-*-api crate; a K6 determinism lint was reported under a D4
leak row), AM-11 downgraded to unmet. AM-1b is added to the scoreboard it
was missing from while the flattering half of the same command's output
was carried.

Decision 5 — widen the name check over every numbered spec and every
crate, reporting outside the exit code until 2026-08-31, with the date in
the tool rather than in prose. Open-ended "gate it later" is how AM-4's
targets went unratified for four workplans.

Decision 6 — replay is proven by re-execution in a fresh process, with
the reviewer's four controls adopted verbatim, including the two I had
not thought of: truncate-by-one-byte and mutated-seed.

Stated worse, not only better: own source grows and AM-4c moves against
us; CI gets slower by one rebuild per acceptance row. Both accepted — the
alternative is a smaller, faster codebase whose acceptance numbers mean
nothing.

CB-WP-0005 executes it in 8 tasks across four phases. The M-D1-MUT
prediction is 9 of 12 and is beatable in both directions: 12 of 12
refutes CB-RES-0004 outright, 3 of 12 means the pass is under-scoped and
must stop and re-plan rather than proceed to Phase C.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 14:25:26 +02:00
923c2038be CB-RES-0004: the instruments count names, not assertions
Tier-L survey plus the full review trail. v1 was REJECTED by adversarial
review with four blocking findings; this is v2, and the finding below is
the reviewer's as much as mine.

The root cause is neither v1's story ("coverage scope is too narrow" —
true but shallow) nor the reviewer's cheaper one ("the link check points
at one file" — true but catches 3 of 7). It is:

  Every coverage instrument in this project counts NAMES. None counts
  ASSERTIONS. M-D1-COV counts covers: tags; M-D1-LNK counts rule-ID
  strings. Both answer "is this rule mentioned?" and neither answers
  "does anything fail if this rule is violated?"

Four of seven defects found this pass are named in the source and inert,
so no name-based check finds them. This subsumes the oldest error class:
all five prior harness-does-nothing instances have the same shape.

Two are mutation-proven, because the reviewer set that bar for findings
they had only read:

  AM-7 — folding a 100k-event log from fresh(999) instead of fresh(42),
  an unrelated genesis state, leaves the test green. The hash reaches
  only a println!; the sole assertion is on elapsed time. And the log is
  built across games seeded 42,43,44... so it could not be asserted as
  written. evidence/CB-EV-0001 reports "AM-7 replay met, 2,290x" —
  unearned on the hash-identical clause. HDN instance #6.

  K9 — making Snapshot::take discard its EventSeq and store 0, which
  destroys the half of K9 that says "+ the EventId it includes", leaves
  the test green. On the acceptance criterion GameKernel §2.4 is zero of
  three, not two of three.

Also found: K10, K14, K18 named nowhere in source; AM-10 vacuous (a K6
determinism lint reported under a D4 leak row, over a cb-*-api crate that
does not exist); AM-11's conformance suite does not exist; AM-1b measured
and omitted from the evidence scoreboard.

All twelve challenge findings conceded, none defended on its facts. v1's
prediction was rejected as unfalsifiable AND already determined; the
replacement names its refuting outcome and is beatable in both
directions.

Chaos d4=1, no override. Declaration 1 of the 12-declaration window.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 14:19:30 +02:00
59b73c3e3d chore: regenerate WORK-RECORDS.md (fix-consistency C-33)
Some checks failed
ci / check (push) Failing after 3s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:29:17 +02:00
c15d627aa0 chore: mark T03-T06 done (measured: $0.79 + $2.14 + $1.05 + $0.44, opus-5)
Some checks failed
ci / check (push) Failing after 4s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:28:41 +02:00
7b4d9e359f CB-WP-0004 T06: retrospective — what actually predicted the outcome
The question was whether converting agent work to deterministic tooling
recovers capacity or the work reappears elsewhere. Neither, cleanly:
tooling recovers capacity where it removes the possibility of doing the
work by hand, and recovers nothing where it merely offers a better
option.

Sorting the five candidates by that test reproduces the results exactly.
env-setup and task-done left no manual path — env-test fails the build if
the friction returns, and task-done refuses to emit an estimate, so the
old way means knowingly writing fiction. Both met. facts-check and
`make status` left grep and heredocs one keystroke away. Both missed.

This is the principle ADR-0003 already used to reject explicit session
markers — nothing that depends on discipline gets to be the primary
index. It governs tooling economics too, and CB-RES-0003 did not apply
it.

Stated confidence sorted the extremes and said nothing in the middle,
where every judgement call lives: both high met, both medium missed, and
the single low also met, so it was miscalibrated at that end too. Future
reviews state the mechanism instead — after this fix, is the manual path
impossible, absurd, or merely inferior? — and only the first two may
carry a cost prediction.

The confound is symmetric and does not explain the split: building
repo.py and task-done.py also required inspection and patching, and those
categories still went to zero. It inflates the size of the miss, not its
location.

The most valuable output is not the 6 points of 38 recovered. It is that
the loop made a falsifiable quantitative forecast, was wrong by 4-5x, and
could say exactly which parts were wrong and why.

CB-WP-0004 status -> done.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:28:35 +02:00
7a374d37d2 CB-WP-0004 T05: control loop — 6 points recovered, not 25-30
cb-cost gains --since, so the baseline (--pin 578dcbe, 662 responses,
$135.60) and this pass (--since 578dcbe, 83 responses, $7.82) are two
disjoint windows over the same transcripts. Every verdict is on share of
pass, since the windows differ 17x in size.

Test 1 — did mechanical turns disappear? Partly. Mechanical share fell
38.2% -> 32.4%. Six points against a predicted 25-30; reported unmet, and
no target was moved in this commit.

  environment setup   11.5% -> 0.6%   (85 turns -> 1)   met, decisively
  hub + workplan      8.5%  -> 0.0%   (46 turns -> 0)   met
  text patching       10.4% -> 9.6%                     not met
  orientation         5.1%  -> 21.2%                    not met, worse

The confound is stated before any defence of the numbers: this is the
pass that built the tools, and the two categories that missed are exactly
the two whose tools were under construction. The clean test is the next
pass, and it is carried forward rather than waived.

Test 2 — did the work relocate? Not into prose. Output tokens per
response fell 896 -> 681. Output's rising share of cost is a shrinking
denominator, not more writing. Cost per response halved and the evidence
refuses to claim it: that is compaction (mean context 232,982 ->
117,822), and attributing it to tooling would repeat CB-WP-0002's
original error in a new direction.

Test 3 — did quality hold? Yes, recorded as explicit judgment. make all
green with two gates that did not exist before, and four findings
surfaced this pass, three caught by controls written this pass — one of
them a 5.2x attribution error that would have reached the hub as a
measured number.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:27:09 +02:00
53c460c992 CB-WP-0004 T04: fact registry and make facts-check — DFD gets a gate
Duplicated-fact drift is the fourth error class and the only one with no
executable rule. No positive control catches it (both copies are
internally consistent) and re-derivation does not either (the copy
reproduces whatever it was copied from). It is caught only by reading a
copy against its source, which nothing in the loop required.

facts.toml holds 15 facts and is GENERATED by `make facts-gen` from
cb-cost, dep-weight and rule-coverage. The trap this task named — a
hand-maintained registry that becomes another drifting copy — is closed
by facts-check re-running the instruments and failing when the committed
registry disagrees with them. A stale registry cannot certify stale
artifacts.

An artifact quoting a fact tags it: **$93.15** <!-- fact --> with the key.
17 occurrences across 5 artifacts are now checked.

Falsified before being believed: changing CostAccounting.md line 158 from
$93.15 to $92.87 — the exact historical drift — produced exit 1 naming
the file, the line and the expected value. Tested against the class it
exists to catch, on a real artifact, not only in its self-test.

It then caught a live tag inside its own documentation example in
InnerLoop.md within the hour. Third time a gate has failed on its own
pass's work.

What it does not close is stated rather than implied: 22 untagged literal
copies remain and are reported, not failed. Tagging is opt-in, a number
can legitimately recur, and a gate that fires on coincidence gets routed
around. Naming the uncovered surface beats claiming the class is closed.

InnerLoop single-source-of-fact moves from prose to executable — v1.3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:24:39 +02:00
6281cd546e CB-WP-0004 T03: make status — one-shot orientation
49 turns and $6.87 per CB-RES-0003 went to grep/ls/wc answering "what is
the state of this repo". tools/status.py answers it in 22 lines: commit
and working-tree state, every workplan with task counts, the next todo
task, spend since the last commit against CB-01/CB-02, and the three fast
gates.

Two constraints are deliberate. It does not build — a status command that
takes two minutes gets replaced by `ls` within a day. And it states its
own limit in the output ("slow gates not run here"), so a green status
cannot be misread as a green `make all`; the self-test asserts that line
is present, and that the whole report stays under 40 lines.

The positive control is the refusal to be confidently empty: a parser
that found zero workplans and zero tasks would print a clean, wrong
picture, which is worse than the greps it replaces. It also caught a
dot-all regex that returned twenty paragraphs of the preceding task's
prose as the "task heading".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:20:27 +02:00
3e1395dd60 chore: mark T01/T02 done (measured: $2.33 + $1.68, 46 responses, opus-5)
Both hub events carry measured tokens for the first time in this repo;
--hub-only backfills T01, which was closed in the file before the tool
that closes it existed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:18:34 +02:00
b52a9ec88a CB-WP-0004 T02: make task-done — close a task on measured numbers
Replaces the three hand-done steps of a task close (46 turns, $11.52 per
CB-RES-0003): the heredoc flipping status in the workplan file, the
hand-written hub call, and the hand-typed token counts.

The third is the reason this task exists. Every update_task_status this
repo produced carried estimated tokens_in/tokens_out — in a project whose
central finding is that estimated token counts are worthless. task-done
reads the measured figure from the transcripts, or refuses; there is no
path through it that emits an estimate.

cb-cost gains by_task_detail: cost, response count, model histogram and
token components per task. task-done imports cb-cost rather than parsing
its printed table, so the hub figure is not a copy that can drift from
its source.

The positive control found a real defect before the tool ran once.
Attribution keyed on a bare T\d\d from the commit subject, so CB-WP-0002
T01, CB-WP-0003 T01 and CB-WP-0004 T01 shared a bucket: the self-test
reported $12.10 for "T01" where the qualified figure is $2.33. That 5.2x
overstatement would have been pushed to the hub as a *measured* number —
the same fiction in a new form. task_label() now keys qualified subjects
on the full id and leaves unqualified ones bare rather than
retro-assigning them to a workplan. The pinned $93.15 benchmark is
unchanged, so historical attribution was not disturbed.

Fourth instance of trusted arithmetic: a number believed because a
program produced it rather than a hand.

Refusals, all exercised by --self-test: unknown id, typo'd id,
already-done task, missing state_hub_task_id, no measured spend, and a
status flip that produced no change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:17:51 +02:00
3f1dbac164 CB-WP-0004 T01: fix environment friction at the root
CB-RES-0003 measured 84 turns and $15.33 — the largest mechanical
category — spent prefixing commands with `cd` and
`export PATH="$HOME/.cargo/bin:$PATH"`. Both causes are now fixed once
instead of per-leaf.

tools/repo.py resolves the repo root from __file__ and cargo from PATH
then the standard rustup locations. Every tool imports ROOT from it, so
the repo path is stated once rather than redefined in four files —
single source of fact, the rule DFD earned in InnerLoop v1.2.
rule-coverage and dep-weight now call enter_root(), which is why their
relative paths did not need rewriting one by one.

The Makefile derives REPO from MAKEFILE_LIST and resolves CARGO the same
way, so `make -C <repo> <target>` works from any directory with no
prefix.

make env-test is the positive control, and is in `make all`: every tool
runs from / with PATH=/usr/bin:/bin. Without it this fix could regress
silently and invalidate T05's measurement — the whole point of the
control loop.

dep-weight's "cargo not on PATH" error is kept rather than deleted. It
should now be unreachable, and --self-test asserts cargo_bin() resolves
unaided; a control that never fires is cheaper than a regression.

loop-lint failed on repo.py on its first run — a reporting tool with a
positive control but no --self-test entry point. Second time the gate
has caught work from its own pass within the hour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:13:52 +02:00
578dcbea78 Sync hub IDs for CB-WP-0004; drop duplicate frontmatter key
Some checks failed
ci / check (push) Failing after 3s
fix-consistency appended state_hub_workstream_id rather than replacing
the empty one, leaving a duplicate YAML key.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:50:23 +02:00
7e21df378a CB-RES-0003 + CB-WP-0004: 38% of pass cost is mechanical turns
Some checks failed
ci / check (push) Failing after 4s
Review of where token-priced turns did work a deterministic tool could
do. Method: classify every turn in both transcripts by the tool calls it
made. The classifier is committed in tools/cb-cost.py and emitted by
`make cost-mix`, so the baseline is reproducible and the same command
can later falsify the predictions.

  mech environment setup       84 turns  $15.33
  mech ad-hoc text patching    75 turns  $13.86
       git                     37 turns  $13.85
  mech hub task status         25 turns  $ 7.46
  mech orientation / inspect   49 turns  $ 6.87
       hub other               32 turns  $ 6.22
  mech ad-hoc transcript       39 turns  $ 4.56
  mech workplan status edit    21 turns  $ 4.06
  MECHANICAL (dedup)          290 turns  $51.26  = 38% of pass

Largest category is `cd` and `export PATH` -- pure friction, and
dep-weight.py already patched it at the leaf, which is evidence it was
noticed and fixed in the wrong place. Second is heredocs string-patching
markdown, which is also the mechanism behind duplicated-fact drift, the
error class InnerLoop v1.2 names and cannot gate.

Explicitly NOT automated: git (37 turns, $13.85) is mostly commit
message authorship -- the highest-output turns in the corpus and the
project's reasoning record. Automating it would save money and destroy
what makes corrections cheap.

CB-WP-0004 implements five candidates and predicts $33-41 recovery
(25-30%), below the 38% measured share on purpose: some inspection and
patching is genuinely exploratory.

The control loop is the deliverable, not a formality. T05 tests three
things and must report all: did mechanical turns disappear, did they
RELOCATE into prose, and did quality hold. If mechanical turns fall and
prose rises by as much, the saving is zero and that is the result to
publish.

Also a self-indictment worth recording: every hub update_task_status in
this project carried hand-typed token estimates, in a repo whose central
finding is that estimated token counts are worthless. T02 fixes it by
reading measured values from cb-cost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:49:57 +02:00