Skip to content

Release 0.7.0 — the behaviour claim is measured - #482

Merged
MongLong0214 merged 137 commits into
mainfrom
dev
Aug 8, 2026
Merged

Release 0.7.0 — the behaviour claim is measured#482
MongLong0214 merged 137 commits into
mainfrom
dev

Conversation

@MongLong0214

Copy link
Copy Markdown
Owner

Promotion of dev into main for v0.7.0. 132 commits.

This is the only PR where promotion-range-lint runs — it validates every record in the promoted range.

The headline

arm re-proposed a ruled-out approach
the agent alone 18.8% (109/579)
with CommitLore 2.8% (16/580)

1,160 registered runs. Three things about how it was produced matter more than the number:

  • The threshold was registered before the run, and the analyser refuses to compute a table until every registered shard is present — it did refuse, by name, twice.
  • The preregistration predicted a smaller effect, gave three reasons and stated its probabilities. All three were conservative. It was wrong, and it is still in the repository.
  • The control arm truncated more (28.5% vs 21.2%), and truncation suppresses re-proposal — the artefact removes control chances rather than manufacturing treatment ones.

Method, exclusions, per-arm truncation and what it does not cover: bench/VERDICT-M5.md.

[directive] became reachable — after the run that measured [claim]

No installed surface had ever passed --trusted-author, so grading failed closed and every record every user had ever received was [claim], while the injected legend advertised a tier nobody could reach (#415). init now records the installing user's git identity; another author's records still grade [claim], so the property that stops a contributor's commit from instructing someone else's agent is untouched.

M5 therefore measures the weaker of the two tiers. That is stated beside the number in the README, the changelog and the verdict.

Also in this release

Verification at this exact tree

103 test files, 2,265 cases pass, 1 skipped
typecheck clean · dist/ has no diff · version 0.7.0 in both manifests
no v0.6.0 pin remains in any of the four READMEs
scripts/check-readme-numbers.mjs exits 0

dist/ is byte-identical to the tree every M5 row was produced against — the CLI reads its version at runtime.

Known, stated rather than hidden

The README's behaviour claim rests on M5 while the generated numbers block beneath it still publishes M4 (#480). Repointing README_SOURCES needs a status note written with the care the M4 one took, not an edit squeezed between a CI failure and a tag.

After the merge

Between this merge and the tag push, every README install one-liner points at a tag that does not exist. The tag goes on the merge commit immediately, and RELEASE-GATE.md §4's seven install checks run against a fresh clone before the release is called done.

MongLong0214 and others added 30 commits August 1, 2026 20:42
The README carried two numbers in 380 lines: the guard's 44.8% precision and
22.0% recall, and the 7.4 seconds to index 768 commits. The first of those is a
disclosure about a secondary, experimental feature -- so the only performance
figure a first-time reader met was the one saying the advisory mostly misses.

The delivery measurement is the answer to the question this product exists for,
and it lived only in bench/ and docs/evidence.md. Moving it up is not a new
claim; every figure in the table is already in docs/evidence.md and derives from
the committed run.

The paragraph under it is the point of the change. A delivery number is a
recall metric, and recall metrics have a known failure mode in exactly this
setting: SWE-bench measured BM25 recall climbing from 29.58 to 51.06 across its
context budgets and reported that performance drops anyway, because models are
ineffective at localizing in a larger context. Publishing 81.7% without that
sentence beside it would be doing what this market does. Publishing it with the
sentence is the difference between a measurement and a marketing figure, and the
citation is the field's founding benchmark rather than our own argument.

The quote is taken from the v3 PDF, not from a summary of it. An earlier draft
of this change carried "performance can still drop, as shown in Table 4"; the
paper says "performance drops, as shown in Table 2".

Record-Id: r-surfacedeliv
Limit: every figure in the table is measured on this repository measuring itself, which is the weakest part of the evidence and is stated in the paragraph rather than left for a reader to discover
Ruled-out: Leading with the token figures | 511,412 against 673,134 is the smaller half of the result, and a token-reduction headline is the claim this market makes without a harness behind it
Ruled-out: Citing the resolve-rate numbers from the same table | they were not extracted from the PDF in this session, and the paper's own sentence carries the argument without them
Certainty: firm
Blast: local
Undo: easy
Verified: readme, readme-order, readme-numbers, readme-positioning and compatibility-matrix pass at 83 across all four files; check-readme-numbers.mjs exit 0 with the BENCH block byte-identical at 39 lines and 2262 bytes; spec/verify.sh OK at 26 fixtures; the recall figures 29.58 and 51.06 and the quoted sentence were read out of arXiv:2310.06770v3 directly
Unverified: how the table reads to someone who has not already accepted that delivery and effect are different things
Put the delivery result where a reader meets it
M1 ran the right experiment at the wrong size: 5/30 against 7/30, p = 0.7480,
and 5.1% power to detect the difference it observed. Its own verdict says what
that means -- an experiment with a 5% chance of seeing its own effect has not
tested the hypothesis. M5 changes n and nothing else.

The size is registered here rather than chosen later, and so is the basis for it.
A pilot run today measured a 33.3% control rate over three tasks, and this
document deliberately does not use it: that sample included the strongest task in
the set, and planning from it would size the run too small. The planning rate is
M1's 23.3%, measured over all ten tasks. Where a convenient number and a
conservative one disagree before a run, the run takes the conservative one.

The two conditions that invalidated earlier matrices are written in as
preconditions rather than hoped for. M4 recorded no per-run treatment exposure
and was withdrawn entirely; M5 requires accepted_records > 0 on every treatment
row and 0 on every control row, states what happens to a row that fails, and
declares the run compromised rather than analysable past a 5% exclusion rate. M4
also records its model as "not recorded"; the harness now refuses a non-simulated
run without --model, so that cannot recur.

The stopping rule is the part worth reading. 1,160 runs is roughly 77 hours, and
a long run invites a look partway through. All rows complete before any table is
computed -- no interim analysis, no early stop, no extension after seeing a
result. Sharding is for restartability and carries no analytical meaning.

Record-Id: r-m5prereg
Limit: ten synthetic tasks, one model and one harness; this bounds an effect on this instrument and is not evidence about coding agents in general
Ruled-out: Planning from the pilot's 33.3% control rate | it covered three tasks including the strongest one, so it would have sized the run below what the full task set supports
Ruled-out: Powering only a halving of the rate | 166 per arm fits in a fraction of the time, but a null at that size would not bound the difference M1 actually observed
Ruled-out: Interim analysis with an early stop | the run is long enough to make peeking tempting, which is exactly why the rule is fixed before it starts
Certainty: firm
Blast: local
Undo: easy
Verified: n computed from the two-proportion normal approximation at alpha 0.05 two-sided and power 0.80, cross-checked against the power table in bench/VERDICT-M1.md; the pilot recorded guard_exposure complete on 12 of 12 rows with accepted_records non-zero on all six treatment rows and zero on all six control rows, so the exposure precondition is known to be satisfiable
Unverified: whether the harness sustains a 77-hour run without drift; the pilot ran 48 minutes
Fix the M5 design before there is a number to fit it to
docs/evidence.md has carried break-even under "not measured" with a one-line
reason: it would need a per-turn ledger of provider-reported usage and an
observed cost for work spent on a rejected alternative. That sentence is about
the break-even #138 withdrew, whose denominator was the value of a prevented
re-proposal -- a behavioural quantity this project has never observed and had
estimated from unrelated runs.

The denominator does not have to be that. decision_delivery already measured the
delivered-token difference between two information routes, on the same corpus,
in the same unit. A ratio built on that has a measured numerator and a measured
denominator, which is exactly what the withdrawn one did not.

So this registers the method and the refusals first, the way
bench/DECISION-DELIVERY.md was done, and section 9 is empty. What is registered
matters more than what it will report: the write side counts only the two terms
obtainable with no model call -- the generated harvest prompt's scaffold and the
staged diff -- so it is a floor, and every break-even from it is a lower bound
rather than an estimate. The two terms it omits are named on the result row
itself, so a consumer of the JSONL cannot mistake the floor for a total.

The five reduction pairs are fixed here rather than after the run. Two of them
are unfavourable by construction and the fifth has no number at all: a reduction
against a route that reads no history is a division by zero, and the honest
report of that is "undefined", not a large percentage in either direction.

The harness reads the read side from a committed delivery run instead of
remeasuring it. Remeasuring would move the corpus -- the record count grows with
the commits that add this file -- and leave one ratio citing two corpora for its
two halves.

Record-Id: r-tokenledger
Limit: both sides are byte-derived token proxies under CHARS_PER_TOKEN=4, so the ratio cancels a uniform tokenizer error and does not cancel a differential one between prose and diffs
Ruled-out: Remeasuring decision_delivery inside this harness | it would price the read side on a corpus that already includes the commits adding this measurement, and docs/evidence.md would cite two corpora for one ratio
Ruled-out: Reporting a single write-side accounting | issue #138 established that a break-even which does not say what it counted is underspecified, so the diff term is reported both charged and free
Ruled-out: Estimating the drafting turn from the 112 recorded runs | those rows carry a session total from --output-format json with no per-turn attribution, so any split across turns would be invented
Certainty: firm
Blast: module
Undo: easy
Verified: npx vitest run passes 2041 tests across 80 files; tsc -p tsconfig.json --noEmit and tsc -p bench/tsconfig.json --noEmit both exit 0; check-readme-numbers.mjs exit 0 with the BENCH block byte-identical at 39 lines and 2262 bytes; the 26 token-ledger tests cover the merge-borne and root-borne exclusions on a synthetic history and the network scan against a planted node:https import
Unverified: whether the reconstructed diff matches the bytes `git diff --cached` printed on the day each record was written; the reconstruction rule is stated in the method document rather than checked
A trial run at HEAD was thrown away before it was committed. It priced 348
captures at HEAD while its read side came from a delivery run three records
earlier, so the numerator and the denominator of every ratio described different
repositories. The method document objects to precisely that on the other side of
the same argument -- section 4 declines to remeasure the read side because doing
so would leave one ratio citing two corpora -- and then section 3 walked into it
from the other direction.

The history is now the harness_commit the delivery run recorded, read off the
same file the read side comes from. A commit that no longer resolves stops the
run rather than falling back to HEAD: ADR-0018's digest fallback establishes
that harness code is identical, which is not the same as having the history
back, and a ledger measured over a different history would still populate every
field and still look plausible.

The deviation is recorded in the section it changes, dated, with its reason,
before the run that reports the figures -- not folded into the original text.

Record-Id: r-ledgerpin
Limit: the ancestry check proves this checkout continues the history the read side was measured on; it does not prove nothing between the two commits changed how a diff reconstructs
Ruled-out: Falling back to HEAD when the recorded commit is unresolvable | it turns a loud provenance failure into a quiet corpus mismatch, which is the class of defect ADR-0018 exists to keep loud
Ruled-out: Keeping the HEAD run beside a pinned one as a sensitivity arm | it was never committed or quoted, so keeping it adds a second table a reader has to reconcile for a 3-record difference
Certainty: firm
Blast: module
Undo: easy
Verified: 28 token-ledger tests pass, including one asserting history_ref equals read_source.harness_commit and one asserting an unresolvable commit is refused; tsc -p bench/tsconfig.json --noEmit exits 0
Unverified: behaviour when the recorded commit resolves but is on an unrelated history -- the ancestry check refuses it, and no test constructs that repository
docs/evidence.md has carried break-even under "not measured" since the figure
#138 withdrew. What was missing there was a denominator: the old ratio divided a
measured cost by the value of a prevented re-proposal, and this project has
never observed one. The measurement runs because decision_delivery supplied a
different denominator -- a measured difference in delivered tokens between two
routes, on a corpus this repository already published.

The write side is a floor and is quoted as one. Two of its four terms are
obtainable with no model call: the generated harvest prompt's scaffold at 1,197
tokens, and each commit's staged diff, which takes a median capture to 3,537
tokens and the mean to 10,064. The mean is 3.8x the median because a few commits
staged generated files, so the mean is the wrong number to carry away and the
distribution is printed instead of it. Verification's zero stops being an
assertion: 14 built modules reachable from the verify entry points were scanned
for a network client and none carried one.

The corpus check is the part worth trusting. 343 single-parent captures plus 3
merge-borne records is 346, which is exactly the record_bearing_commits the
delivery run's own census counted at the same commit -- two independent walks of
one history agreeing. It also falsified something this document asserted: three
merges do carry a record, all of them "Merge dev into <branch>" integrations
documenting a conflict resolution. The count was reported so the assumption
would be checked, and it was wrong.

The finding is not the one a product would want. Against a naive whole-repository
dump the records pay for themselves after 39 reads, but nobody has 92 million
tokens of context, so that is a real number about an unreal baseline. Against
the comparator an agent actually faces -- git log on the path, truncated to the
same 800 tokens -- break-even is at least 22,326 path-scoped reads, twenty-one
full passes over every evaluated file. That is not a plausible amount of editing.
So on this corpus the token argument is the weak one and recall is what carries
the product: 81.7% against 42.0% at the same budget.

And against an agent that reads no history at all there is no break-even at any
read count, because a comparator that spends nothing offers nothing to amortize
against. That row is in the table rather than omitted from it, and its reduction
column reads undefined rather than a large negative percentage.

The reduction figures carry their denominators on the same line for the same
reason. The one worth quoting is 99.2%: the same projection with the budget
removed on both sides, recovering the same count of gold pairs, at 708.8 tokens
per read against 88,122.0. Equal in count -- the delivery row records counts and
not sets, and that caveat travels with the number.

What is still missing is named instead of estimated. The tokens a model spends
drafting a record need a model call, and the driver could not attribute one to
that turn anyway: it reads a single session-total usage object out of
--output-format json. A per-turn ledger plus a bench arm that runs capture
against each run's own transcript is what would close it.

Record-Id: r-ledgerresult
Limit: both sides are byte-derived proxies under CHARS_PER_TOKEN=4 rather than a provider tokenizer, so the ratio cancels a uniform error and not a differential one between diff text and prose
Limit: break-even in reads assumes reads land on the evaluation set the way the delivery run's per-path average describes, and real editing concentrates on a few files
Ruled-out: Leading with the 99.4% reduction against the whole-repository dump | it is the largest number in the table and its denominator is a route no agent can run, which is the figure this market publishes
Ruled-out: Omitting the code-only row because it has no percentage | a reduction table that drops the case where the product is the expensive option is the thing the method document was written to avoid
Ruled-out: Publishing a single write-side accounting | #138 established that a break-even which does not say what it counted is underspecified, so the diff term is reported both charged in full and charged at nothing
Certainty: firm
Blast: local
Undo: easy
Verified: the run is bench/results/token-ledger-20260801T122953Z.jsonl at harness commit 8665be3, harness digest 9c91253e4cb06a0c07076620ecdad01ebf7c13f9, dist digest f54cda4795ccc1083e00aa38d8637a2e6f22466ef20213fa7d127c7fd301d1d2, over history b3f5692; 343+3=346 agrees with the delivery census; npx vitest run passes; tsc -p tsconfig.json --noEmit and tsc -p bench/tsconfig.json --noEmit exit 0; spec/verify.sh OK at 26 fixtures; check-readme-numbers.mjs exit 0 with the BENCH block byte-identical at 39 lines and 2262 bytes; validate --range over the adoption range exits 0
Unverified: whether the reconstructed per-commit diff matches the bytes git diff --cached printed on the day each record was written, and whether the two routes recovering 2,047 pairs each recovered the same 2,047 -- the delivery row records counts, not sets
Measure the token ledger: what a record costs to write, what the projection saves to read
The first M5 launch omitted --task, whose default is every fixture in
bench/tasks/. That directory holds the ten reproposal-* cases this measurement
registers and ten qualification-gitseed-* cases that qualify the harness and test
no hypothesis. The run was producing all twenty, so it did not match §3's "over
all ten tasks" and would have taken twice as long to say so.

It was caught 3.6 hours in at 80 rows, during the §6 precondition check, and
restarted with the ten fixtures named explicitly. The shard script now passes
them and says in a comment why, because the failure here was a default asserting
itself through an omission rather than anything anyone decided.

The 80 rows are committed rather than deleted. A restart with no artifact behind
it is indistinguishable from a run that always looked this way, and this project
has spent a lot of credibility on the difference. They are marked not citable and
excluded from the analysis set.

Worth stating explicitly because it is the thing that makes a restart legitimate
or not: no outcome was examined first. What was read is what §6 and §7 require to
be monitored -- exposure completeness, arm-correct accepted_records, stopped_by
balance, model recorded. The 2x2 table was not computed, and the cause of the
restart has nothing to do with results.

Those rows do carry one useful fact, which is why the deviation note quotes it:
the preconditions that invalidated M4 held perfectly at 0.0% exclusion against a
5% threshold, so the design is satisfiable and the restart risks nothing but time.

Record-Id: r-m5dev1
Limit: the deviation note reports integrity figures from the discarded rows, which is defensible only because none of them is the registered outcome; a reader who disagrees should discount them entirely
Ruled-out: Letting the 20-task run finish | it produces the registered rows correctly but takes 4.3 days instead of 2.2, and leaves a permanent question about whether the extra fixtures mattered
Ruled-out: Deleting the off-design rows | the artifact is the evidence that the restart happened for the stated reason
Certainty: firm
Blast: local
Undo: easy
Verified: the restarted shard script passes the ten reproposal fixtures explicitly and its syntax was checked before launch; readme-numbers passes and check-readme-numbers.mjs exits 0 with the BENCH block byte-identical
Unverified: whether any other runner default is silently in force in this measurement -- only --task was audited
The deviation note admitted only --task had been audited, which is not a state to
leave a measurement in. The rest are checked now: --tasks resolves to bench/tasks/
correctly, --max-turns is deliberately unset so each fixture's own budget applies
as it did in M1, and --timeout-ms and --permission-mode take the same defaults M1
ran under. No second default is silently in force.

The audit did surface one number. On the registered fixtures, over-turns ran at
22.5% in the off-design sample against 15.0% in M1. A run cut off at its turn
budget had less chance to re-propose, so a false on such a row is weaker evidence
than a false on a completed one.

The temptation is to exclude those rows. That is exactly the choice this document
exists to stop: the rule is visible now only because the rate is, and M1 kept
them. §7 stands unchanged. What is added is an obligation on the verdict -- report
the over-turns share per arm beside the table -- registered while the outcome is
still unknown, which is the only time such a requirement is worth anything.

Record-Id: r-m5overturns
Limit: the 22.5% comes from 40 discarded rows, so it is an early indication of the rate rather than a measurement of it
Ruled-out: Excluding over-turns rows from the analysis set | the rule would be changed after seeing the rate, and M1 kept them, so the two runs would stop being comparable on the one axis M5 was built to vary
Ruled-out: Raising the turn budget mid-run | it changes the instrument between shards and makes the earlier rows a different measurement
Certainty: firm
Blast: local
Undo: easy
Verified: every --option in bench/runner.ts compared against the shard script's command line; over-turns share computed from t702-m1-final.jsonl (15.0% of 60 reproposal rows) and the off-design sample (22.5% of 40)
Unverified: whether the over-turns share on the registered run matches the off-design sample, which is what the verdict will report
The first screen carried three different taglines. The image said "Git remembers
what changed. CommitLore remembers why.", the heading under it said "Your agents
inherit the code. Make them inherit the judgment.", and the alt text said a
coding agent must not revive a decision the repository already reversed. The
image was left behind by a positioning change that moved the heading twice and
never reopened the SVG.

The rebuild keeps what was already right: the split composition, the warm paper
palette, and real material from this repository rather than decoration. What
changes is the headline, and what the card on the right shows. It used to show
one ruled-out alternative. It now shows an active record beside a superseded one
that is withheld, because the lifecycle filter is the part of this product no
comparable tool has, and the old card demonstrated the part several of them do.

The demo SVG gained a `<title>` and a `<desc>`, and the generator gained the
reason. `aria-label` on the root serves the README's `img` embed and nothing
else; a reader who opens the file directly had no name for it. Regenerated
rather than hand-edited, since the file says not to edit it by hand.

The bottom caption was clipped on the first attempt -- it read "hook con" at the
canvas edge -- which a rendered preview caught and a well-formedness check would
not have. Shortened until it fits.

Record-Id: r-heroconsist
Limit: the card text is 19-20 units, so at a 360-pixel mobile render it falls below the legible threshold; the headline and the alt text carry the message there, and the same content is in the Markdown below, but the card itself is decoration at that width
Ruled-out: Converting the animated demo to a GIF | the skill that prompted this review states GitHub does not play animation inside SVG, and GitHub was observed serving this file byte-identical with all five SMIL elements intact, so a GIF would cost the text layer and the file size for nothing
Ruled-out: Turning the delivery table into a chart | tables belong in Markdown where they stay selectable and diffable, and the numbers would have left the text layer
Certainty: firm
Blast: local
Undo: easy
Verified: rendered at 1200 and inspected -- nothing clipped, active and superseded rows both legible; both SVGs parse; readme, readme-order, readme-numbers, readme-positioning, compatibility-matrix, demo and manifest pass at 114; check-readme-numbers.mjs exit 0 with the BENCH block byte-identical; spec/verify.sh OK at 26 fixtures; the skill's own audit_readme.py reports no issues
Unverified: how the hero reads in GitHub's dark theme -- the palette is a fixed light paper and was not re-checked against a dark background
Record the M5 deviation instead of quietly restarting
Make the hero image say what the heading below it says
…s number

Every figure this project has is CommitLore measuring CommitLore. A competitive
review named that as the largest evidence weakness and it is a real one, not a
presentational one. bench/DECISION-DELIVERY.md §7 already says so in its first
bullet, but declaring a weakness is not measuring past it.

This registers what can and cannot be carried to a repository nobody here wrote,
and it does so before the harness exists, so the commit order is checkable the
same way DECISION-DELIVERY.md's was.

The split it fixes is the whole design. The `git log` baseline needs no records
at all — it is a fact about how deep a file's history is and how long its commit
messages are — so whether 42.0% is a property of `git log` or a property of our
repository is answerable outright. The delivery figure needs records, and no
external repository has any, so records have to be manufactured from ground
truth the project declared itself: revert commits, selected by the machine-
written `This reverts commit <sha>` line rather than by a `Revert "…"` subject,
because that line names the commit and makes a record derivable instead of
guessed.

Six filters and a return check are fixed here rather than after seeing which
ones flatter the number, including the 0.5 threshold and the two alternative
thresholds the run has to report beside it.

The document also states, in the words the result section will have to repeat,
that a backfilled figure is an upper bound on a process no user has run. Every
generated record is correct by construction because the filter kept only reverts
whose change is provably still gone. That is an oracle, and an oracle is not
evidence anyone would record anything.

Limit: reverts are the one decision class an arbitrary repository declares
  mechanically — constraints, warnings and quietly-abandoned alternatives leave
  no revert and are invisible to this corpus
Limit: four repositories, all Python, all large and long-lived; nothing here
  will speak to a small or young repository or to another ecosystem's commit
  habits
Ruled-out: rewriting the external histories to plant trailers in commit messages
  | it changes every downstream sha, destroys the pinned-SHA corpus statement,
  and src/core/backfill.ts already refuses it for the same reason
Ruled-out: reporting only the plain `git log` arms on the backfilled corpus |
  they score 0% there by construction because the records are in notes, and a 0
  presented as a fact about Git is the most dishonest number this could produce
Ruled-out: sampling paths on the large repositories | the sweep is one `git log`
  per path and a deterministic sample still invites the question the full sweep
  closes
Warn: do not read §4's coverage figure as a recall figure — it equals one only
  if records are one per commit and evenly spread, which is false in detail
  here, which is why the calibration row at b3f5692 exists
Verified: the four pinned SHAs were read from `git rev-parse HEAD` on full
  clones taken today, and the harness will refuse to measure any other tree
Unverified: no number exists yet — the harness this document specifies has not
  been written, and §9 is empty on purpose
Evidence: bench/EXTERNAL-CORPUS.md
Blast: local
Undo: easy
Certainty: firm
Record-Id: r-e1a7c4
Provenance: authored
CommitLore-Version: 2.0.0
Implements bench/EXTERNAL-CORPUS.md, committed one commit earlier and unchanged
since. Three measurements: the record-free part of the `git log` baseline over
every tracked path of five corpora, the deterministic revert-to-record backfill,
and the registered delivery metric run on what the backfill produced. No number
yet — §9 of the method document is still empty.

The delivery metric is not reimplemented. `measureDecisionDelivery` scores the
external corpus, so a figure here is comparable to the 81.7% row because it came
out of the same scorer, the same denominator and the same answer key. That
forced two changes to a registered harness, and both are additive with the
registered behaviour as the default, so this repository's own rows do not move:
`buildCensus` gains an opt-in that folds `refs/notes/commitlore` into the answer
key, and `measureDecisionDelivery` gains a route list that defaults to the seven
arms §5 of DECISION-DELIVERY.md registered.

The notes-aware Git arms are the awkward part and they are not optional.
Backfill cannot rewrite an external history — it would change every sha and
delete the pinned-SHA corpus statement the whole measurement rests on — so the
records land in the notes mirror, where `git log --format=%B` cannot see them.
The plain Git arms therefore score 0% on that corpus. That 0 is a fact about the
record channel, not about Git, so both plain arms are kept and reported and
`git-log-path-notes-budgeted` is added as the comparator that reads the same
records from the same place at the same budget.

Two things the harness refuses rather than works around. A clone that is not at
the pinned SHA, or is not clean, stops the run: the delivery metric reads the
working tree through `git ls-files`, so a dirty clone would score files the
pinned tree does not contain. And the coverage sweep asserts, per path, that
`git log --format=%B` really is each body followed by one LF — the offsets that
decide which commits survive a prefix cut are wrong otherwise, and a coverage
figure computed from wrong offsets still looks like a result.

Limit: the sweep is two `git log` calls per path and the delivery phase runs
  `git log --follow` on every tracked path, so a full run over the four externals
  is hours rather than minutes on one machine
Ruled-out: reimplementing the scorer for the external corpus | two scorers that
  drift by a line produce two numbers that look comparable and are not, which is
  the failure #163 already recorded here
Ruled-out: replacing the per-path `--follow` chain with one forward pass over a
  rename graph | it is much faster and it is a different attachment predicate
  from the one DECISION-DELIVERY.md §3 registered, so the two corpora would stop
  being comparable
Ruled-out: turning the census notes option on by default | every measurement
  registered before it existed read commit messages only, and a denominator that
  silently grew would invalidate them without saying so
Warn: `budgeted_log_coverage` is not recall — it counts commits, not records,
  and only the calibration row at b3f5692 says how far apart the two are on a
  repository where both are known
Verified: 15 new tests over a fixture whose reverts undo modifications rather
  than additions, so the reverted path is still tracked at HEAD and the
  denominator is not empty
Verified: a notes-only record is invisible to `git-log-path` and delivered by
  `git-log-path-notes`, measured rather than asserted
Verified: `buildCensus` finds 0 records without the option and 1 with it, on the
  same fixture
Verified: typecheck of bench/tsconfig.json, npm run typecheck, and
  scripts/check-readme-numbers.mjs all clean
Unverified: the four externals have not been measured yet — the harness has run
  end to end only on psf/requests and on the test fixture
Evidence: bench/external/run.ts
Evidence: bench/external/backfill.ts
Evidence: test/external-corpus.test.ts
Follows: r-e1a7c4
Blast: module
Undo: easy
Certainty: firm
Record-Id: r-3c9d52
Provenance: authored
CommitLore-Version: 2.0.0
First measurement this project has on a corpus it did not write: 137,706 commits
of Django, SymPy, scikit-learn and requests against the 549 of its own. The
method was registered two commits ago and nothing in it moved.

The answer to the question that mattered is clean. The record-free skeleton of
the `git-log-path-budgeted` arm reads 55.6%, 49.3%, 44.9% and 37.4% on the four
externals. On this repository, at the exact commit that produced the published
rows, it reads 39.1% against a published record recall of 42.0% — and 66.8%
macro against 65.2%. So the proxy tracks the thing it stands in for to within
three points where both are known, and 42.0% sits inside the external band,
nearer its floor than its ceiling.

The mechanism does not transfer even though the number does, and that is the
more useful finding. Django loses coverage because its paths carry a median of 8
commits at 213 tokens for the whole log; this repository loses it because its
paths carry a median of 1 commit at 687 tokens, so one message exhausts the
budget and 258 of 1,069 paths get nothing at all against Django's 1 of 7,079.
Writing longer commit messages makes ordinary Git worse at a fixed budget. That
is a cost of the practice this tool encourages and it was not visible anywhere
in bench/ before now.

The delivery half is an oracle and is labelled as one in §9.0, above the number
rather than below it. 169 records generated from revert commits, each correct by
construction because the filter kept only reverts whose change is provably still
gone. `commitlore` reads 100.0% on all four, which §9.5 says plainly is what the
attachment predicate forces once records are small and the budget never binds —
not a retrieval result. The informative quantity is the gap to ordinary Git
reading the same records from the same place at the same budget, and §9.6
decomposes it into three different stories rather than pooling them: Django is a
budget story, scikit-learn is a rename story, and SymPy leaves 35 pairs that
neither explains.

That residue is the one new thing about the baseline. Four of the 35 were opened
by hand and in each the commit carrying the record changed the path and does not
appear in `git log -- <path>` at all. Git's default history simplification does
not enumerate every commit that touched a path — which, if it generalises, costs
the ordinary-Git comparator records that no budget and no `--follow` would
recover, on this repository as much as on SymPy.

Limit: no maintainer of these four repositories wrote a record, so §9.4 bounds a
  process nobody performed and is not evidence anybody would perform it
Limit: zero superseded and zero expired records in the whole external corpus, so
  `stale_delivered = 0` on every arm is arithmetic and not evidence about the
  lifecycle filter
Limit: one template, one record shape, one decision class — reverts are the
  decisions loud enough to reach the history as an undo, and constraints,
  warnings and quietly-abandoned alternatives leave none
Ruled-out: quoting the 0.0% on the two plain Git arms as a result | the records
  are in the notes mirror because backfill must not rewrite these histories, so
  that 0 is a fact about the record channel and not about Git
Ruled-out: pooling the four repositories into one figure | the three-way split
  in §9.6 is invisible in any average, and the question asked was whether a
  figure from one repository describes another
Warn: `commitlore` at 100.0% is near-definitional on this corpus — gold and the
  shipped route decide which records belong to a file by the same predicate, and
  with a budget that never binds there is nothing left for it to falsify
Verified: 39.1% record-free coverage against 42.0% published recall, and 66.8%
  macro against 65.2%, at the same commit b3f5692
Verified: 41 of 418 revert candidates name a change that is materially back in
  the tree at the pinned SHA and were refused; Django accepts 33 at a 0.25
  threshold, 43 at 0.5 and 49 at 0.75
Verified: 102 of the 169 accepted records carry no reason at all, because
  `git revert`'s default message is a subject and a boilerplate line
Verified: every clone was asserted to be at its pinned SHA and clean before it
  was measured, and the byte layout of `git log --format=%B` was asserted per
  path before any commit was scored as delivered
Unverified: whether history simplification explains SymPy's 35-pair residue at
  population scale — four cases were opened, the count was not measured
Unverified: nothing here says the 81.7% delivery figure transfers; the corpus
  that would answer it is one where somebody other than this tool's author wrote
  the records, and no such repository exists yet
Evidence: bench/EXTERNAL-CORPUS.md
Evidence: bench/results/external-corpus-20260801T133722Z.jsonl
Follows: r-3c9d52
Blast: local
Undo: easy
Certainty: firm
Record-Id: r-7f2b18
Provenance: authored
CommitLore-Version: 2.0.0
Measure CommitLore on four repositories it did not write
Every figure this project publishes was measured on this repository measuring
itself, and the sharpest reading of that is not that the numbers are wrong but
that nobody can tell. The `git log` comparator was the part most exposed: 42.0%
could as easily have been a fact about how this repository writes commits as a
fact about `git log`.

It is not. On four repositories nobody here wrote -- Django, SymPy, scikit-learn
and Requests, at pinned commits -- the share of a path's history surviving an
800-token cut runs 37.4% to 55.6%, and 42.0% sits inside that band.

The more useful half is what did not transfer, and it costs us rather than
flatters us. Our paths carry a median of one commit at 687 tokens where Django's
carry eight at 213, so 258 of our 1,069 paths get nothing at all at that budget
against Django's 1 of 7,079. Long commit messages make the ordinary-Git baseline
worse at a fixed budget, and that is this project's own practice showing up in
its own comparator. It was not visible anywhere before this run.

The delivery figure from that corpus is deliberately not quoted here. It reads
100.0%, and it reads that way because a backfilled record is one line, the budget
never binds, and gold and the shipped route decide what belongs to a file by the
same predicate. The pointer says to read the two sections that explain it before
the number, because the number alone would be the most misleading thing on this
page.

Record-Id: r-extbaseline
Limit: the band is four Python repositories chosen for having enough revert history to backfill from, so it is evidence about large long-lived Python projects rather than about repositories in general
Ruled-out: Quoting the 100.0% delivery figure | it is the value the attachment predicate forces on one-line backfilled records, not a retrieval result, and on a README it would read as the opposite
Ruled-out: Leading with the external band instead of our own 81.7% | the external corpus has no real records in it, so it cannot carry the claim the delivery number makes
Certainty: firm
Blast: local
Undo: easy
Verified: readme, readme-order, readme-numbers, readme-positioning and compatibility-matrix pass at 83 across all four language files; check-readme-numbers.mjs exit 0 with the BENCH block byte-identical; spec/verify.sh OK at 26 fixtures; the four pinned corpus SHAs were resolved against the GitHub API and their dates match the corpus statement
Unverified: whether the band holds outside Python, and whether a repository whose records were written by someone other than this tool's author would reproduce 81.7%
Answer the strongest objection to the delivery number, in the README
The README's only performance figure was 7.4 seconds to index 768 commits, from a
field report. That answers "is this cheap to try" and leaves the question anyone
with a real repository asks next: what happens at a hundred times that size.

The answer is measured and it was sitting in docs/evidence.md. An indexed context
query at 100,000 commits is 496 ms p50; the commit-msg hook is 185.85 ms p50 and
the injection hook 102.40 ms. Those are the numbers that decide whether this stays
installed rather than whether it gets tried once, and the second question is the
one that costs a user something to answer for themselves.

The paragraph also carries the figure from that run that looks bad: the same query
without an index takes 86,673 ms at 100,000 commits. Publishing the good number
while leaving that one in a linked document would be choosing which half of a
single measurement a reader sees. It also explains a design choice rather than
excusing it -- the index is not a speedup on a query that already worked, it is
what makes the query possible at that size, which is why init builds one and
doctor checks it.

Record-Id: r-scaleproof
Limit: the 100,000-commit figures come from a synthetic repository built by the deterministic harness, not from a real codebase of that size, so they describe the index's shape rather than any particular project
Ruled-out: Quoting only the 496 ms | it is one arm of a run whose other arm is 86,673 ms, and a reader who found that later would be right to distrust the first number
Ruled-out: A latency table | three figures do not need one, and the README just had its reference material moved into docs/
Certainty: firm
Blast: local
Undo: easy
Verified: readme, readme-order, readme-numbers, readme-positioning and compatibility-matrix pass at 83 across all four language files; check-readme-numbers.mjs exit 0 with the BENCH block byte-identical; spec/verify.sh OK at 26 fixtures; the beautify skill's audit_readme.py reports no issues; every figure quoted was read out of docs/evidence.md rather than recomputed
Unverified: how the indexed query behaves on a real repository at that size, which no run in this project has measured
Put the scaling numbers where the objection is raised
A competitive survey turned up arXiv 2603.15566 -- "Lore: Repurposing Git Commit
Messages as a Structured Knowledge Protocol for AI Coding Agents", March 2026,
four months before this repository existed. Its abstract describes this product:
git trailers carrying constraints, rejected alternatives, agent directives and
verification metadata, no infrastructure beyond git, a standalone CLI, readable
by any agent that can run a shell command. Compared against its README, the
vocabularies map almost one-to-one -- Constraint to Limit, Rejected to Ruled-out,
Confidence to Certainty, Scope-risk to Blast, Reversibility to Undo. Both worked
examples are an auth-token-expiry commit.

So the differentiator table had a row missing, and it is the row a reader would
have found on their own. Finding it that way teaches them something about this
project rather than about Lore.

What is actually different was checked rather than assumed. Lore has no
Supersedes:, no Expires: and no lifecycle concept -- grepping its README for
supersede, expire, lifecycle, retire or reversed returns only matches inside its
own worked example, which is about expired auth tokens. It has no trust grading.
And the paper says it "outlines an empirical validation path", which is a precise
way of saying it ran no experiment.

That leaves a claim this project can defend: not that it invented the protocol,
but that it added the lifecycle and the grading, and ran the validation the paper
outlined -- including VERDICT-M1 at p = 0.7480, VERDICT-M4 withdrawn for
unverifiable exposure, and ROUTE-GAP, where the injected context was read and
ignored. Being second is a fact about a date. Being second and quiet about it
would have been a fact about the project.

Record-Id: r-priorart
Limit: the comparison is against Lore's README and its abstract; the full paper was not read, so a lifecycle described only in the PDF would have been missed
Ruled-out: Saying nothing | the survey found it in an afternoon and anyone evaluating this project will
Ruled-out: Framing it as convergent design | it may well be, but "we arrived independently" is unfalsifiable and asks for exactly the trust this project argues should not be extended to unverifiable claims
Ruled-out: A feature-by-feature comparison table | Lore is a design paper with a small implementation, and scoring it against a shipped tool would be unfair and would read as defensive
Certainty: firm
Blast: local
Undo: easy
Verified: arXiv 2603.15566 resolved through the arXiv API -- title and abstract read directly, published 2026-03-16; the trailer vocabulary and the absence of any lifecycle term were checked against tmdgusya/lora's README; readme, readme-order, readme-numbers, readme-positioning and compatibility-matrix pass at 83 across all four language files; spec/verify.sh OK at 26 fixtures
Unverified: whether the resemblance is convergent or derivative, which is not decidable from either artifact and is not claimed either way
Name the paper that published this protocol first
`hooks install` writes commitlore.bin/commitlore.root into one repository's
config. An upgrade installs the new release somewhere else and never visits the
repositories that pinned the old one, so a repo can sit two releases behind on
the path that actually validates its commits while `commitlore --version`
reports the newest one. Reproduced against a v0.5.0 install directory alongside
0.6.0: both doctor and hooks status called it healthy, and doctor printed the
stale v0.5.0 path inside its own `ok` line. It had the value in hand and did not
act on it.

That is not cosmetic, because the hook is the enforcement point. Everything
0.5.1 and 0.6.0 shipped that touches validate, prepare-commit-msg or post-commit
is inert in such a repository -- this release's Ruled-out: narrowing, the
shallow-clone gate, duplicate-id dedup, the fail-open capture hooks. It is also
latent: deleting the old install directory turns every commit there into a hook
failure.

The comparison goes in core/hook-target.ts, the mirror both readers already
share, so doctor and hooks status cannot disagree about it and the remedy they
already name -- `commitlore hooks install` -- is the one that fixes it.

A pin whose version cannot be established is reported too, not passed over. The
pinned build still decides what runs, and "could not find out" is a different
answer from "it matches"; a false green there costs a repository every fix
shipped since the pin was written, which is the trade this project has made the
same way everywhere else.

Record-Id: r-pinskew
Limit: the comparison reads the package.json above the recorded path rather than running it with --version, so a pin whose manifest and bundle disagree is reported by its manifest
Ruled-out: Re-pointing the pin from doctor --fix | doctor's --fix boundary is reversible local config only, and the module comment states the commit-msg hook is reported and never written -- rewriting commitlore.bin would also change which binary validates commits, silently, from a diagnostic
Ruled-out: Having the upgrade re-point every repository | install.sh cannot enumerate the repositories that ran hooks install, which is why the issue itself proposes reporting as the self-correcting half
Ruled-out: Spawning the pinned bundle for --version | the recorded path is by design a value a .git/config edit can change, and hooks status spawns nothing today -- executing it on every status and every doctor is a larger door than the question is worth
Ruled-out: Treating a manifest with no version as a match | that is the false green the check exists to remove
Certainty: firm
Blast: module
Undo: easy
Verified: reproduced with a v0.5.0 install pinned beside 0.6.0 -- doctor and hooks status both reported ok before, both name the skew and `commitlore hooks install` after, and running that command clears it; 6 tests added across doctor, hooks and hook-target failed first and pass now; doctor, hooks, init, cli, hooks-entry-resolution and hook-target pass at 180; npm run typecheck and npm run build clean
Unverified: whether any repository other than the reporter's carries a stale pin, which nothing here can enumerate
Compare the version the hook is pinned to, instead of printing it
TOKEN-LEDGER.md blocker B gave two reasons the drafting turn (W4) is missing
from the write side. One was that it needs a model call. The other was sharper:
the driver read a single session-total `usage` object, so the harness could not
have attributed an answer to the turn that produced it even if a call were
made. The second reason is now false. This is the instrument, not the
measurement.

The CLI was run rather than read about. `--output-format stream-json` emits an
assistant event per content block, each carrying the same `message.usage` and
the same `message.id`, so summing them double-counts every turn. Deduplicated,
`input_tokens` and both cache fields are final and reconcile exactly with the
session total — and `output_tokens` does not. It is the `message_start`
snapshot: three turns reported 4, 1 and 1 against real outputs of 157, 193 and
36, a session total of 6 against 403. Nothing on the event marks it provisional
while its neighbours are not. The term the write side is missing is precisely
the one term this format reports wrongly, and the correct figure arrives only
on `message_delta`, only under `--include-partial-messages`. Both raw captures
are committed as fixtures, so that paragraph is checkable rather than asserted.

Nothing an earlier run measured moves. `--per-turn-usage` is opt-in and off is
the shape every row in bench/results/ was produced under. Both output formats
end in the same `result` object, and `transcript`, `turns` and `tokens` are
still read from it under the same field selection — verified against a live
run: session total 26 + 649 + 752 = the 1,427 the row reported. The ledger is
built from events the JSON format never printed and nothing already measured is
re-derived from them.

Every ledger carries its own audit rather than asking to be trusted:
`turn_total`, `session_total`, and a `reconciled` boolean that is true only
when the turns sum to the total the CLI states for itself, field for field. A
parser that missed or duplicated a turn says so on the row.

The schema gate was already failing before this touched it. `reproposal_matches`
and the four `rejected_path_*` counts reached the rows without reaching
`result.schema.json`, so `verify.mjs` rejected all 80 rows of the most recent
matrix on `additionalProperties`. Adding a sixth field to that list without
fixing it would have shipped a gate that gates nothing.

Limit: `turn_usage` carries the provider's tokenizer; every figure in
  bench/TOKEN-LEDGER.md is in the product's own ceil(chars / 4) proxy. A future
  W4 must be reported beside the floor, never added into it, until both sides
  are on one tokenizer
Limit: `content_blocks` says a turn called a tool, not which tool or on what —
  the tool_use block's name and input are discarded, so #141's rejected-path
  tool actions stay uncounted
Ruled-out: turning per-turn capture on by default | it changes the CLI
  invocation, and a harness flag that quietly rewrote how a run is observed
  would make old and new rows two different things under one name
Ruled-out: summing the `usage` on assistant events | they repeat once per
  content block and their output_tokens is a pre-generation snapshot; that
  reading is wrong by 397 of 403 on the one probe that checked it
Ruled-out: writing `turn_usage` on every row with nulls when uninstrumented |
  an always-null field is worse than no field, and absent already reads as "not
  instrumented" everywhere else on this row
Ruled-out: retaining the whole stream to parse at the end | partial messages
  emit an event per chunk, so a long run would hold tens of megabytes to
  produce a few hundred integers
Warn: this closes the attribution half of blocker B only. W4 is still
  unmeasured, W3 is still unrecoverable for this corpus, and no figure in
  TOKEN-LEDGER.md section 9 changes
Warn: section 5 of TOKEN-LEDGER.md is pre-registration and was appended to, not
  edited — the floor has to stay visibly declared in advance
Verified: the raw event stream of two real invocations, kept and committed
  under test/fixtures/claude-stream/ with local paths redacted
Verified: the per-turn ledger reconciles with the CLI's own session total on
  all four usage fields — 26 / 386 / 307 / 72,845 on the fixture, and again on
  a live run through the wired driver
Verified: the same driver with the option off and on returns the same
  transcript, turn count and token field selection
Verified: 12 new tests; bench/verify.mjs now validates 252 rows across three
  current-shape result files that it previously rejected
Verified: npm run typecheck, tsc on bench, npm run build, and
  scripts/check-readme-numbers.mjs all clean
Unverified: no drafting turn has been priced. No bench arm runs `capture`
  against its own transcript and diff, so the number blocker B is about still
  does not exist
Unverified: subagent attribution is covered by a constructed stream, not an
  observed one — no probe here produced a nested agent
Evidence: bench/TOKEN-LEDGER.md
Evidence: bench/drivers/stream-json.ts
Evidence: test/fixtures/claude-stream/partial-messages.jsonl
Follows: r-ledgerresult
Blast: module
Undo: easy
Certainty: firm
Record-Id: r-4e7c19
Provenance: authored
CommitLore-Version: 2.0.0
test/hooks.test.ts carried one literal NUL at offset 7059, line 184, inside
`.join('\n\0\n')` -- where the `\0` was the raw byte sitting in the source, not
the two-character escape it reads as. Git calls a blob binary when it finds a
NUL in the first 8000 bytes, and 7059 is inside that window, so every diff of
this file printed `Binary files ... differ` on GitHub and locally. Scanning all
1,251 tracked files finds exactly one hit, this one.

The cost was paid on every review. #388 changed this file, and reviewing that
change meant stripping the byte into a scratch copy and diffing that by hand.
The file is 1,200-plus lines and covers the commit-msg hook, so the file hardest
to review was the one covering the enforcement point. Search was worse than the
diff: `grep -n OWN_HOOKS_DIR test/hooks.test.ts` exited 1 and reported nothing
for a symbol on line 49, well before the byte -- not truncation at the NUL but a
refusal to report the file at all, and `git grep` answers only `Binary file
test/hooks.test.ts matches`.

The question is whether the NUL was buying something. The separator joins
per-file entries of the form `${name} ${mode} ${contents}`, so it has to be a
sequence that will not turn up inside a hook body; otherwise two different states
of .git/hooks could serialise to the same string and a change would pass unseen.
A NUL looks like the one sequence a text file cannot hold. In this position it is
not. The entries are read with `readFileSync(path, 'utf8')`, which decodes a 0x00
byte in a hook to U+0000 in the string -- measured directly against a hook file
written with a NUL in it. The old separator was therefore exactly as forgeable as
a text one, and worse for the only hooks that plausibly carry NULs at all: a
shell script holds none, a compiled hook holds many.

What actually makes either spelling safe is that the joined string is never
parsed. It is compared for equality against a snapshot of the same directory
taken earlier in the same process, so an ambiguous separator cannot mis-split
anything. The only failure available to it is a collision, and a collision needs
a hook body carrying the sentinel on a line of its own, placed so the whole
concatenation is reproduced byte for byte. None of that reasoning was written
anywhere, which is how the byte survived; it is now recorded at the definition,
which is the part that keeps.

The assertion is untouched. `expect(snapshotOwnHooks()).toBe(ownHooksBefore)`
still compares names, modes and bytes, and the helper still builds its entries
the same way. Only the spelling of the separator moved.

Record-Id: r-nulsep
Limit: the sentinel is safe because nothing that writes into .git/hooks contains it, not because it is unrepresentable -- a hook body carrying the sentinel on its own line is the one input that could in principle collide, and the helper does not reject it
Ruled-out: Keeping the NUL and recording why in a comment | the reason would have to be that a NUL cannot occur in a snapshotted body, and it can -- readFileSync utf8 decodes 0x00 to U+0000 -- so the comment would have documented a guarantee the code does not provide
Ruled-out: Marking the file text in .gitattributes | that hides the symptom from git diff while leaving the byte in the source, so grep, editors and everything else that reads the tree as text still skip or truncate it
Ruled-out: Length-prefixing each entry to make the encoding injective | genuinely collision-proof, but it changes what the snapshot serialises, and #389 is about how the separator is spelled rather than what the comparison holds
Ruled-out: Joining on a plain newline | hook bodies end in a newline, so entries would run together and a change that moved bytes across an entry boundary could vanish, which is the collision the separator exists to prevent
Certainty: firm
Blast: local
Undo: easy
Verified: one NUL at offset 7059 confirmed by byte scan, and the same scan over all 1,251 tracked files reports none anywhere after the change; readFileSync utf8 measured to yield U+0000 from a hook written with a 0x00 byte, which is what makes the NUL non-load-bearing; the new blob holds no NUL, so a further edit to the file diffs as text and git diff --numstat reports line counts instead of a dash; the helper re-run with the new separator against a scratch repository's real .git/hooks still detects a one-byte content change, a 755 to 644 mode change, a hook added, a hook removed, and a body that embeds the sentinel itself; hooks, init and doctor pass at 130; npm run typecheck and npm run build are clean and dist is byte-unchanged
Unverified: whether any hook anywhere carries the sentinel in its body, which nothing here can enumerate; the diff of this commit itself still reads binary because its pre-image is, which no change to the post-image can alter
Make per-turn token usage observable, and say what it still cannot price
Spell the hook snapshot separator as text, so the file can be reviewed
A stop reason this document never named turned up in the registered run:
over-tokens, from the fixture's own per-task budget rather than the invocation
cap, which has spent about 3 million of 200 million. It is the same shape as
over-turns -- a run cut off before finishing, where a false carries less than a
false on a completed run.

At 71 of 1,160 rows the combined truncation rate is 16.7% in the treatment arm
and 31.4% in the control. The off-design sample was balanced at 15 and 15; this
is not.

Two readings fit and the honest thing is to write both down now rather than pick
whichever the final table makes convenient. It could be noise that pushes the
control's rate down, which works against the hypothesis rather than for it. Or it
could be the treatment settling sooner because it has the records, which is an
effect rather than a confound -- on an outcome this measurement did not register
and therefore cannot claim.

Nothing about the analysis changes. Excluding truncated rows after seeing that
their rate is asymmetric is precisely the move a pre-registration exists to stop,
and M1 kept them, so dropping them would break the one comparison M5 was built to
make. What is added is an obligation on the verdict to report all three shares
per arm, registered now because after the table exists it would be worth nothing.

Record-Id: r-m5trunc
Limit: the rates come from 71 rows, so they indicate an imbalance rather than measure one; the verdict reports the final figures
Ruled-out: Excluding truncated rows from the analysis set | the rule would change after the rate became visible and asymmetric, and M1 kept them
Ruled-out: Raising the per-task token budget for the remaining shards | it changes the instrument mid-run and makes the completed rows a different measurement
Ruled-out: Stopping to investigate the imbalance | the stopping rule forbids interim analysis, and the imbalance is reportable without resolving it
Certainty: firm
Blast: local
Undo: easy
Verified: over-tokens traced to tokens 60000 in the fixture YAML against a run that spent 74,314; invocation cap at 200,000,000 with about 2,958,350 spent; M1's truncation share computed from t702-m1-final.jsonl at 15.0% of 60 reproposal rows
Unverified: which of the two readings is right, which the registered comparison cannot decide and this note does not claim to
MongLong0214 and others added 28 commits August 8, 2026 07:10
CDEB-P ran sixteen times and returned three parameters that could not have been
found by reading the protocol again. v1.3 changes exactly those, and nothing
about the design, the matrix or the twenty locked decisions.

The token threshold was 15%, chosen before anyone measured what the ON arm
costs. It costs 45% more, so 15% required a 1.71x relative lift -- at a 50%
control rate the ON arm would have had to reach 85.5% to clear a gate whose
sibling asks for ten points. Injecting context costs tokens by construction, so
the threshold has to know the overhead, and it cannot learn it from the outcomes
without becoming a number chosen after the fact. It now derives from a
calibration measured on the disposable smoke tasks, which never enter the
corpus and therefore expose no outcome, and both the calibration and the derived
threshold are frozen before the study runs.

Token efficiency also leaves the full-headline conjunction. Three ANDed gates
sounded like three measurements and behaved like one: the hardest could veto the
other two, so a real behaviour improvement would have reported FAIL. It is still
evaluated, still reported, still available as a partial claim, and its threshold
is now stricter than the number it replaced.

"Completable in one fresh agent session" was a wish with no check, and one task
in four burned a quarter of the study hitting the wall in all four runs. A task
now passes a wall-clock probe at 60% of budget before sealing, and the probe may
read only wall time and stop reason -- never the oracle -- which is the same
distinction that let this session read stop_reason mid-study without reading a
result.

Qualification required the record to exist, not to be reachable. Two tasks in
four delivered nothing to the ON arm, which made those runs ON by assignment and
OFF in substance and left one task carrying the whole question. Sealing now
requires that a path the good control edits actually renders the expected record
at the frozen snapshot -- a check that runs no agent.

Limit: the calibration is measured on disposable tasks, so it estimates overhead on work that is not the corpus and a corpus with different injection density will sit off it
Ruled-out: raising the 15% to a number that looked achievable | picking a threshold to be clearable is the same act as picking one after seeing results, and only the derivation makes it preregistered
Ruled-out: keeping token efficiency in the conjunction with the new threshold | the objection was never the number, it was that one gate could erase a measured behaviour result while the report called it a three-metric study
Ruled-out: widening the hook matcher so the two silent tasks would deliver | that redefines a delivery failure as a delivery success, and §9.5 exists to record exactly that failure
Warn: the 0.6 probe budget and the 1.15 calibration factor are judgement calls with no measurement behind them, unlike the three findings that forced this revision
Blast: local
Undo: easy
Certainty: firm
Verified: schemas parse and now pin 1.3.x so a protocol change is a schema change CI notices, the token threshold is a range with a required calibration rather than a constant, and the eleven verifier cases pass against a 1.3.0 fixture
Provenance: authored
Record-Id: r-cdebv13
Write down what 0.7.0 changes, starting with the tier nobody could reach
An external production-readiness review rejected the first v1.3 draft, and it
was right on every finding. This replaces that draft.

The formula was wrong, and wrong in the direction that matters. TVPDSS reduction
is `1 - o/q` for overhead o and relative lift q, so the overhead is already
inside the metric. Deriving the threshold from o again counts it twice, and the
algebra comes out as `q >= 1.15*o^2` -- at the pilot's measured o = 1.45 that is
a 2.42x lift, which at a 50% control rate asks the ON arm for 120.9%. I replaced
an unreachable gate with an impossible one. The stated 5% floor could never be
selected either: the schema requires o >= 1.0 and the formula returns 13.04%
there.

The 15% threshold is restored, and the diagnosis that produced the draft is
withdrawn with it. The gate was never unreachable -- it was strict because
injecting context costs 45% more tokens, and asking for 15% fewer per usable
result on top of that is a demanding bar rather than a broken one. Calibration
stays as a feasibility note that sets no threshold.

Removing token from the headline conjunction while leaving §17.1's sentence
alone created a path where a failed token gate still produced "used Y% less
token volume". Three gates now exist with fixed wording each, and only the
combined one generates the three-metric sentence. The draft's claim that this
loosened nothing was false and is recorded as false: `P and M` is strictly
weaker than `P and T and M` whatever T's threshold becomes.

The runtime probe named no arm, which lets one arm's runtime select the corpus;
it discarded the row the freeze manifest was supposed to contain; and it called
a returned process a completed task, which a no-op satisfies. It is now an ON
and OFF pair, artifacts preserved, renamed to runtime-boundedness.

Deliverability was checked with `commitlore context`, which is not the surface
CDEB measures -- budget, trust grading, matcher, index and output parsing all
sit between it and the agent, and zero shipping delivery was the defect being
fixed. It now drives the frozen shipping inject path and passes only when the
record appears in the forwarded payload.

And the draft asserted the pilot showed task qualification caused the two silent
tasks. The pilot's own result says its instrumentation could not tell that from
a hook that never fired. The causal claim is withdrawn; v1.3 closes both
possibilities separately instead.

Limit: schemas and the verifier now require the qualification evidence, but nothing yet produces it -- CDEB-10 must build the probe and the deliverability check before a freeze can be written
Ruled-out: keeping the derived threshold with the exponent corrected | any threshold that moves with measured overhead is a threshold the study can influence, and the fixed bar is the only one that means the same thing before and after the run
Ruled-out: lowering 15% to something the pilot's overhead clears | that measures a different claim and calls it the same one; if 45% overhead makes the bar hard, the bar is reporting a true fact about the product
Warn: the 0.6 probe budget is still a judgement call with no measurement behind it, and it now gates corpus membership through two agent sessions per task
Blast: local
Undo: easy
Certainty: firm
Verified: the double-count reproduces as q >= 1.15*o^2 giving 120.9% at o=1.45 against 85.3% for the fixed bar, and 18 verifier cases pass including a study with no freeze manifest, a hand-set threshold, an unqualified corpus, a build that disagrees with its freeze, delivery with zero executions, executions exceeding opportunities, and a hook that fired on a path with no records
Provenance: authored
Record-Id: r-v13fix
…uessing it

Two of the review's remaining limits close here. The third does not, and says so.

The delivery qualification exists now rather than being a paragraph. It builds a
real PreToolUse payload and runs the pinned shipping injector -- same command,
same budget, same trust configuration, same snapshot as the ON arm -- and passes
only when the expected record id appears in the bytes the hook forwarded. It
renders nothing itself.

One test carries the whole argument for why the first version was wrong. A
record sits in the repository, on the edited path, and the shipping budget is
squeezed to one token: `commitlore context` would still show it, and this check
fails. That is the entire difference between the surface that was checked and
the surface CDEB measures, made into an assertion. The pilot's defect was zero
shipping delivery, and only one of those two surfaces can see it.

The 0.6 probe threshold was a judgement call, and the pilot's own wall times
settle it. Completed runs topped out at 0.48 of budget and the failing task sat
at 1.00, so the good and bad tasks separate anywhere in between and 0.6 touches
neither end. The same data limits what the gate may claim: two repeats of one
cell ran 89s and 431s, a 4.9x spread, so two probes cannot bound the tail. The
gate screens the median. Study timeouts stay ordinary measured failures under
intention-to-treat, and what it prevents is the thing that actually happened --
a task whose four runs all timed out and contributed nothing.

Limit: the runtime probe still has no implementation, so a freeze cannot yet be written even though the schema now demands its evidence
Ruled-out: qualifying delivery through `commitlore context` | budget, trust grading, the guard, lifecycle projection, the matcher and output parsing all sit between it and the agent, and the defect being fixed was zero delivery through exactly those
Ruled-out: requiring every good-control path to carry every record | a record scoped to one file of a multi-file change still reaches an agent that opens that file, and demanding all of them rejects sound tasks
Ruled-out: throwing on a non-zero injector exit | the hook is fail-open by design, so the exit code belongs in the freeze manifest where a reader can weigh it
Warn: delivery qualification proves the record can arrive, not that it will -- an agent that edits another path first or uses a tool outside the matcher still gets nothing, and §9.5 is what records that
Blast: local
Undo: easy
Certainty: firm
Verified: six cases -- a path carrying its record qualifies, a path carrying none does not, a squeezed budget fails what context would pass, a missing second record fails the task, any one of several paths suffices, and the payload digest and exit code are recorded rather than thrown
Provenance: authored
Record-Id: r-delivchk
…lot's numbers

The schema demanded runtime qualification evidence that nothing produced, which
was the correct failure direction and still a gap. It is closed.

The decision function is pure -- probes in, verdict out -- so the gate can be
tested without spending two agent sessions per assertion, and `runProbe` is the
only part that spends them. That split is what makes this suite runnable in CI
at all.

Every case is a pair the pilot actually produced. 80s and 89s qualifies. The
task that sat at 902s and 903s in all four runs does not. 431s -- the slowest
run that ever completed, at 0.48 of budget -- qualifies, which is the observed
separation the 0.6 threshold sits inside without touching either end.

Three refusals encode review findings rather than taste. A single arm is
refused because runtime is treatment-sensitive and qualifying on one arm selects
a corpus that arm finishes faster, with the bias inseparable afterwards. Two
probes of the same arm are refused for the same reason. The verdict reads the
slower arm rather than the mean, because the study runs each task six times and
averaging lets a fast arm carry a slow one into the corpus.

The last test asserts what the probe type does not contain. A selector that
could see functional_pass or rejected_decision_revived would be selecting on the
dependent variable, so the absence is pinned rather than assumed.

Limit: the gate screens runtime and says so -- `completed` means the process returned, which a no-op satisfies, and the pilot's 4.9x within-cell spread means two probes cannot bound the tail
Ruled-out: averaging the two arms | the study runs six sessions per task, so the slower arm is the one that decides whether they fit, and a mean lets the faster arm carry a task in
Ruled-out: keeping the probe artifact out of storage | the freeze manifest is required to carry per-task probe results, and a qualification nobody can recheck is not a gate
Warn: `runProbe` costs two agent sessions per task, so qualifying a 30-task corpus is 60 sessions before a single measured run
Blast: local
Undo: easy
Certainty: firm
Verified: eight cases against real pilot pairs -- 80/89s qualifies, 902/903s timeout does not, 431s at 0.48 of budget qualifies, a 700s slower arm fails on the slower arm, single-arm and same-arm probe sets are refused, an errored arm is refused however fast, and the probe type carries none of the three outcome fields
Provenance: authored
Record-Id: r-runprobe
The runtime gate decides corpus membership by wall time, so a probe on a
different model than the study screens a distribution the study will never
produce. §2.2 already forces a new study id when the observed model changes;
this is the same rule reaching the qualification that selects the corpus, and
`qualifyRuntime` now refuses a probe whose model is not the pinned one.

The model is sonnet. The 0.6 threshold this gate applies was derived from the
pilot's wall times -- completed runs topping out at 0.48 of budget against a
failing task at 1.00 -- and every one of those runs was sonnet. Switching models
does not adjust that derivation, it deletes it, and the threshold goes back to
being a number somebody picked. M1 and M5 also measured sonnet, so this is the
choice that leaves CDEB comparable with the evidence already published.

The refusal is worth having whichever model is chosen later, because the failure
it prevents is silent: a qualification run on a stronger model finishes faster,
admits tasks the study cannot finish, and the resulting timeouts look like task
difficulty rather than a mismatched screen.

Limit: pinning to sonnet means CDEB will say nothing about whether a stronger model needs delivered decisions less, which is a question worth its own study rather than a variable inside this one
Ruled-out: qualifying on opus while measuring on sonnet | the gate would screen runtimes the study never produces, and its threshold is derived from sonnet data
Ruled-out: leaving the probe model unchecked and documenting the requirement | the mismatch produces timeouts that read as task difficulty, so a note cannot catch what a comparison can
Warn: a later model change invalidates the 0.6 derivation as well as the pinning, so both must be re-established rather than only the constant edited
Blast: local
Undo: easy
Certainty: firm
Verified: nine cases pass, including a pair where one arm probed opus against a sonnet-pinned study and the qualification refuses by naming both
Provenance: authored
Record-Id: r-probemodel
Fix the three protocol numbers the pilot proved wrong
A repository with 815 commits, hooks installed and an index current with HEAD
held zero CommitLore records, and doctor reported all ten of its checks ok.
Four captures sat in its pending directory, one of them staged with a passing
validation and a record ready to attach, all four eight days old.

Every step behaved as designed. `capture-stage` stamps expiry five minutes
after staging; the commit did not happen inside that window; the
prepare-commit-msg gate skipped the record because it had expired; pending-gc
protects the staged phase and so never collected the file. The net effect was
that the product silently stopped producing records and said nothing.

`pending ls` already prints `stale` and `never-collected` on exactly those
rows. The information existed. The command people actually run did not carry
it, which is #402 and #400's category and the reason this is a doctor change
rather than a capture change.

The check separates the staged loss from the drafts that never staged, because
merging them hides which decisions had a record ready to attach. It names the
oldest timestamp so the age is visible without a second command.

Limit: the check reads staleness, so a capture whose base commit is still HEAD reports ok even if it has been waiting long enough that nobody remembers preparing it
Ruled-out: lengthening the five-minute expiry | a staged record binds to the tree it was prepared for, and attaching it to a different tree is worse than dropping it -- the defect is the silence, not the expiry
Ruled-out: letting gc collect expired staged files | that erases the only evidence a capture was ever prepared, and this repository found the defect precisely because the files were still there
Warn: a repository that has never captured anything and one whose pending directory was deleted both report ok, so this check cannot distinguish a healthy install from a missing one
Blast: local
Undo: easy
Certainty: firm
Verified: six cases -- ok with nothing captured, ok while a capture can still apply, warn naming the staged loss and its wording, staged and never-staged counted separately, no loss claimed when nothing reached staged, and the oldest timestamp present; run against the real repository it reports the one staged loss and three drafts by name
Provenance: authored
Record-Id: r-doctorpend
…althy

Eleven closed issues and one open one are the same defect. #40, #128 and #149
probed a path doctor reconstructed rather than the command that actually runs.
#49 let the hook be redirected to any executable and still said ok. #382 called
a stale hook pin ok. #335 reported 106 records where git had zero. #296
prescribed a fix that could not repair the condition, and #63's fix broke git
fetch and then said ok. #458 reported ten checks ok on a repository that had
silently stopped producing records eight days earlier.

None of those is a missing check. They are one structural failure: a verdict
with no evidence behind it, in a command that had no way to say "I could not
look" and no way to say "this failure is that failure's consequence".

The ADR decides the model. Every check carries typed evidence rather than
prose, so a claim and the observation behind it travel together. A check that
cannot run says skipped with a reason from a closed union rather than passing.
A check whose dependency failed is annotated `blocked_by` and still renders its
own evidence, because a suppressed row is how a screen lies by not looking.
Checks become registry data instead of a hand-assembled array, so a new one
cannot be added without a category and a place in the order.

The PRD specifies it, and the two hardest calls are recorded with their
reasons. Exit codes stay 0 and 1: the Swift reference maps degraded to 3, and
SPEC §10 fixes 3 protocol-wide as "ran and answered but could not see
everything", so that mapping would give 3 a doctor-private meaning §10 forbids.
Profiles are rejected with a reopening condition, because a profile that omits
checks reproduces #458 exactly -- the screen that lied by not looking.

Limit: this lands the model and the specification only; no check changes behaviour and the shipping report is byte-identical until the registry work begins
Ruled-out: a `manual` status, as the Swift reference has | it exists there for operator attestations the OS cannot query, and every CommitLore remediation is a command, so a status nothing emits is dead contract surface
Ruled-out: the reference's 0/1/2/3 strict exit mapping | SPEC §10 fixes 2 as "could not run" and 3 as "could not see everything" for every command, and RELEASE-GATE §4 plus init's `doctor --fix` depend on warn exiting zero
Ruled-out: per-surface profiles, for now | omitting checks to make a surface faster is the #458 shape, and `--only`/`--category` cover the subset uses until a measurement shows the full set is too slow
Warn: `blocked_by` is the one new field that can hide a real defect if it ever suppresses a row rather than annotating it, so the invariant belongs in a test before the first collapse ships
Blast: local
Undo: easy
Certainty: firm
Verified: SPEC §10's table read directly -- 2 is "could not run", 3 is "ran and answered but could not see everything", and it states a code MUST NOT carry a meaning another command does not give it; the ADR number follows the retired 0023-0025 range that ends at 0022 and resumes at 0026
Provenance: authored
Record-Id: r-adr32doc
Say when a capture was prepared and then dropped
An adversarial review of ADR-0032 and the doctor PRD returned three blocking
findings, seven major, three minor. Every finding checked out against source,
and two were not refinements but reversals of claims the documents stated as
fact. This repository publishes its corrections, so the ADR now opens with a
section listing what was wrong and what replaced it.

The reversals. First, the status derivation made ok unreachable: any
non-optional skip degraded the aggregate, and squash-conservation,
inject-runtime and inject-version skip routinely on healthy repositories --
permanent degraded is the #458 first-screen lie with its polarity flipped.
Skip reasons now carry a class: an observed true empty (not_applicable) does
not degrade; only could-not-verify does. Second, the exit-code reasoning was
wrong, not merely overstated: SPEC §10 does not forbid doctor exiting 3 -- an
unfetched notes mirror and shallow history are code 3's own named examples,
and RELEASE-GATE §1 already requires guard to exit 3 on an unfetched mirror.
Warn keeps exiting 0, but on its true grounds: RELEASE-GATE §4 compatibility
and SPEC's "a command need not use every code" -- named as compatibility debt,
with a narrow vision-gap 3 left open rather than falsely closed. The claim
that init depends on doctor's exit code was also false (init branches on
needsAttention) and is deleted.

The review also built three repositories that stayed healthy while broken.
Closed: a notes-availability check consults the remote's advertisement so
"refspec fixed, nothing fetched" can never aggregate ok; a capture-liveness
check warns when the hook chain is green yet no commit ever carried a record
(#458's literal shape); evidence is now required on every row, ok included,
so an ok with empty evidence no longer typechecks. One adjacent case stays
open and is named with its reason instead of papered over.

The rest: the absolute network ban was false against the shipping command,
which already runs git fetch --dry-run and git ls-remote -- the ban is
rescoped to doctor's own process, with offline runs degrading transport rows
to warn; registry order is frozen to the shipping runDoctor order the first
revision's table contradicted; blocked_by gets one mechanical producer (a
factory constructor, with the runner only normalising chains) and the
misattribution of SetupDoctor's design is corrected; the severity field and
the dedicated install-source check are cut; every mis-cited line number is
re-verified against source.

Limit: documents only -- src/ and test/ are untouched, and the two checks this revision specifies (notes-availability, capture-liveness) do not exist yet
Ruled-out: keeping "SPEC forbids doctor exiting 3" | spec/SPEC.md line 274 names an unfetched notes mirror and shallow history as code 3's own examples, so the prohibition claim was false and saying so plainly beats softening it
Ruled-out: degrading the aggregate on every non-optional skip | doctor.ts lines 628, 699 and 942 skip on ordinary healthy repositories, which made ok permanently unreachable
Ruled-out: a capture-recency heuristic to close the produced-then-stopped case | SPEC line 155 makes a record per commit optional, so recent recordless commits are not evidence of breakage; the gap is named open in PRD §2.2 instead of guessed shut
Ruled-out: dropping --only and --category as the review suggested | they are filters over registry data feeding the partial-honesty rule, not new code paths, and the disagreement is recorded in the ADR's Rejected section with that reason
Warn: the earlier revision claimed init depends on doctor's exit code; init.ts line 104 branches on needsAttention and ignores the exit code, so do not reintroduce that claim when next touching §7
Blast: local
Undo: easy
Certainty: firm
Verified: doctor.ts probes the network at lines 196 and 248; its ten skipped return sites and runDoctor's array order at 1021-1036 read directly; SetupDoctor.swift's severity comment found at lines 63-64 not 548, blockingCause at 634, and wc -l over SetupDoctor*.swift gives 3,754 lines across 21 files
Provenance: authored
Record-Id: r-adr32rev
Decide what a diagnostic must prove before it says a repository is healthy
Both are documented invariants -- ADR-0032 §8 promotes them, PRD §11 names the
tests -- and neither had one. A check added next month that phoned home or left
a state file behind would have passed the whole suite. Every later ticket in
this milestone rebuilds doctor's internals, so the fences go in before the
rebuild rather than after it.

The read-only fixture leaves a check failing on purpose. A run where everything
passes never reaches the remediation paths, and those are where a write would
most plausibly appear.

Writing the read-only assertion strictly found that the documented wording is
false. A plain run touches `.git/commitlore/index.db-shm`, and `--fix` creates
that and `-wal`. Those are SQLite's bookkeeping for a WAL database -- opening
one creates them for readers as much as writers -- and they hold no committed
data. Making the run literally write-free would mean opening the index outside
WAL, trading a documentation problem for the concurrency one #420 was about. So
the test asserts the invariant that carries the meaning: `index.db` is
byte-identical and nothing else moves except those two sidecars, and #473 fixes
the wording rather than the code.

The socket fence stubs this process's own socket construction, which cannot see
a socket opened by a spawned git. Two shipping checks contact the remote that
way. PRD §8.1 has since been rescoped to say exactly that, and the header
records which half this file can prove.

Limit: the socket stub covers this process only, so a check that shelled out to curl would pass it -- the fence is Node-level construction, not egress
Ruled-out: asserting zero writes literally, as §8.2 was worded | it would have failed on SQLite sidecars that carry no data, and the honest repair is the sentence, not the database mode
Ruled-out: a fixture where every check passes | the remediation paths are where a write would appear, and a green fixture never reaches them
Warn: the write inventory compares size and mtime, so a same-size rewrite inside one filesystem timestamp tick would slip through; a content hash would close it at the cost of walking the index on every run
Blast: local
Undo: easy
Certainty: firm
Verified: three cases -- a plain run over a fixture with a failing check leaves index.db byte-identical and touches nothing but the sidecars, every check completes with net.Socket/connect/createConnection and dns.lookup throwing, and --fix adds only remote.<name>.fetch config keys
Provenance: authored
Record-Id: r-docinv461
Fence doctor's read-only and no-network promises before rebuilding it
…nting severity

The row was seven fields. No category, so nothing could ever select a
subsystem; no severity, so ordering findings meant re-deriving it wherever
someone needed an order; no evidence slot, so a check stated its conclusion
with the observation interpolated into prose or dropped. `skipped` carried its
reason as free text, which is a skip nothing can act on.

Severity is now a total function of status computed inside the one factory, and
absent from its parameter list -- passing one is a type error rather than a
discouraged habit. Two axes that can disagree make every consumer resolve the
disagreement; deriving at a chokepoint makes the inconsistency unrepresentable.
`skipped` maps to info rather than warning, because a check that could not run
reported nothing, and giving it a warning's weight is how a report starts
ranking its own blind spots above its findings.

Category is a required positional argument rather than a lookup keyed by id.
The lookup was simpler and wrong in a specific way: a check added later would
silently take a default. This makes omitting one fail to compile.

The instrument landed before the change, which is the part worth keeping. It
caught the very first edit -- and the failure was the snapshot's own, not the
report's: mkdtemp's random suffix was unnormalised, so it varied per run. A
snapshot that fails for reasons unrelated to what it guards is deleted within a
week, so the normaliser was fixed rather than the expectation regenerated
blindly.

Migrating the sixty-one call sites took two attempts. The first rewrote
positional calls into object literals, converted fifteen, and raised the error
count from sixty-one to two hundred and forty-two; it was reverted whole rather
than repaired.

Limit: evidence is `{}` on every row until the ticket that populates it, so the field exists and proves nothing yet
Ruled-out: deriving category from a table keyed by check id | it is less code and gives a new check a silent default, which is the failure mode this milestone exists to remove
Ruled-out: mapping `skipped` to `warning` | a check that could not run has reported nothing, and weighting a blind spot above a finding inverts what the report is for
Ruled-out: repairing the object-literal migration that broke | it had converted a quarter of the sites and quadrupled the error count, and a half-applied mechanical rewrite is harder to audit than a fresh one
Warn: `skipReason` is still optional on the skipped overload; until the sites are mapped a bare skip is constructible, and the type will not catch it
Blast: local
Undo: easy
Certainty: firm
Verified: 102 files and 2,254 cases pass; the text snapshot and the check-order snapshot are unchanged across the model change, every v1 JSON key is present with its type on every row, severity agrees with status on every row of a full run, and the two rows that deliberately clear needsAttention still do
Provenance: authored
Record-Id: r-checkmodel
runDoctor was a hand-written array of thirteen ad-hoc functions. A throw in any
of them killed the command, so the user who most needs a diagnosis -- the one
whose repository is in a state some check did not anticipate -- got a stack
trace instead of the other twelve answers. Nothing was timed, so §10's budget
was an assertion with no instrument, and there was no data structure a filter
could ever read.

Checks are registry entries now. A throw becomes one failed row carrying the
message's first line as evidence, and the rest of the report still renders.
Every row is stamped from a monotonic clock, because a wall clock can go
backwards and a negative duration is worse than none.

One ordering fact drove the design. `commit-msg-hook` consumes `hook-runtime`'s
result but presents before it, so the dependency runs backwards against the
report order and cannot be satisfied by running earlier entries and reading
their output. Memoising the computation keeps "each check runs exactly once"
true without reordering anything a user reads. That edge is deliberately not
declared as a dependency: §2 admits only earlier entries, and declaring a
backward edge to make the data look complete would put a lie in the structure
the fix plan will later walk.

Effect injection is not here. §2.3 names five effects and only the clock is
injected; threading the rest through thirteen checks touches all of them, and a
half-injected context is worse than none because a test that stubs it would
then exercise an arbitrary subset. Split to #476 with that reasoning.

Limit: the registry is data but nothing filters it yet, so the ordering guarantees are tested and unused until the --only ticket
Ruled-out: declaring commit-msg-hook -> hook-runtime now | the edge points backwards against report order, and a declared dependency that the emission order cannot satisfy is a lie in the structure the fix plan walks
Ruled-out: injecting git and spawn alongside the clock in this change | it rewrites every check while the text must stay byte-identical, and partial injection makes a stubbed-context test cover an arbitrary subset
Ruled-out: letting a throwing check abort the report | the twelve remaining answers are worth more than the one missing, and the containment row says which subsystem went unreported rather than implying it passed
Warn: containment turns a crash into a `fail`, so a check that throws on every run now reports a steady failure rather than an obvious one -- the evidence line is the only thing distinguishing them
Blast: local
Undo: easy
Certainty: firm
Verified: 102 files and 2,259 cases pass with the text and check-order snapshots unchanged; ids are unique and kebab-case, every category is populated, every declared dependency resolves to an earlier entry, every row carries a whole non-negative durationMs, and a check forced to throw yields one fail row with its first message line while the other twelve still render
Provenance: authored
Record-Id: r-registry463
CI disagreed with the local run on two rows, and the disagreement was the
interpreter's path: nvm under a home directory here, hostedtoolcache under
/opt on the runner. The report was identical; the snapshot was a record of
where it was first generated.

This is the second time this instrument has failed for a reason that has
nothing to do with what it guards -- the first was mkdtemp's random suffix.
Both are the same mistake in different clothes: normalising the values a
reader cares about and leaving in the ones that vary per machine. A snapshot
that cries wolf gets deleted, and this one has to survive twelve more tickets.

Limit: normalisation is a list of known-variable things, so the next machine-specific value to appear in a detail string will fail once before it is added
Ruled-out: dropping the two rows that mention the interpreter | they carry the commitlore.bin and commitlore.node pairing, which is exactly what #382 and #71 were about
Warn: the node path is replaced by exact match, so a detail string that renders the interpreter differently -- relative, or through a symlink the check did not resolve -- would slip past and reintroduce the drift
Blast: local
Undo: easy
Certainty: firm
Verified: the regenerated snapshot contains no home directory, no nvm path and no hostedtoolcache path; thirteen cases pass locally
Provenance: authored
Record-Id: r-snapnode
Give doctor a check model and a registry, and stop one throwing check taking the rest
200 rows, 100 per arm, seeds 11 through 20, produced by one harness at 788a9db
against one model. Section 6 exposure is complete on all 200. No row stopped
with an error.

The 44 rows that did not reach a natural stop are kept rather than dropped: 40
over-turns and 4 over-tokens. Deviations 1 and 2 already oblige the verdict to
report truncation per arm, and a shard that quietly excluded its truncated rows
would make that obligation unmeetable.

This is deviation 3's second replacement shard. The 400 rows lost to a temp
reaper are now 400 rows re-run and committed, which is also why this lands the
moment the shard finished instead of at the end of the study.

The registered analysis still refuses: seeds 55-58 are outstanding, and it says
so by name rather than by computing a table on 1,080 of the registered 1,160.

Limit: this shard proves nothing on its own -- it is 200 of 1,160 and the stopping rule is the only thing entitled to read them together
Ruled-out: dropping the 44 truncated rows to make the shard look clean | deviations 1 and 2 require per-arm truncation in the verdict, and a shard that excludes them cannot support that line
Ruled-out: holding all shards and committing once at the end | that is the practice that lost 400 rows to a temp reaper, and the fix was durable storage per shard rather than more care
Warn: 22% of this shard is over-turns against 7% in the first re-run shard, so the per-arm truncation split matters more than it looked when the deviation was written
Blast: local
Undo: easy
Certainty: firm
Verified: 200 rows, cond split 100/100, exposure complete 200/200, one harness commit and one model across the shard, and the registered analysis refuses by naming the missing 55-58 file rather than computing on a partial set
Provenance: authored
Record-Id: r-m5shard2
An agent handed the repository's active records re-proposed a ruled-out
approach in 16 of 580 runs. Without them, 109 of 579. Fisher exact two-tailed
p = 0.0000, difference -16.1pp, Newcombe 95% -19.6 to -12.7. The registered
threshold was 6.6pp.

Appendix A.2 predicted the direction and got the size badly wrong. It argued
for an effect smaller than the threshold on three grounds -- that no arm passes
--trusted-author so the treatment is its weakest form, that ROUTE-GAP records
treatment runs implementing what the injected block ruled out, and that the
truncation imbalance shrinks the gap. All three were conservative, and the
effect is 2.4x the threshold anyway. The note on how the prediction could fail
guessed a control base rate above the 23.3% planned against; it came in at
18.8%, below it, so the size is not explained by the explanation that was
offered for it in advance.

The truncation asymmetry is reported per arm because deviations 1 and 2 require
it, and it points the same way: the control arm truncates at 28.5% against
21.2%, truncation suppresses re-proposal, so it removes control chances rather
than manufacturing treatment ones. 16.1pp is a floor with respect to that
artefact.

What the number does not cover is stated where a reader meets the number. Every
record rendered `[claim]` because no arm passed --trusted-author, and the
payload's own legend told the agent not to act on it as an order. The
`[directive]` tier became reachable only after this run. One model, one
harness, ten constructed fixtures, and an oracle that reads the final tree
rather than establishing that anything was read.

Limit: this measures `[claim]`-graded delivery on one model and ten synthetic tasks, and says nothing about the `[directive]` tier that 0.7.0 made reachable
Ruled-out: reporting the difference without the per-arm truncation split | deviations 1 and 2 exist because the arms truncate unequally, and a table that hides it invites the reader to assume they did not
Ruled-out: dropping the excluded error row from the account | one row was excluded by the registered rule, and a verdict that mentions 1,160 without mentioning the exclusion is describing a different denominator
Warn: the effect is large enough that the obvious next question is whether it survives tasks nobody designed to be discriminative, and these ten were designed to be
Blast: local
Undo: easy
Certainty: firm
Verified: 1,160 of 1,160 registered rows, exposure complete on 580 and 579 with zero failures in both arms, one harness and one dist digest across every shard, 80 original cells superseded by re-runs through a rule in the analysis code, and the seven shard production windows printed with re-runs marked
Provenance: authored
Record-Id: r-verdictm5
Report M5: 2.8% against 18.8%, and a magnitude prediction that was wrong
…ered

The README carried a sentence admitting the central question was open: whether
an agent handed a decision acts on it. That sentence has been true since the
first release and is now false, so it is replaced by the measurement rather
than left standing beside it.

2.8% against 18.8% across 1,160 registered runs. The three things that make the
number worth reading go with it: the 6.6pp threshold was registered before the
run, the preregistration predicted a smaller effect and was wrong, and the
control arm truncated more, which removes control chances rather than
manufacturing treatment ones.

Three limits sit in the same paragraph as the number rather than a page away.
Every record rendered `[claim]` and the payload told the agent not to act on it
as an order, so this measures the weaker of the two tiers and the stronger one
became reachable only in this release. It is one model, one harness, ten
constructed fixtures. And the oracle reads the final tree: it shows that agents
which received records re-proposed less often, not that any of them read
anything.

Limit: the README now leads its behaviour claim with a `[claim]`-tier number while shipping a `[directive]` tier nobody has measured, and that gap will widen until something measures it
Ruled-out: putting the limits in docs/evidence.md and linking them | a reader who stops at the table has then read a claim without its scope, and the scope is what makes the claim survivable
Ruled-out: rounding 6.7x into the headline | the ratio is arithmetic on two small counts and moves fast with either; the two rates and the interval are what the study actually bounds
Warn: the sentence this replaces was the honest one for four releases, and its replacement will need the same treatment the day a task set nobody designed for discrimination gives a different answer
Blast: local
Undo: easy
Certainty: firm
Verified: 82 cases across the readme, compatibility-matrix and manifest suites pass, and the changelog suite accepts the entry
Provenance: authored
Record-Id: r-readmem5
Ten sites reported `skipped` and every one said why only in prose. A consumer
that needs to tell "we did not look" from "we looked and it is fine" -- the
distinction the whole model exists to make structural -- had nothing to match
but sentences the next release is free to reword.

The mapping is the ticket's table, detail strings byte-unchanged. The factory's
skipped overload now requires a reason, so a bare skip does not compile.

Tightening the overload found a live gap the prose had hidden.
`commit-msg-hook` inherits `hook-runtime`'s status, and the compiler pointed
out that a skipped runtime would make the inherited row a skip with no reason.
`hook-runtime` has no skip site today, so the branch is unreachable -- and it
is written out rather than cast away, inheriting the runtime's own reason,
because adding one later must not be able to produce a reasonless skip here
silently.

Limit: the union has six members because ten sites needed six, and the next check that skips will need a seventh rather than one of these stretched to fit
Ruled-out: a catch-all `other` member | it is the free-text problem with a type annotation, and a consumer branching on `other` is back to reading the detail string
Ruled-out: casting the inherited status to satisfy the overload | the cast compiles today and lies the moment `hook-runtime` gains a skip site, which is exactly when nobody would be looking
Warn: `nothing_applicable` covers two squash-conservation conditions that differ -- no candidate branch, and candidates that recorded nothing checkable -- so a consumer cannot tell them apart without the detail
Blast: local
Undo: easy
Certainty: firm
Verified: 103 files and 2,265 cases pass with the text snapshot unchanged; every skipped row across two fixtures carries a union member, no non-skipped row carries the key, the serialized report contains no null skipReason, and an unborn HEAD maps to unborn_head end to end
Provenance: authored
Record-Id: r-skipreason
The README gate rejected the first version of this change, and it was right to.
It carried a p-value, a percentage-point figure and a Fisher result typed into
prose, beside a generated block that CI regenerates and byte-compares. A
statistic retyped into a paragraph drifts from the log that produced it, which
is the precise failure the gate exists to prevent -- and the generated block is
worth nothing if the paragraph above it carries a hand-written one.

The two rates and their counts stay, because they are what the claim is; the
significance test, the interval on the difference and the registered threshold
move to bench/VERDICT-M5.md, and the README says why they live there.

The generated block itself still publishes M4. Repointing it means declaring
seven shards with a status note covering supersession, the excluded row and the
truncation split, in the register the M4 note set -- deliberate machinery, not
a config value, and not something to change between a CI failure and a tag.
Filed as #480.

Limit: the README's behaviour claim and the generated block below it now describe different studies until #480 lands
Ruled-out: repointing README_SOURCES at M5 inside the release | its own comment explains why the declaration is deliberate, and writing that note in a hurry would produce exactly the careless publication it was built to stop
Ruled-out: dropping the rates too and linking everything | the two rates are the claim, and a README that states a behaviour result without its magnitude sends every reader to a second document to learn what was measured
Warn: the gate matches `p =`, a `pp` figure and `Fisher exact`, so a future statistic in another shape -- an odds ratio, a chi-square -- would pass it and reintroduce the drift
Blast: local
Undo: easy
Certainty: firm
Verified: 30 cases across the readme and readme-numbers suites pass, including the stray-statistic scan that rejected the first attempt
Provenance: authored
Record-Id: r-numgate
Give every skip a reason a machine can branch on
Put the measured number where the README said the question was unanswered
Eighteen version pins move together because the readme suite asserts they
match package.json: the two manifests, and four READMEs carrying the install
one-liner, the pinned `sh install.sh` invocation, the `git clone --branch` and
the inspect-first variant. Splitting them across commits leaves the repository
in a state where the documented install points at a tag that does not exist.

`dist/` is unchanged. The CLI reads its version at runtime, so the built tree
is byte-identical to the one every M5 row was produced against.

The release carries the first measurement of the thing the product is for. An
agent handed this repository's active records re-proposed a ruled-out approach
in 16 of 580 runs against 109 of 579 without them. It also carries the change
that makes the number describe the weaker case: `[directive]` was unreachable
on every install that has ever existed, and became reachable here, after the
run that measured `[claim]`.

Limit: the README's behaviour claim now rests on M5 while the generated numbers block beneath it still publishes M4, which is #480 rather than a release-time edit
Ruled-out: bumping the manifests first and the README pins after | the readme suite pins them to package.json, so the intermediate commit is one where CI is red and the documented install is wrong
Ruled-out: rebuilding dist for the version change | the version is read at runtime, and a rebuild would move the digest every M5 row records without changing a byte of behaviour
Warn: between the promotion merge and the tag push, every README install one-liner points at a tag that does not exist -- tag promptly and confirm the URL rather than assuming
Blast: system
Undo: easy
Certainty: firm
Verified: 103 files and 2,265 cases pass at this version, no v0.6.0 pin remains in any README, both manifests read 0.7.0, dist has no diff, and the readme, changelog, readme-numbers and compatibility-matrix suites accept the entry
Provenance: authored
Record-Id: r-rel070
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

CommitLore — record lint

Trailers: clean — 137 commits in origin/main..c69d3e8afa644d36d2f359f823d76e11c0b1d773
Active constraints: 271 limits · 602 ruled-out · 172 warnings — from 310 records over 222 changed paths

Active constraints for the paths this PR touches

Limits (271)

  • r-rel070 d4a4d8b — the README's behaviour claim now rests on M5 while the generated numbers block beneath it still publishes M4, which is The README's generated numbers block still publishes M4; M5 measured the thing the README leads with #480 rather than a release-time edit
  • r-numgate b770054 — the README's behaviour claim and the generated block below it now describe different studies until The README's generated numbers block still publishes M4; M5 measured the thing the README leads with #480 lands
  • r-skipreason 85aa8d6 — the union has six members because ten sites needed six, and the next check that skips will need a seventh rather than one of these stretched to fit
  • r-readmem5 6d04c0b — the README now leads its behaviour claim with a [claim]-tier number while shipping a [directive] tier nobody has measured, and that gap will widen until something measures it
  • r-verdictm5 29779a1 — this measures [claim]-graded delivery on one model and ten synthetic tasks, and says nothing about the [directive] tier that 0.7.0 made reachable
  • r-m5shard2 d7e1e3e — this shard proves nothing on its own -- it is 200 of 1,160 and the stopping rule is the only thing entitled to read them together
  • r-snapnode 2ef8745 — normalisation is a list of known-variable things, so the next machine-specific value to appear in a detail string will fail once before it is added
  • r-registry463 ddf5592 — the registry is data but nothing filters it yet, so the ordering guarantees are tested and unused until the --only ticket
  • r-checkmodel 9cbed57 — evidence is {} on every row until the ticket that populates it, so the field exists and proves nothing yet
  • r-docinv461 2edf2ad — the socket stub covers this process only, so a check that shelled out to curl would pass it -- the fence is Node-level construction, not egress
  • r-adr32rev 6955e81 — documents only -- src/ and test/ are untouched, and the two checks this revision specifies (notes-availability, capture-liveness) do not exist yet
  • r-adr32doc f35921e — this lands the model and the specification only; no check changes behaviour and the shipping report is byte-identical until the registry work begins
  • r-doctorpend 458bcec — the check reads staleness, so a capture whose base commit is still HEAD reports ok even if it has been waiting long enough that nobody remembers preparing it
  • r-probemodel 6966cb3 — pinning to sonnet means CDEB will say nothing about whether a stronger model needs delivered decisions less, which is a question worth its own study rather than a variable inside this one
  • r-runprobe 6a368ec — the gate screens runtime and says so -- completed means the process returned, which a no-op satisfies, and the pilot's 4.9x within-cell spread means two probes cannot bound the tail
  • r-delivchk 59ba928 — the runtime probe still has no implementation, so a freeze cannot yet be written even though the schema now demands its evidence
  • r-v13fix f4b6859 — schemas and the verifier now require the qualification evidence, but nothing yet produces it -- CDEB-10 must build the probe and the deliverability check before a freeze can be written
  • r-cdebv13 e0bd61a — the calibration is measured on disposable tasks, so it estimates overhead on work that is not the corpus and a corpus with different injection density will sit off it
  • r-clog070 172fa3d — the entry stays under ## Unreleased and names no version, because the version bump belongs to the release commit and a changelog that pre-announces a number can be wrong about it
  • r-cdebpres e01b482 — one repository, four tasks, six usable runs per arm and a local evaluator -- no claim gate is evaluable and preregistration §8 forbids any number here reaching a public surface
  • r-audit343 f2d2cc2 — the remaining entries were verified against issue titles, source comments or this session's own work, so an entry whose defect I did not personally see is only as accurate as its title
  • r-readmecold 08efdff — only README.md is reordered, so the ko, ja and zh-CN readers still meet the evidence first until the follow-up lands
  • r-selfaudit cd0068f — the page is maintained by hand, so an entry can go stale against the code it describes; the closing line says so and asks for an issue when it does
  • r-cdebseal 6cb710c — the snapshot is pinned to one sha in the runner, so a pilot re-run after dev moves measures the old tree until that constant is changed deliberately
  • r-cdebp01 8787e89 — one repository, four tasks, sixteen runs, and a local evaluator -- no claim gate is evaluable and no number here may reach a public surface
  • r-trust415 a030e93 — this changes what a fresh install delivers, so M1 and M5 remain measurements of [claim]-graded delivery and their numbers do not transfer to the directive path
  • r-cdebmat02 8080420git bundle create --all includes refs/notes only by configuration, so the notes ref is passed explicitly -- a bundle that silently dropped the mirror would materialize an OFF arm by accident
  • r-cdebver01 ce7b278 — the schemas freeze protocol 1.2.0 constants -- thresholds, matrix size, category names -- so a protocol change is a schema change and CI notices
  • r-cdebprd12 c3fefd2 — CDEB measures the shipping trust configuration as it exists, which grades every record claim; a directive-path result requires a different study under a different product digest
  • r-m5set441 e9b4b5f — the registered run spans shards produced days apart against a sonnet alias that is not pinned to a build, so the production window is part of the result and not an aside
  • r-m5shard1 a06ebecbench/runner.ts prints an outcome on every console line, so a faithful copy of its stdout renders results in plain text
  • r-m5slip2 87b6bfbbench/runner.ts prints reproposed= on every console line, so any command that shows raw log output shows outcomes
  • r-mcplife424 8cd3c6d — the tool registration that was lost belongs to the client, so nothing in this repository can detect the loss from inside a session or restore it
  • r-capmode30 40818c2stage cannot check consent, so auto records what is certainly true -- no prompt was shown -- instead of asserting what it cannot know
  • r-drafted30 b126176 — a commit message is immutable, so a drafted record is never upgraded in place -- promotion is a later record that Supersedes it, and that half is not in this change
  • r-plugupd1 bd4363b — the plugin cache belongs to the client, so nothing in this repository can update it or detect the version drift except by asking the running hook
  • r-amendid430 4c450ebcommit-msg gets no argument, environment variable or ref that distinguishes an amend from an ordinary commit
  • r-hookver433 a3b92d7 — nothing here can update anything -- the plugin cache is the client's, so the only move available is to say what is true
  • r-deltadb31 2d46b38 — nothing here has been measured against DeltaDB and there is no build to measure, so every row of the comparison is read off published descriptions and this repository's own numbers
  • r-binx428 b9d1ea8 — the allowlist accepts .mjs/.js paths, which are exactly the files most likely to carry a shebang and no execute bit
  • r-adr30x 25bf632 — a commit message is immutable without rewriting history, so a record cannot be upgraded in place and promotion has to be a superseding record
  • r-adr30x 25bf632X- extensions are defined as never interpreted by the core, so the unread signal cannot live in one
  • r-m5appa1 b36f12e — the runner prints reproposed= on every console line, so any progress check that reads raw log output sees outcomes
  • r-m5keep1 1f84e41 — the two lost shards had no copy outside a directory whose contract permits emptying it, so the rows are unrecoverable by any means
  • r-m5err4 5159b98 — seeds 55-58 produced 70 rows with no measurement, so the surviving data cannot reach the registered n without re-running them
  • r-recurse422 418734c — git runs pre-push on every push including one a pre-push hook makes, so any push from inside the hook must opt out of hooks explicitly
  • r-busy420 9555569 — a full rebuild on a large repository takes longer than any timeout a hook can afford to wait, so the scan fallback stays reachable by design
  • r-nograde415 bbba547 — no benchmark arm and no installed hook passes --trusted-author, so directive is unreachable in both and the two arms differ by the tag alone (No benchmark arm configures a trusted author, so the [directive] path the README's claims rest on has never been measured #415)
  • r-sync416 deb21d2 — git neither fetches nor pushes notes by default, so a mirror only moves when something configures or invokes it
  • r-mention408 ec314cd — paraphrase space is unbounded and this table cannot see semantic rewording, so blocked remains a speed bump and the grade remains the load-bearing control
  • r-schema406 22b2c35 — an index is derived state with no migration path, so a meaning change can only be handled by discarding the file
  • r-m6draft d82e057no-grade is inert on every existing fixture, so M6 cannot run on the task set M1 and M5 used
  • r-m6draft d82e057 — M6 has no prior compliance rate to size against, so n cannot be fixed before a base-rate pilot
  • r-note409 0dff3e4 — the notes ref is an ordinary ref with no signature requirement, so authorship there is a claim about who wrote the text and not proof of it
  • r-backfillclosed 00de5fa — the guard reads the mirror state at invocation, so a fetch completing mid-run is not observed
  • r-initunfetched 889d191 — it reports the state as it was before init ran, so a mirror fetched between the capture and the report would be named wrongly; that window is the four steps of one command
  • r-mirrorunread 8e4bdc5 — the caveat can only fire where notesAvailability returns unfetched, so a refspec added after cloning and never fetched through still builds silently -- the distinction config alone cannot carry, recorded on that function under r-fetchowed
  • r-committrig 1570580 — a phrase list only nudges host-side skill selection, so this widens the chance the skill is considered and guarantees nothing about when it runs
  • r-claimsmatch 506ada4 — this fixes the sentences an external reviewer found; no systematic pass was made over every claim in the four files against every published measurement
  • r-m5analysis 3450656 — the script enforces the row count, not the identity of the rows; a run that produced 1,160 rows under a changed harness would satisfy it, which is what harness_commit and dist_digest on each row are for
  • r-bootstrapgone bb0aff8 — the rule reads shipped scripts under scripts/, hooks/ and src/, and deliberately not docs/ or the READMEs, where npm appears in ADRs and comparisons and a guard would fire on prose
  • r-benchscope 67f4375 — nothing checks the shape of the eight metric-row files. This gate names them and steps over them, and bench/deterministic/types.ts is the only definition that family has -- there is no JSON schema for it, so drift on that side is still invisible
  • r-benchscope 67f4375 — the pre-provenance exemption reads started_at, which is data on the row rather than a fact about the file. A row that misreported it would be held to the shorter list of requirements; that is a deliberate falsification rather than the omission this fixes, and nothing here detects it
  • r-m5trunc 01f3478 — the rates come from 71 rows, so they indicate an imbalance rather than measure one; the verdict reports the final figures
  • r-nulsep 406dbdf — the sentinel is safe because nothing that writes into .git/hooks contains it, not because it is unrepresentable -- a hook body carrying the sentinel on its own line is the one input that could in principle collide, and the helper does not reject it
  • r-4e7c19 6d4e828turn_usage carries the provider's tokenizer; every figure in bench/TOKEN-LEDGER.md is in the product's own ceil(chars / 4) proxy. A future W4 must be reported beside the floor, never added into it, until both sides are on one tokenizer
  • r-4e7c19 6d4e828content_blocks says a turn called a tool, not which tool or on what — the tool_use block's name and input are discarded, so reproposed counts correct behaviour as failure — measure rejected work, not rejected mentions #141's rejected-path tool actions stay uncounted
  • r-pinskew 007ccbf — the comparison reads the package.json above the recorded path rather than running it with --version, so a pin whose manifest and bundle disagree is reported by its manifest
  • r-priorart 507ae24 — the comparison is against Lore's README and its abstract; the full paper was not read, so a lifecycle described only in the PDF would have been missed
  • r-scaleproof 4c093f2 — the 100,000-commit figures come from a synthetic repository built by the deterministic harness, not from a real codebase of that size, so they describe the index's shape rather than any particular project
  • r-extbaseline 064daf6 — the band is four Python repositories chosen for having enough revert history to backfill from, so it is evidence about large long-lived Python projects rather than about repositories in general
  • r-7f2b18 38fbc18 — no maintainer of these four repositories wrote a record, so §9.4 bounds a process nobody performed and is not evidence anybody would perform it
  • r-7f2b18 38fbc18 — zero superseded and zero expired records in the whole external corpus, so stale_delivered = 0 on every arm is arithmetic and not evidence about the lifecycle filter
  • r-7f2b18 38fbc18 — one template, one record shape, one decision class — reverts are the decisions loud enough to reach the history as an undo, and constraints, warnings and quietly-abandoned alternatives leave none
  • r-e1a7c4 653d1c3 — reverts are the one decision class an arbitrary repository declares mechanically — constraints, warnings and quietly-abandoned alternatives leave no revert and are invisible to this corpus
  • r-e1a7c4 653d1c3 — four repositories, all Python, all large and long-lived; nothing here will speak to a small or young repository or to another ecosystem's commit habits
  • r-3c9d52 dc9e769 — the sweep is two git log calls per path and the delivery phase runs git log --follow on every tracked path, so a full run over the four externals is hours rather than minutes on one machine
  • r-heroconsist 788a9db — the card text is 19-20 units, so at a 360-pixel mobile render it falls below the legible threshold; the headline and the alt text carry the message there, and the same content is in the Markdown below, but the card itself is decoration at that width
  • r-m5overturns 64a24cb — the 22.5% comes from 40 discarded rows, so it is an early indication of the rate rather than a measurement of it
  • r-m5dev1 1c9466d — the deviation note reports integrity figures from the discarded rows, which is defensible only because none of them is the registered outcome; a reader who disagrees should discount them entirely
  • r-tokenledger 298e47c — both sides are byte-derived token proxies under CHARS_PER_TOKEN=4, so the ratio cancels a uniform tokenizer error and does not cancel a differential one between prose and diffs
  • r-ledgerpin b0da937 — the ancestry check proves this checkout continues the history the read side was measured on; it does not prove nothing between the two commits changed how a diff reconstructs
  • r-ledgerresult bc31c90 — both sides are byte-derived proxies under CHARS_PER_TOKEN=4 rather than a provider tokenizer, so the ratio cancels a uniform error and not a differential one between diff text and prose
  • r-ledgerresult bc31c90 — break-even in reads assumes reads land on the evaluation set the way the delivery run's per-path average describes, and real editing concentrates on a few files
  • r-surfacedeliv fae9e1e — every figure in the table is measured on this repository measuring itself, which is the weakest part of the evidence and is stated in the paragraph rather than left for a reader to discover
  • r-rel060 e999b9d — the install one-liner in all four READMEs now points at a tag that does not exist until this is tagged, so the window between merging to main and pushing v0.6.0 is one where the documented install is broken
  • r-pipesplit b4fa571 — test/dogfood.test.ts validates every record in this history, so a new violation class is only available if it rejects none of the 620 Ruled-out: values already written
  • r-delivpairedresult e9332a6 — one corpus, one repository, one query strategy per route; this is delivery, which bounds recovery from above, and no agent was run
  • r-delivarms b3f5692 — the paired rows separate the cap from the mechanism only while both members read identical inputs, which is why the git pair shares one command's output rather than issuing two
  • r-delivharness ccbb75c — gold attaches a record to the paths its commit changed and follows renames, which is also how the shipped projection decides scope, so a high commitlore recall is partly definitional and only the budget, grading and index resolution can move it below one
  • r-gcunstageable 5cd6b8f — ADR-0021 fixes the pending format and stamps expires_at at stage only, so giving these phases an expiry earlier is a format change rather than a fix
  • r-gcunstageable 5cd6b8f — gc runs only when capture gc is invoked -- nothing schedules it, so a leaked file goes at the next run rather than at the 24-hour mark
  • r-gcunstageable 5cd6b8f — staleness is derived from base_head against HEAD; a transaction whose staged diff moved while HEAD did not is equally unstageable and is still kept, which is the conservative half of the same test
  • r-gcunstageable 5cd6b8f — a staged transaction that is never applied is still kept for ever -- the hook skips it once expires_at passes and gc protects the phase -- which is a separate leak this change deliberately does not touch
  • r-secondtie 998bf18 — committed_ts is %ct at one-second resolution and the index stores no ordinal that orders two commits inside one second, so a tie on that path can be made deterministic but never topological
  • r-dedupviol 18ad9c1 — the key includes line, so two detectors that locate one finding differently -- one with a line, one without -- would still print it twice; today both resolve the line through the same locateTrailerLines/lineForViolation path
  • r-readmesplit344 7314a03 — three checks bind content to a position in the README, so the complete record example, the protocol vocabulary table and the generated benchmark block could not move
  • r-owntmproot 6543870 — the demo still defaults to the shared tmpdir, so concurrent commitlore demo runs still create sibling directories there -- that is deliberate, and it is safe only because nothing now asserts over that namespace
  • r-asksbeforestage 81bc919 — stage accepts a verified nonce and nothing else, by ADR-0021's design against a replayed verification -- consent cannot be an argument to it, so the prompt can only live in the caller
  • r-asksbeforestage 81bc919 — the CLI's one-command form composes prepare, verify and stage in one process, so it still stages without asking; the skill says so and sends the asking path to the MCP tools
  • r-diffdefault 4ac8163 — the test reads the option string out of the source rather than out of --help output, so a change to how commander renders descriptions would not be caught
  • r-shallowlast 0913821 — the spawn still happens once per validate that has a dangling ref, which is the case where the answer is actually needed
  • r-exitonemeans 89f7af8 — a shallow clone cannot tell a reference that resolves below the boundary from one that resolves nowhere, so neither verdict is available and the check can only name the question it could not answer
  • r-failopen abc54ea — with the gate installed and no CLI resolvable, commits are still refused -- that is the one hook holding a verdict back, and this change does not reach it
  • r-notereach 1e72a28 — reachability is decided against HEAD alone, so a record mirrored onto a live branch that is not checked out is not served until it is
  • r-captureskill 6947cf9 — a skill loads into every agent's context, so a paragraph duplicating what the runtime prompt already prints is paid for on every session
  • r-heropolish f6144bc — README.ko.md still switches from 존댓말 to 해라체 below the hero; that split is older than this change and belongs to the restructure in README still carries the reference manual it should be linking to #344
  • r-pluginpath353 e364f3a — a plugin manifest has no way to add anything to PATH, so no plugin-side change can make the documented commands resolve
  • r-fetchowed 11f04b4 — config alone cannot separate a refspec that was fetched through from one that was only written, so the availability verdict cannot carry that distinction
  • r-guarddisclose 8a4d0c7 — a disclosure asserted by tool name covers the tool that is named, and the ADR's requirement is about every surface that exposes the behaviour
  • r-realoutput f9efea0 — a README block introduced as what the tool prints is a behavioural claim, and inventing its shape is the same defect as inventing a number
  • r-refspecfetch 936d206 — configuring a refspec is not fetching through it, and a state machine that conflates the two turns its own remedy into a way of hiding the problem
  • r-actionsleak a6fbb4b — a code path that no test and no first-party workflow exercises is the one an outside adopter takes by default, and its absence from CI is not evidence it is unused
  • r-actionsleak a6fbb4b — a trust label the caller must act on is worthless unless the value it describes is actually withheld at the point the data is built
  • r-release051 19810d2 — the hook is written at install time, so no release repairs a repository that already has one; every release touching hook behaviour has to restate what does
  • r-heroinherit 89b13ac — a headline that implies detection commits the product to guard's numbers, and guard is an advisory measured at 22% recall
  • r-convertreadme e12c816 — a README claim about the default workflow is only true if the shipped skill performs it, and the skill currently requires the user to name CommitLore first
  • r-fieldreport 753f4e7 — this section reports one engineer's day on one repository; it is evidence that the mechanism works there, not a measured effect size, and the wording has to keep those apart
  • r-readmefinal 40aeae0 — a mutation oracle anchored on a claim that can become false will silently stop testing when the claim is removed; the needle has to be asserted present
  • r-recordgate335 a83ebe3 — a denylist cannot decide whether something is a record, because the keys nobody has claimed are unbounded; that question needs the vocabulary, and the two must not be answered by one filter
  • r-recordgate335 a83ebe3Verified: in a release note is indistinguishable from Verified: in a record, and no context signal separates them without risking real records
  • r-draftfirst329 0506a5d — a usage error that names the wrong input costs an invocation and points the reader away from the fault; ordering is part of the message
  • r-release050 ad402c7 — the hook is written at install time, so a corrected release never reaches a repository that already has one; every release fixing hook behaviour has to say what repairs an existing install
  • r-uninstall1123 4ddac0d — the installers write five agent configs, not the four the ticket's measured inventory lists; the fifth is Windsurf at .codeium/windsurf/mcp_config.json
  • r-uninstall1123 4ddac0d — opencode's entry is shaped differently from the other three -- the command is an array -- so one recogniser cannot serve all of them
  • r-uninstall1123 4ddac0d — a checkout is 1366 files at this head, not the 1206 the ticket measured at 6e1d46d; any assertion bound to that count is stale
  • r-winsupported1124 6333251 — repositories that installed the hook before Windows: the commit-msg hook hangs instead of returning, and #71's containment can never match there #321 keep the old stub and must re-run commitlore hooks install; a corrected release does not reach them, and this row's claim is about a working install
  • r-winderive1124 282693b — a diagnostic that retypes the code it describes goes stale silently, because nothing fails when the two drift apart -- it has to be read out of the artifact that ships
  • r-winbound1124 156deed — an unbounded hang is not evidence; it is a job that dies at the runner timeout having printed nothing, so every hook-invoking commit here has to carry its own bound
  • r-winassert1124 616005d — an assertion whose only oracle is an absent side effect cannot distinguish "refused" from "the mechanism never worked", so each one needs a positive control that fires before the attack
  • r-winshell1124 aa68a9a — a GitHub Actions bash step is invoked as bash -eo pipefail, so set -uo pipefail inside the step does not clear -e and any bare command that fails ends the step at that line
  • r-winpath1127 bdf4ac0 — the stub is written to .git/hooks at install time, so a repository installed before this fix keeps the old text and must re-run commitlore hooks install; installing a corrected release is not enough
  • r-winpath1127 bdf4ac0${dir%/*} returns its input unchanged when no separator remains, so a loop that tests for emptiness never terminates at a drive root
  • r-winpath1127 bdf4ac0 — neither dirname nor ${var%/*} finds a parent in a backslash-separated path; both answer .
  • r-compat1122 e7d8516 — a non-empty guard does not detect deletion; each table's row keys have to be asserted as a set or the statement can silently shrink to one row
  • r-compat1122 e7d8516 — substring comparison hides a narrowing -- ./ is inside ../ and Edit|Write is inside Edit|Write|MultiEdit|NotebookEdit -- so cells are compared as their rendered form
  • r-compat1122 e7d8516 — a sentinel containing \0 makes git treat the file as binary, which costs it diff, blame and log -p permanently
  • r-compat1122 e7d8516 — the plugin path needs bash, because scripts/commitlore-run.sh carries a #!/bin/bash shebang, and no install script checks for it
  • r-muslbullet1126 04ac181 — this ticket owns four bullets and not the tests that read the section around them, so a check that breaks here means a region was taken that was not allocated
  • r-ps1scope282 72f23df — the hook-runtime probe spawns /bin/sh, so it cannot report on a Windows hook at all until T-1124 changes it
  • r-ps1shell282 97735d6 — a step's shell key takes no expression, so a per-host matrix cannot select the interpreter
  • r-t1120nodeinst 14deeb4 — git and node are hard prerequisites now, so a host without them installs nothing and says which one is missing
  • r-t1110policy 9e7b37a — only a repository-local policy file is read -- PRD-F13 requirement 11 permits either one location or a stated precedence, and an ambiguous precedence is worse than a missing feature
  • r-gateb3rev a2e38b9 — the shipped install.sh downloads a platform asset, so no document may describe it as Node-only until the installer itself changes
  • r-rel041notes 71efe1f — 0.4.1 makes the installer honest about a verification it cannot complete rather than fixing the kill, so an upgrading user may still see the unverified message instead of a version
  • r-rel040pins b76c40b — the pin names a tag that does not exist until the tag is pushed; between this merge and that push the documented command refers forward
  • r-rel040notes 5d57a72 — the 26.3-point density gap quoted in the notes is measured at this head and will drift with merge volume; it is illustrative of the denominator problem rather than a stable figure
  • r-gcwiring f21f28e — the guard against this class is four CLI-level tests; nothing structurally prevents a future subcommand from colliding with a parent option again
  • r-flake221fix 2b21ed9 — checkInjectRuntime ENOENT does not block init
  • r-density183denom 62ee7a4 — density row must name both denominators; a rate without a named population is the defect class "check must state what it could not see"
  • r-lb0xl89a 236229e — the static contract uses explicit placeholder text for TRANSCRIPT and DIFF rather than omitting those sections, because the prompt text references them by name
  • r-c44a1edb 71f5197 — src/core/pending-gc.ts -- gc must never remove a staged or applied file regardless of expiry; T-1018 post-commit may still finalise them
  • r-0ll5sxk0 2853a22 — consumption happens after commit succeeds, exactly once; consuming earlier loses the record on failed commits, consuming twice lets one record attach to two commits
  • r-t1009list 778638c — the list is still maintained by hand, so it will need editing for every future tool; that is the cost of catching an unintended one
  • r-t1009stage b5fcf4e — the nonce pattern check bounds what a caller can send, but a caller holding a valid nonce for its own repository can stage repeatedly until the record is consumed
  • r-t1005gates 15421c0 — policy identity is compared as a hash, so a policy edit that produces the same hash is indistinguishable from no edit
  • r-t1016svg 321c6f1 — byte-exactness is verified on this platform; a different platform's Node could in principle render differently, and nothing here proves it does not
  • r-t1006cli d22580b — the command composes the phases in one process, so a crash between verify and stage leaves a verified pending record that only garbage collection will clean up
  • r-t1008mcp ab00b54 — src/mcp/server.ts: readOnlyHint must be false for verify_capture — the tool writes verification results to the pending transaction
  • r-t1007mcp b6ef112 — commitlore_prepare_capture uses readOnlyHint: false because it writes a pending transaction
  • r-t1003verify7b 5e9f96b — Verification failure must never block a commit | the verify phase returns empty on any failure rather than throwing, because blocking a commit on an optional enrichment step is the feature people disable (ADR-0006)
  • r-t1013verbose205 294ec82 — --verbose only selects the formatter; it does not change runInit logic, step order, exit codes, or --json output
  • r-t1022sig e0c641d — the first pushed attempt asserted one header string in the test and built another in the formatter; CI caught the mismatch and the formatter was aligned to the asserted string, which is the one that states the measured figures on the output surface
  • r-t1022sig e0c641d — focused-test evidence for this change is CI's, not local; test/guard.test.ts reports zero tests and stalls on this machine at dev with no changes applied
  • r-t1024bc 023f6d9 — response shape is exactly five fields per CEO amendments and ADR-0020 confidence-separation constraint | adding a sixth field or letting context inherit guard_confidence violates the acceptance criteria
  • r-t1021known 8dfffc1 — the figures are measured against one archived 417-decision corpus, which is deliberately hard and is not deployment prevalence
  • r-t1011demo 1c0fc0c — the scene is one fixed pair of decisions, so it demonstrates the mechanism rather than measuring how often it matters
  • r-t1002prep 3e2c8c1 — the prompt contract is a string this phase emits; nothing verifies the agent honoured it until verify runs
  • r-t1020desc dd12b42 — the test asserts on the exact precision and recall figures; a future re-measurement changes both the description and the test
  • r-t1020desc dd12b42 — the first attempt's Record-Id used hyphens, which the r-[a-z0-9]{6,} format rejects; both the lint action and the dogfood test caught it
  • r-initresult204 ea4a08e — --verbose flag not wired yet (T-1013)
  • r-t1030diag 344ada0 — the heuristic uses a regex on the first line of stderr; an error that prints no stack frame and no "not found" string will be reported as cause unclear even if a human could classify it
  • r-pin030readme 504b54e — install.sh must already support tag-based download for the one-liner to work; verified that the URL resolves to a tagged tree
  • r-fix191amb cb94448 — the same-message test still passes by accident of collectRecords returning one record per commit; the divergent-notes test is what exercises the actual suppression path
  • r-fix187val 40f2436 — the tip-scan adds one full-history git-log call per range invocation; acceptable for a lint-time check but visible in benchmarks at scale
  • r-notes030 a289ca5 — the density denominator is named here and in the handoff, not in the harness that emits it, so the next run reproduces the same ambiguity
  • r-hero172a bc0d971 — Stale-exposure benchmark is one corpus, one query, and one pinned embedding model at a fixed two-record budget
  • r-bench161 b9b788e — digest fallback proves identical harness code at HEAD, not existence of the recorded commit
  • r-econ127a 40e197d — no task in the current 8-task pool passes Make benchmark task qualification two-sided (4–5 of 6) #109 qualification
  • r-dupsucceed 6f77fcf — supersession is resolved within one repository's history, so a record superseded in a fork that was never merged still grades as current here
  • r-dupsuccorder f46c02d — a successor before a later duplicate cannot resolve that later collision
  • r-dupsucc729 5a6b238 — published dev history cannot be rewritten
  • r-densitymerge135 490b2bb — the density-only path must not contend with a wall-clock benchmark
  • r-densityci135 e81cb1f — a published density result must be recomputed from its recorded source history in CI
  • r-cpaa134 f6f6d03 — the floor excludes the model's generation cost, so a reader comparing this against a hosted service's bill is comparing a part against a whole
  • r-valdup145 bcb9563 — the same-message check sees only the message, so two commits each declaring the same id separately are still caught by the reference check rather than here
  • r-convtrail150 57e89d2 — the denylist answers a different question from isRecordKey's allowlist, so a conventional trailer this protocol later claims would need removing from one and adding to the other
  • r-epipe2026 d9ee9ff — spawnSync may report EPIPE after git exits while its input pipe is being written
  • r-f61curve f05a3fc — the frozen 30-label corpus gives the archived 3/8 precision a 95% Wilson interval of 13.7%–69.4%
  • r-scale131 3f0f4a5 — production measurements retain every previously published fixture size
  • r-benchlock131 79f7ae2 — two runs launched with different node binaries or through a wrapper that does not name node in argv are still not detected
  • r-rejwork141 5f7af65 — the counts read the diff, so an agent that pursued a rejected approach entirely in conversation and committed nothing registers zero
  • r-doctorepipe 0420f5c — the new deterministic tests exercise evaluateInjectRun with a synthetic spawnSync result rather than forcing the live race, because no payload this check sends is large enough to make the write block deterministically the way an artificially large one does in the reproduction above
  • r-init107 f485f07 — the generated dist artifacts are rebuilt from TypeScript source
  • r-survsplit e73aed5 — path-reachability is measured against git's rename detection, so the figure moves with git's similarity threshold rather than with anything here
  • r-be140cost 8c01bd5 — no per-turn provider token ledger or observed avoided-work cost exists yet
  • r-noise142 aa3d1f1 — exposure uses a fixed-seed corpus of two applicable records and realistic unrelated records at five sizes
  • r-probepath 51f6446 — the probe still only runs a command it recognises, so a hand-edited but equivalent hook reports not-checked rather than a verdict
  • r-readme129 ab5f210 — the break-even rests on tokens estimated from bytes at the product's own four-characters-per-token constant, so it moves with that assumption
  • r-doctorprobe ed94491 — the probe runs only a command it recognises, so a user who hand-edits the hook into an equivalent but different form gets not-checked rather than a verdict
  • r-m4basis 5e2d2cb — the guard question stays unanswered until the exposure instrument is verified and M4 is rerun on it
  • r-m4withdraw e5f9b73 — the guard question is now unanswered rather than answered null
  • r-instpath119 9e1fce7 — a user who ignores the printed line still gets "not found" on the next command
  • r-readmeux1 b664205 — interactive record building does not exist, so the honest answer is still "an agent writes it or you do"
  • r-rel021a a79e350 — v0.2.0 remains on the remote with no release attached
  • r-expreadme1 9e69abe — bench/VERDICT-M4.md still cites the Fisher figure; the two disagree until the verdict records why the number was withdrawn from the README
  • r-expomerge1 d6ad014 — M4's existing rows have no exposure field and must read as unknown, not as not-exposed — backfilling by inference would erase the finding
  • r-f61a2c 9114cf0 — the matcher remains deterministic and lexical; no embedding or semantic service is available to distinguish paraphrases
  • r-exposure52 ba69411 — legacy JSONL artifacts predate model and guard-exposure fields | their absence remains unknown and is never inferred or backfilled
  • r-rdme96a 9c9371c — scripts/check-readme-numbers.mjs's withdrawal-notice and stray-statistic checks constrain what can appear outside the (absent, here) generated benchmark block — re-checked after every edit, not just at the end
  • r-init96a 913c7e3 — doctor's own exit-code contract treats warn as non-fatal by design (SPEC §10, commitlore-setup skill) — init deliberately diverges from it for its own summary, and that divergence is the one thing most likely to look like a bug on a future read of this diff
  • r-fix92dupid 7f41a6e — cross-references between two blocks declared by the same commit (a Follows:/Supersedes: naming a sibling block's id) are still reported as dangling rather than resolved against the sibling -- unchanged from before this fix, and called out in validate.ts's own comment as future work
  • r-fix93pkg 9c4a396 — package.json remains a development artifact (build, typecheck, dependency floor) -- it is not read as a distribution manifest by anything in this repository
  • r-relinstall c6e1d04 — never tested against the real GitHub release infrastructure (no release exists yet — that is the owner's action) — verified against a locally built SEA binary, a hand-made SHA256SUMS, and a local HTTP server standing in for GitHub's release-asset redirects, which is everything this repository lets a change verify before a tag exists.
  • r-distrace88 d118a73 — the fix insulates bench-ablation.test.ts from the race; it does not remove the underlying design (four test files independently, redundantly rebuilding one shared dist/ in their own beforeAll). A fifth file doing the same thing, or a future check elsewhere that also depends on dist/'s mid-run stability, can still race the same way.
  • r-parsemulti 6d39d25parse has no git-commit context (no sha, no notes mirror) — its identityCollision check is local to the one message being parsed and cannot detect a Record-Id that collides with something already committed elsewhere in history the way context's fold does.
  • r-multirec01 92aeb24 — parseRecordBlocks only recognizes a non-final block by its declared Record-Id, so an unidentified inherited record beyond the first stays recoverable in the plan that computed it but not in a later re-parse of stored text; squash-preserve orders unidentified blocks last so the common case (at most one) is unaffected.
  • r-multirec01 92aeb24 — multi-block reference checking (Follows:/Supersedes:) does not resolve one block's reference against a sibling block declared by the same commit; each block is still checked against every earlier commit in history.
  • r-issue59a1 21dd96c — three score bands over 8 firings is still a small-sample report; issue Guard fires wrong 5 times out of 8 — precision 37.5% at the shipped threshold #61 item 5 tracks corpus growth as a separate concern from this wording fix
  • r-exit065 e545dee — any new command's exit codes must be drawn from SPEC §10, not invented locally
  • r-fix70a1 d707fc7 — one encoding layer and explicit lexical forms in the four published languages; semantic paraphrases, nested encodings, and split payloads remain outside coverage
  • r-shwt66 5efa206 — git rev-parse --git-path may return a repository-relative path, so resolve it against cwd
  • r-merge66 40e7987 — Generated dist files were resolved only by npm run build and npm run bundle
  • r-fix760 fb8ba45 — Git remains the authority on trailer recognition; diagnostics must not loosen the parser
  • r-refint74 572f573 — validate cannot perform conservation checks because it has no before state
  • r-warn75 24c7cc8 — exit-code semantics remain owned by guard's exit 2 means blocked; everywhere else in the same CLI exit 2 means bad usage #65
  • r-shallow66 60a8659 — a depth-1 clone can only inspect its reachable commit history
  • r-doctor72 996bcde — generated dist artifacts must come from npm run build and npm run bundle, not a hand merge
  • r-fix067 a915af0 — PreToolUse hook failures must always exit 0 and never change stdout's hookSpecificOutput contract
  • r-fix063 0b8c496 — doctor performs remote probes; an unreachable remote reports could not verify instead of ok
  • r-det58p 1edccdf — deterministic results must identify a committed harness, corpus, binary, run, and machine
  • r-det058 695cdf6 — the suite must need no model, agent, network or uncommitted benchmark input
  • r-fix053 ecc4b90 — QueryResult.notes remains repository-level availability and is independent from whether one record was mirrored
  • r-fix055 43b40f8 — harvest-verify makes no model call, so semantic entailment is outside its contract
  • r-fix054 664d4e2 — notes-only metadata must survive folding; a mirror is one record, not two
  • r-fix056 55cb8bc — blocked output may retain only validated structural values that cannot carry prose
  • r-4c73a2 1d82a5bcode inherits the limit of any lexical surface — an alternative implemented under another name is invisible, one named in a string literal is counted; both were true of artifacts
  • r-7a3e91 cf859e4 — better-sqlite3 stays external because it is native — the bundle degrades to --no-index without it, which only works because r-6f2a08 made that load lazy first
  • r-6f2a08 4c2d432 — esbuild cannot follow createRequire(import.meta.url)('x'), so any dependency reached that way stays external no matter what the bundle config says
  • r-9c07e2 9c4d25a — the plugin still needs Node for the CLI — the protocol does not, but guard, the index and the MCP server do (T-706 · Bundle the CLI as a single file — run from a clone alone #38)
  • r-3b8f52 1f8b4be — the figure is one machine, one run — it establishes the order of magnitude, not a regression baseline anyone should tune against
  • r-3e8a41 2f0a8a0 — scoping is not implemented, so off-path records reach every arm
  • r-2f8b90 a376808claude -p inherits the operator's MCP servers, settings, CLAUDE.md, skills and hooks unless every one is explicitly disabled
  • r-9c2f74 d653153 — the ablation arms cannot discriminate on these fixtures -- no-grade and no-lifecycle are byte-identical to the treatment in 9 of 10 tasks, because the seeds carry one reconstructed record and one task with a lifecycle trailer between them
  • r-9c2f74 d653153 — the harness assembles its own projection rather than calling the shipped injector, so what is measured is the harness's rendering of the records, not src/core/inject.ts (issue B-08 · Replace the benchmark harness injector with the actual src/core/inject.ts #36)
  • r-4a8e15 49e12c7 — git's grammar requires a subject before a trailer block, so a serialized block is not by itself a parseable message
  • r-6e1a72 5e09846npx commitlore is the first thing a reader will try, and it fails until the package is published
  • r-0c5d38 aeb54a6 — the suite runs against the source tree, so no test in it can observe what packaging drops
  • r-8a2f61 1a5adec — hooks and CI branch on the exit code, so two failures sharing one code is a wrong answer, not an inconvenience
  • r-3f7a29 49817dc — reconstruction reads text written before the protocol existed, so the evidence is thinner than a harvest and the discard rate is expected to be high
  • r-2b8f45 0adcaf5 — a matcher that flags real work gets uninstalled, so the false positive rate is the binding constraint, not detection
  • r-9f3b16 6b13589 — git decides rename lineage by content similarity, so any claim about following renames is a claim about a threshold, not a guarantee
  • r-4e9c72 a7a7e26 — the index is derived, so nothing about its state can make the tool give a wrong answer -- only a slower one
  • r-6f2e58 ea9ae6d — a library test and a binary test cover different failures, and the packaging layer between them is exactly where a working module becomes a broken install
  • r-8c4a17 f2ab0c2 — Record-Id is single-valued, so a merge that inherits several records has no well-formed way to declare them in the message
  • r-8c4a17 f2ab0c2 — a verifier that accepts near-miss citations verifies nothing, so normalisation cannot grow past whitespace
  • r-5c8b31 60ddc39 — the agent CLI exposes no in-flight turn limit, so a per-task turn budget can only ever be observed with this driver
  • r-7e5f02 e5f5e00 — npm installs through an engine mismatch, so the ecosystem's own signal cannot be relied on to stop anything
  • r-3a9d68 6a3fc3b — a test runner reports what ran, and nothing in its summary distinguishes "did not run" from "does not exist"
  • r-0b7c44 d2b2ce3 — a command is only real once --help names it, because that is where users look before they read source
  • r-9a5e17 6d68703 — five workers on one repository share npm test and tsc, so file ownership alone does not prevent one worker from "fixing" another's half-written code -- verification scope had to be split too
  • r-6c2b95 5da793c — the CLI holds no API key, so any real driver runs through the user's own agent session and cannot be exercised in this environment
  • r-4d8a13 b1034ca — the same field carries two shapes, so any near-miss between them resolves to whichever branch is looser unless the looser one excludes the shape deliberately
  • r-8e2d51 ef93c0e — git is the parser, so a git version that folds or bounds trailers differently is a correctness problem for us, not a compatibility footnote
  • r-1f4b26 65f01c7 — this repository ran under two retired names, so its early records cannot validate against the current vocabulary and a whole-history check would be permanently red
  • r-7f0e39 76f3f2d — literal substitution only catches the exact strings you list, so the same term written with a different separator survives
  • r-5a8c04 c46a577 — git owns the definition of a trailer block, so any behavior we cannot get from interpret-trailers is behavior we must not invent
  • r-9d31b7 4ac6e30 — the example lives in four translated files, so any fix that is not mechanically enforced will drift again on the next edit
  • r-c0f4e2 3d249cd — npm gitlore is held by an active same-domain CLI, so the owner's first-choice name was not available
  • r-b2e7f1 00d348d — Parsing must delegate to git interpret-trailers -- reimplementing the block rules would drift from the rest of the git ecosystem
  • r-a8f3c1 ef48843 — Rename must land before any code exists -- after 27 tickets it would touch spec, fixtures, index, hooks and every doc

Ruled out (602)

  • r-rel070 d4a4d8b — bumping the manifests first and the README pins after | the readme suite pins them to package.json, so the intermediate commit is one where CI is red and the documented install is wrong
  • r-rel070 d4a4d8b — rebuilding dist for the version change | the version is read at runtime, and a rebuild would move the digest every M5 row records without changing a byte of behaviour
  • r-numgate b770054 — repointing README_SOURCES at M5 inside the release | its own comment explains why the declaration is deliberate, and writing that note in a hurry would produce exactly the careless publication it was built to stop
  • r-numgate b770054 — dropping the rates too and linking everything | the two rates are the claim, and a README that states a behaviour result without its magnitude sends every reader to a second document to learn what was measured
  • r-skipreason 85aa8d6 — a catch-all other member | it is the free-text problem with a type annotation, and a consumer branching on other is back to reading the detail string
  • r-skipreason 85aa8d6 — casting the inherited status to satisfy the overload | the cast compiles today and lies the moment hook-runtime gains a skip site, which is exactly when nobody would be looking
  • r-readmem5 6d04c0b — putting the limits in docs/evidence.md and linking them | a reader who stops at the table has then read a claim without its scope, and the scope is what makes the claim survivable
  • r-readmem5 6d04c0b — rounding 6.7x into the headline | the ratio is arithmetic on two small counts and moves fast with either; the two rates and the interval are what the study actually bounds
  • r-verdictm5 29779a1 — reporting the difference without the per-arm truncation split | deviations 1 and 2 exist because the arms truncate unequally, and a table that hides it invites the reader to assume they did not
  • r-verdictm5 29779a1 — dropping the excluded error row from the account | one row was excluded by the registered rule, and a verdict that mentions 1,160 without mentioning the exclusion is describing a different denominator
  • r-m5shard2 d7e1e3e — dropping the 44 truncated rows to make the shard look clean | deviations 1 and 2 require per-arm truncation in the verdict, and a shard that excludes them cannot support that line
  • r-m5shard2 d7e1e3e — holding all shards and committing once at the end | that is the practice that lost 400 rows to a temp reaper, and the fix was durable storage per shard rather than more care
  • r-snapnode 2ef8745 — dropping the two rows that mention the interpreter | they carry the commitlore.bin and commitlore.node pairing, which is exactly what doctor and hooks status report a stale hook pin as ok: commit-msg still bound to v0.5.0 after upgrading to 0.6.0 #382 and Security: commitlore.bin and COMMITLORE_BIN are executed, and the env path lacks the guard the config path has #71 were about
  • r-registry463 ddf5592 — declaring commit-msg-hook -> hook-runtime now | the edge points backwards against report order, and a declared dependency that the emission order cannot satisfy is a lie in the structure the fix plan walks
  • r-registry463 ddf5592 — injecting git and spawn alongside the clock in this change | it rewrites every check while the text must stay byte-identical, and partial injection makes a stubbed-context test cover an arbitrary subset
  • r-registry463 ddf5592 — letting a throwing check abort the report | the twelve remaining answers are worth more than the one missing, and the containment row says which subsystem went unreported rather than implying it passed
  • r-checkmodel 9cbed57 — deriving category from a table keyed by check id | it is less code and gives a new check a silent default, which is the failure mode this milestone exists to remove
  • r-checkmodel 9cbed57 — mapping skipped to warning | a check that could not run has reported nothing, and weighting a blind spot above a finding inverts what the report is for
  • r-checkmodel 9cbed57 — repairing the object-literal migration that broke | it had converted a quarter of the sites and quadrupled the error count, and a half-applied mechanical rewrite is harder to audit than a fresh one
  • r-docinv461 2edf2ad — asserting zero writes literally, as §8.2 was worded | it would have failed on SQLite sidecars that carry no data, and the honest repair is the sentence, not the database mode
  • r-docinv461 2edf2ad — a fixture where every check passes | the remediation paths are where a write would appear, and a green fixture never reaches them
  • r-adr32rev 6955e81 — keeping "SPEC forbids doctor exiting 3" | spec/SPEC.md line 274 names an unfetched notes mirror and shallow history as code 3's own examples, so the prohibition claim was false and saying so plainly beats softening it
  • r-adr32rev 6955e81 — degrading the aggregate on every non-optional skip | doctor.ts lines 628, 699 and 942 skip on ordinary healthy repositories, which made ok permanently unreachable
  • r-adr32rev 6955e81 — a capture-recency heuristic to close the produced-then-stopped case | SPEC line 155 makes a record per commit optional, so recent recordless commits are not evidence of breakage; the gap is named open in PRD §2.2 instead of guessed shut
  • r-adr32rev 6955e81 — dropping --only and --category as the review suggested | they are filters over registry data feeding the partial-honesty rule, not new code paths, and the disagreement is recorded in the ADR's Rejected section with that reason
  • r-adr32doc f35921e — a manual status, as the Swift reference has | it exists there for operator attestations the OS cannot query, and every CommitLore remediation is a command, so a status nothing emits is dead contract surface
  • r-adr32doc f35921e — the reference's 0/1/2/3 strict exit mapping | SPEC §10 fixes 2 as "could not run" and 3 as "could not see everything" for every command, and RELEASE-GATE §4 plus init's doctor --fix depend on warn exiting zero
  • r-adr32doc f35921e — per-surface profiles, for now | omitting checks to make a surface faster is the doctor reports a healthy install on a repository where every capture has been silently dropped for 8 days #458 shape, and --only/--category cover the subset uses until a measurement shows the full set is too slow
  • r-doctorpend 458bcec — lengthening the five-minute expiry | a staged record binds to the tree it was prepared for, and attaching it to a different tree is worse than dropping it -- the defect is the silence, not the expiry
  • r-doctorpend 458bcec — letting gc collect expired staged files | that erases the only evidence a capture was ever prepared, and this repository found the defect precisely because the files were still there
  • r-probemodel 6966cb3 — qualifying on opus while measuring on sonnet | the gate would screen runtimes the study never produces, and its threshold is derived from sonnet data
  • r-probemodel 6966cb3 — leaving the probe model unchecked and documenting the requirement | the mismatch produces timeouts that read as task difficulty, so a note cannot catch what a comparison can
  • r-runprobe 6a368ec — averaging the two arms | the study runs six sessions per task, so the slower arm is the one that decides whether they fit, and a mean lets the faster arm carry a task in
  • r-runprobe 6a368ec — keeping the probe artifact out of storage | the freeze manifest is required to carry per-task probe results, and a qualification nobody can recheck is not a gate
  • r-delivchk 59ba928 — qualifying delivery through commitlore context | budget, trust grading, the guard, lifecycle projection, the matcher and output parsing all sit between it and the agent, and the defect being fixed was zero delivery through exactly those
  • r-delivchk 59ba928 — requiring every good-control path to carry every record | a record scoped to one file of a multi-file change still reaches an agent that opens that file, and demanding all of them rejects sound tasks
  • r-delivchk 59ba928 — throwing on a non-zero injector exit | the hook is fail-open by design, so the exit code belongs in the freeze manifest where a reader can weigh it
  • r-v13fix f4b6859 — keeping the derived threshold with the exponent corrected | any threshold that moves with measured overhead is a threshold the study can influence, and the fixed bar is the only one that means the same thing before and after the run
  • r-v13fix f4b6859 — lowering 15% to something the pilot's overhead clears | that measures a different claim and calls it the same one; if 45% overhead makes the bar hard, the bar is reporting a true fact about the product
  • r-cdebv13 e0bd61a — raising the 15% to a number that looked achievable | picking a threshold to be clearable is the same act as picking one after seeing results, and only the derivation makes it preregistered
  • r-cdebv13 e0bd61a — keeping token efficiency in the conjunction with the new threshold | the objection was never the number, it was that one gate could erase a measured behaviour result while the report called it a three-metric study
  • r-cdebv13 e0bd61a — widening the hook matcher so the two silent tasks would deliver | that redefines a delivery failure as a delivery success, and §9.5 exists to record exactly that failure
  • r-clog070 172fa3d — grouping by conventional-commit type | the reason a reader opens this file is "what breaks and what is now possible", and feat:/fix: sorts by the author's vocabulary rather than by that question
  • r-cdebpres e01b482 — reporting the completed-only table as the pilot's result without the timed-out task | the exclusion is the registered stop_reason rule, and a reader who cannot see that a quarter of the study never finished cannot judge the rest
  • r-cdebpres e01b482 — dropping the two zero-delivery tasks as broken | they are the intention-to-treat case the protocol is built around, and they are also how the qualification gap was found
  • r-audit343 f2d2cc2 — deleting the No evidence that a fresh agent recovers the decisions a repository holds #343 entry rather than correcting it | it is one of the strongest entries on the page, and removing an item because I got a detail wrong is the failure the page exists to document
  • r-audit343 f2d2cc2 — fixing it silently | the page's whole claim is that this project publishes its false claims, and a silent edit makes that claim false too
  • r-readmecold 08efdff — cutting the evidence sections down to move them up | the caveats are the reason the numbers can be trusted, and a shorter honest section is a longer dishonest one
  • r-readmecold 08efdff — reordering all four languages in this change | it would mean moving text I cannot re-read for accuracy, and a mistranslated claim is worse than a badly ordered one
  • r-selfaudit cd0068f — generating the page from the issue tracker | the value is in the one-line account of what each defect cost, which no title carries, and a generated list of titles reads as a changelog
  • r-selfaudit cd0068f — leading with the shipped-bug section | those flatter, because everyone ships bugs; the false-claims section is the one almost no project publishes and the only one that is evidence about this project rather than about software
  • r-cdebseal 6cb710c — bundling --all and trusting the prompt not to mention other branches | the agent has a shell and git, and a seal that depends on the sealed party not looking is not a seal
  • r-cdebseal 6cb710c — keeping close() synchronous and retrying the rmSync | it hides a real shutdown-ordering bug behind a loop, in the exact code path MCP tools for commitlore vanish mid-session (ToolSearch returns zero results despite server reported connected) #424 now points at
  • r-cdebp01 8787e89 — running the 180-run confirmatory study now | its corpus requires five repositories with pre-cutoff natural records and none exists, so the study would either not run or run on a corpus built to be runnable
  • r-cdebp01 8787e89 — reusing bench/workspace.ts for materialization | its record-stripping mode is the §6.3 prohibited control, and CDEB-02's bundle path exists to make that construction impossible
  • r-trust415 a030e93 — dropping [directive] from the injected legend | that resolves the contradiction by deleting the tier that carries SPEC §7's security meaning, and the legend was not the thing that was wrong
  • r-trust415 a030e93 — trusting every committer reachable from HEAD | it reads as "more useful by default" and is exactly the attack -- a contributor who lands one commit could then write directives to a maintainer's agent
  • r-cdebmat02 8080420 — reusing bench/workspace.ts for materialization | its record-stripping mode is the §6.3 prohibited control, and a helper that can build the forbidden shape should not be in the path that exists to prevent it
  • r-cdebmat02 8080420 — comparing arms by bundle digest alone | both arms come from one bundle, so the bundle digest cannot catch a materialization that diverged after import; the §6.2 identity is computed from each working copy
  • r-cdebver01 ce7b278 — default-in discovery over the CDEB tree like the legacy gate | that gate's enemy was the unregistered file, CDEB's is the unregistered input, and bench:m5 reads every jsonl in bench/results, so the registered analysis would run over M1, M4 and files marked non-citable #441 showed a glob passing a stopping rule on contamination
  • r-cdebver01 ce7b278 — validating with the draft-07 Ajv entry and downgrading the schemas | the 2020-12 entry ships in the same package, and weakening six schemas to fit a meta-schema is backwards
  • r-cdebprd12 c3fefd2 — reopening any of the twenty locked decisions in §29 | v1.1 locked them deliberately and every v1.2 change is additive definition, disclosure, or leak-closure
  • r-cdebprd12 c3fefd2 — enabling capture in the ON arm for realism | an agent commit mid-run would stage records unattended and change repository state between repeats, destroying the same-history invariant the whole design rests on
  • r-m5set441 e9b4b5f — keeping the directory scan and filtering by filename prefix | every contaminating file here is a .jsonl under the same directory, and a prefix rule is one rename away from readmitting them
  • r-m5set441 e9b4b5f — deriving a seeds-51-54 file so the shard list is one file per entry | it makes a second copy of rows that already exist and moves the supersession out of code into an edit nobody reviews
  • r-m5shard1 a06ebec — committing the log verbatim | it hides nothing either way, since the rows carry the same values, and the rendered form is the tripwire Appendix A.1 records being hit twice
  • r-m5shard1 a06ebec — dropping the log entirely | it is the only artifact tying this shard to a run id, a token total and a wall-clock window
  • r-m5slip2 87b6bfb — relying on remembering not to print log lines | it had already been written down once and was broken anyway; the rule has to be carried by the command rather than by attention
  • r-mcplife424 8cd3c6d — reporting the loss from doctor by probing the MCP server | the server starts and answers on demand -- measured, 60 protocol requests and 24 tool calls with no crash -- so a probe reports healthy in exactly the situation being investigated
  • r-mcplife424 8cd3c6d — writing the log outside .git/ so it survives a clone | it is evidence about one machine's processes, and a path that can reach a commit is a path that will
  • r-capmode30 40818c2 — stamping drafted at stage instead of verify | verify is where accepted records are written, and stage would have to rewrite a payload whose hash it is simultaneously checking
  • r-capmode30 40818c2 — letting off draft a candidate and drop it | the transcript would be hashed to disk for a feature the repository declined
  • r-capmode30 40818c2 — overwriting provenance in suggest too | that mode's host may have asked, and marking its records unread would be as unsupported as marking them read
  • r-drafted30 b126176 — capping unattended records in the capture pipeline | the writer would own a rule the readers depend on, and any other host staging a record could simply not apply it
  • r-drafted30 b126176 — an X- extension trailer to carry the unread signal | SPEC §3 defines those as never interpreted by the core, so a grading rule reading one would license every reader to invent grading keys
  • r-plugupd1 bd4363b — treating the pinned 0.4.0 as a distribution defect | the marketplace advertises 0.6.0 off the default branch and a client fetched it on request; the defect was that no surface said to ask
  • r-amendid430 4c450eb — recording the amend in prepare-commit-msg for commit-msg to read | it is cross-hook state in the hooks, needing a marker keyed to HEAD so a stale one cannot suppress a real collision, and the last hook change made at speed hung every push
  • r-amendid430 4c450eb — excluding HEAD from the duplicate walk | it would pass a genuine divergent duplicate whenever the colliding record happened to sit on HEAD
  • r-amendid430 4c450eb — widening the rule to whatever §3.2 might mean by a lifecycle update | that needs deciding which content changes stay updates, and amend needs none of it
  • r-hookver433 a3b92d7 — comparing against the published release over the network | every other check in doctor reads local state, and a version check that needs the internet is one that fails on a plane and tells nobody why
  • r-hookver433 a3b92d7 — making it a fail | an older hook still delivers records, under older rules, which is worth saying and not worth refusing to run over
  • r-hookver433 a3b92d7 — folding it into doctor and hooks status report a stale hook pin as ok: commit-msg still bound to v0.5.0 after upgrading to 0.6.0 #382's pinned-CLI check | that one is about the commit-msg hook's recorded target, and merging the two would report one surface's staleness under the other's name

Truncated: 701 lines omitted — the comment hit GitHub's 65000 character limit.

Trailer violations fail this check. Active constraints are informational — they are what the repository already decided, not a verdict on this PR.

@MongLong0214
MongLong0214 merged commit 1ec6571 into main Aug 8, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant