Skip to content

Release 0.7.1 — the tier 0.7.0 announced, actually reachable - #486

Open
MongLong0214 wants to merge 19 commits into
mainfrom
dev
Open

Release 0.7.1 — the tier 0.7.0 announced, actually reachable#486
MongLong0214 wants to merge 19 commits into
mainfrom
dev

Conversation

@MongLong0214

Copy link
Copy Markdown
Owner

Promotion of dev into main for v0.7.1. Patch on the released 0.7.0.

Why this exists

0.7.0 shipped with its headline change non-functional. The review verdict on that promotion was FAIL and named the defect; the promotion had already been merged and tagged before the verdict arrived. That sequencing failure is recorded in #484 and in the CHANGELOG, not only here.

The defect, and the one the sweep found

commander declares --trusted-author with a default of [], so options.trustedAuthor ?? configuredTrustedAuthors(cwd) never fell through. Every record on every install still graded [claim] — the condition #415 was opened about, reintroduced one layer up by its own fix.

The first commit's Warn: said it repaired one site rather than the class. Sweeping the class found query.ts immediately: commitlore context reported claim for a record the hook rendered directive, while the comment at that line already asserted the two routes must answer alike. A comment asserting a property is not the property.

Verified against the released artefact

0.7.0   inject --path  → [claim]        context → [claim]
0.7.1   inject --path  → [directive]    context → [directive]

Six new cases spawn the built CLI. They fail against the 0.7.0 build, which is the only property that makes them worth having — the tests that passed drove buildInjection with hand-assembled options and never crossed the command line, which is the only path the hook uses.

Also

  • package-lock.json declared 0.1.0 while both manifests read 0.7.0. Stale since the first release.
  • The CHANGELOG corrects two miscounts in 0.7.0's own review evidence: 132 commits where the range held 137, and seven install checks where RELEASE-GATE.md §4 lists six. Both were mine.

Evidence at this exact head

head                  cb960d0e3af5c188cef3e1b27116d353b5ac30e6
CI (push)             success
Demo — PR lint        success
promotion-range-lint  success
full suite            103 files, 2,271 cases pass, 1 skipped

Sequencing for the tag, corrected

The verdict's second blocker was that the tag publishes before the post-tag fresh-clone gates, making publication fail-open. This time the release notes are not attached and the release is not announced until RELEASE-GATE.md §4 has run against a clone of the tag. The tag itself still auto-creates a release — that is the workflow, not a choice — so the gate runs immediately after the push and before any notes go on.

0.7.0 is not retracted

Its notes carry the defect at the top with a reproduction and a pointer here. Deleting a published tag breaks every clone that already fetched it.

Stated costs

Both routes now read git config on every invocation, including the hook on the hot path of every edit. One local config read, but new work in a place measured in milliseconds — for #472's budget to bound.

The sweep covered ?? against an options field in src/commands, the shape that produced both defects. An option whose default is consumed some other way would not have shown up.

MongLong0214 and others added 3 commits August 8, 2026 14:44
0.7.0's headline change made `[directive]` reachable and it reached nobody.
`commander` declares `--trusted-author` with a default of `[]`, so the flag
arrives as an empty array rather than undefined when absent, `?? ` passes it
through, and the fallback to the author `init` records is dead code. Every
record on every install still graded `[claim]` -- the condition #415 was opened
about, reintroduced one layer up by the fix for it.

Resolution is on length now. An explicit flag is always non-empty and an absent
one is always empty, whichever shape commander hands over, so the test matches
what the caller meant rather than what the framework happened to supply.

The tests that passed drove `buildInjection` with options assembled by hand and
never crossed the command line, which is the only path the hook uses. The four
new cases spawn the built CLI. They fail against the 0.7.0 build, which is the
only property that makes them worth having.

That file's header already carried the warning, one layer down: a unit test of
`gradeRecord` would have passed throughout the period the original bug existed.
The same sentence applied to `buildInjection` and I did not hear it.

`package-lock.json` declared 0.1.0 while both manifests read 0.7.0, stale since
the first release, and moves with them now.

The CHANGELOG carries two corrections to 0.7.0's own review evidence: the
promotion PR said 132 commits where the range holds 137, and seven install
checks in RELEASE-GATE section 4 where it lists six. Both were mine.

Limit: 0.7.0 stays published with its notes amended to name the defect at the top; retracting a tag people may already have installed trades a known-bad version for an unknown one
Ruled-out: deleting the v0.7.0 tag and re-cutting it | rewriting a published ref breaks every clone that already fetched it, and the release notes can carry the truth without that
Ruled-out: reading the flag with a nullish check and documenting the commander default | the documentation would sit in this file while the defect sits in every install, which is the arrangement that produced this
Warn: any option declared with a non-nullish default has the same shape, and nothing checks for it -- this fix repairs one site, not the class
Blast: system
Undo: easy
Certainty: firm
Verified: the released 0.7.0 artefact renders `[claim]` for `inject --path` and `[directive]` only with an explicit flag, the patched build renders `[directive]` for the same plain invocation, and the four CLI-spawning cases pass alongside the six that already existed
Provenance: authored
Record-Id: r-rel071
The first fix repaired `inject` and its own Warn line said it repaired one site
rather than the class. Sweeping the class found the second site immediately:
`query.ts` resolved trusted authors with the same nullish check against the
same commander default, so `commitlore context` reported `claim` for a record
the hook rendered `directive`.

The comment at that line already said the two routes must answer alike, or the
grade means one thing on the hook and another on the terminal. That sentence
was written before the fallback existed on either route, stayed true while
neither had it, and became false the moment one did. A comment asserting a
property is not the property, and nothing was checking.

Two cases now compare the routes directly, configured and unconfigured, and
they run the built CLI rather than the functions behind it -- the same reason
the first four exist.

Limit: the sweep covered `?? ` against an options field in src/commands, which is the shape that produced both defects; an option whose default is consumed some other way would not have shown up
Ruled-out: threading a cwd parameter into queryOptions to match inject | this route resolves the repository it was invoked in and has no cwd of its own, so a parameter would exist only to look symmetrical with a function that needs one
Ruled-out: deleting the comment now that a test enforces it | it explains why the routes must agree, which the test asserts but does not say
Warn: both routes now read git config on every invocation, including the hook on the hot path of every edit -- one local config read, but it is new work in a place that is measured in milliseconds
Blast: system
Undo: easy
Certainty: firm
Verified: against the same repository the released artefact was reproduced in, `context` and `inject --path` both render directive with a configured author and both render claim without one; twelve cases pass in the trusted-author suite
Provenance: authored
Record-Id: r-queryroute
Release 0.7.1: the tier 0.7.0 announced was never reachable
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

CommitLore — record lint

Trailers: clean — 19 commits in origin/main..ea66f50a16bfccfa6dd45abba526a37bd72a3c9a
Active constraints: 188 limits · 435 ruled-out · 114 warnings — from 221 records over 32 changed paths

Active constraints for the paths this PR touches

Limits (188)

  • r-pubprereq 8ffb31c — the gates qualify the commit and its CI, not the tag's authorship or timing -- anyone who can push a v* tag to a qualified commit on main can still publish, and expiring or re-pointing a tag after these jobs pass is outside what any of them observe
  • r-failclosed493 3216a54 — the job runs on ubuntu-latest only, so it qualifies the POSIX install; the Windows path is still covered by install-ps1 in CI against the branch rather than against the tag
  • r-vergate492 5b7a823 — the gate now covers every versioned manifest that exists today, found by searching for "version" across the tree -- a manifest added later is not covered until someone adds it here, which is the same standing every declaration in this repository has
  • r-plugres483 7dab3e3 — the gate now catches the mismatch at release time, and doctor's inject-version check catches it for a user, but nothing tells a user who has never run doctor
  • r-collapse466 d24a284 — only two edges are declared -- inject-version on inject-runtime, and the §2.2 checks on the capture chain -- so a dependency nobody wrote down still surfaces as an independent finding
  • r-m5sources b910dba — the seven shards are declared individually, so a shard added later is invisible to this block until someone lists it -- which is the property the declaration was built for and the cost that comes with it
  • r-prdwrites afd48df — the exception is named for SQLite specifically, so a future dependency that touches the filesystem on open would need its own line rather than being covered by this one
  • r-evidence465 e1a3c92 — evidence answers what was observed, not whether the observation was the right one to make -- index ingests any key: value line as a trailer; doctor reports 106 records where git has 0, and context serves commit subjects to the agent #335's wrong count would now be visible in a field rather than prevented
  • r-queryroute 4ae1f6f — the sweep covered ?? against an options field in src/commands, which is the shape that produced both defects; an option whose default is consumed some other way would not have shown up
  • r-rel071 af8e0ab — 0.7.0 stays published with its notes amended to name the defect at the top; retracting a tag people may already have installed trades a known-bad version for an unknown one
  • 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-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-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-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-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-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-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-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-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-binx428 b9d1ea8 — the allowlist accepts .mjs/.js paths, which are exactly the files most likely to carry a shebang and no execute bit
  • 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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-4e9c72 a7a7e26 — the index is derived, so nothing about its state can make the tool give a wrong answer -- only a slower one
  • r-7e5f02 e5f5e00 — npm installs through an engine mismatch, so the ecosystem's own signal cannot be relied on to stop anything
  • 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-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 (435)

  • r-pubprereq 8ffb31c — expressing either check as an if: on publish | a condition in YAML has no test, and a release gate whose logic cannot fail in a suite is a claim rather than a control
  • r-pubprereq 8ffb31c — treating a missing required check as nothing to report | that is the empty-set inversion above, and it is exactly how a release with no CI at all would have published
  • r-failclosed493 3216a54 — reusing the workflow's checkout instead of cloning | the row asks whether a fresh clone works, and a checkout is not the thing users get
  • r-failclosed493 3216a54 — making the stale-hook row non-blocking because its fixture also fails another check | the confounding finding is separable by reading the row, and a row excused for being awkward is the fail-open being repaired
  • r-vergate492 5b7a823 — globbing for JSON with a top-level version key | node_modules and generated fixtures carry that shape, and a gate whose scope moves with the working tree fails differently on a clean clone than on a developer's machine
  • r-vergate492 5b7a823 — warning on a missing manifest and continuing | the tag is immutable once fetched, so a release that publishes past an unreadable manifest cannot be corrected afterwards
  • r-plugres483 7dab3e3 — preferring CLAUDE_PLUGIN_ROOT over PATH | it reopens how ADR-0011's ship-dist-in-the-repo interacts with the installer's wrapper, and the node-resolution advantage that motivated the current order would need re-examining rather than reversing
  • r-plugres483 7dab3e3 — comparing versions inside the run script | it sits on the hot path of every edit and would spend a second process launch there to report a condition doctor already reports
  • r-collapse466 d24a284 — declaring commit-msg-hook on hook-runtime to complete the graph | that edge runs backwards against registry order, and a declared edge the emission order cannot satisfy is a lie in the structure the fix plan walks
  • r-collapse466 d24a284 — omitting a blocked row from the report and keeping it only in the JSON | the text report is what a user reads, and a row missing from it is indistinguishable from a check that was never run
  • r-m5sources b910dba — globbing bench/results for m5-*.jsonl | the file next to them is a withdrawn design log, and a glob is how the wrong dataset gets published without anyone deciding to
  • r-m5sources b910dba — reporting only the 1,160 and dropping the row count | the rows are on disk and a reader who counts them would find the block understating; naming both and the reason is what makes either checkable
  • r-prdwrites afd48df — opening the index outside WAL so the promise becomes literally true | it reintroduces the concurrency failure Concurrent hooks make half the injections fall back to a full scan, because no SQLite busy timeout is set #420 fixed, to make a sentence accurate
  • r-prdwrites afd48df — leaving §8.2 and scoping only the test | the document is what a reader checks the code against, and a test quietly narrower than the promise it implements is the arrangement this milestone exists to remove
  • r-evidence465 e1a3c92 — collapsing stdout and stderr to present or empty, as the reference does | it sanitises against paths and tokens it cannot inspect, while these checks diagnose from the first line and would lose the diagnosis
  • r-evidence465 e1a3c92 — leaving evidence optional on non-ok rows | a conclusion without its observation is the shape every defect above shares, and optional means absent in the row nobody looked at
  • r-queryroute 4ae1f6f — threading a cwd parameter into queryOptions to match inject | this route resolves the repository it was invoked in and has no cwd of its own, so a parameter would exist only to look symmetrical with a function that needs one
  • r-queryroute 4ae1f6f — deleting the comment now that a test enforces it | it explains why the routes must agree, which the test asserts but does not say
  • r-rel071 af8e0ab — deleting the v0.7.0 tag and re-cutting it | rewriting a published ref breaks every clone that already fetched it, and the release notes can carry the truth without that
  • r-rel071 af8e0ab — reading the flag with a nullish check and documenting the commander default | the documentation would sit in this file while the defect sits in every install, which is the arrangement that produced this
  • 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-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-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-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-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-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-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-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-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
  • r-binx428 b9d1ea8 — running the value through node instead of exec'ing it | this branch has no recorded interpreter to use, and inventing one would give an environment variable more trust than the recorded path gets, not less
  • r-binx428 b9d1ea8 — reporting the unrunnable value and exiting non-zero | a hook that cannot find its checker has nothing to reject, which is why the unresolved ending already exits 0
  • r-recurse422 418734c — guarding with an environment variable the hook sets and the command checks | it works, but it puts the fix in the caller when the hazard is in the push, and a second caller that forgets the variable reopens it
  • r-recurse422 418734c — moving the publish to post-commit to avoid the nesting | it was ruled out in The notes mirror is written locally and never leaves the machine, so a teammate's clone cannot see a record it holds #416 for adding a network call to an operation that had none, and a hang is not a reason to take a worse design
  • r-busy420 9555569 — a longer timeout that waits out a rebuild | the agent is blocked on this hook, and at 100k commits the rebuild is the same order as the 86s scan it would be waiting to avoid
  • r-busy420 9555569 — serialising injections behind a lock file | it makes every edit wait for the slowest neighbour, where SQLite already lets readers run concurrently under WAL and only writers contend
  • r-sync416 deb21d2 — pushing the mirror from the post-commit hook | it adds a network call and a failure mode to an operation that had neither, on a machine that may be offline and a remote the user has not chosen to write to
  • r-sync416 deb21d2 — fetching the mirror on the read path so it is always current | notes.ts states that availability reads git config with no network, and the PreToolUse hook has a latency budget a fetch would be felt in on every edit
  • r-sync416 deb21d2 — letting the newest side win when two clones diverge | the note is the record, so picking a winner destroys one; the union keeps both and loses nothing
  • r-mention408 ec314cd — leaving the quoted-warning case alone as an acceptable false positive | a blocked record has its content withheld rather than downgraded, so the cost is not one dulled sentence but a safety warning the agent cannot read at all
  • r-mention408 ec314cd — treating quotation marks as the mention signal | an attacker quotes as readily as a defender, and the reporting-verb window at least costs them the imperative form
  • r-schema406 22b2c35 — shipping a release note instead of a version bump | nothing surfaces the staleness for a user to act on -- doctor reported the stale cache as healthy, which is the whole reason the bug is silent
  • r-schema406 22b2c35 — re-applying the classifier when rows are read | the gate belongs at the boundary where raw git output becomes a candidate record, and a second copy at read time is the two-implementations-one-hole shape this codebase already warns about
  • r-note409 0dff3e4 — refusing to grade any notes-sourced record above claim | it passes the same regression test while breaking the notes mirror for repositories that legitimately use it, which is why the control case asserts that a note by a trusted author still grades directive
  • r-note409 0dff3e4 — attributing a note to its most recent writer | a cat_sort_uniq notes merge puts two writers' text in one blob, so the later writer's grade would cover the earlier writer's content -- verified against git, and the test for it fails against this rule
  • r-note409 0dff3e4 — requiring a signature on the notes ref before trusting it | commit authorship is not signature-verified either, so demanding it of notes alone sets an inconsistent bar and is a much larger change than the forgery needs
  • r-backfillclosed 00de5fa — Reporting without refusing | the report does not stop the duplicate, and this is the one command that can create one
  • r-backfillclosed 00de5fa — Refusing the read-only modes too | they are how the state is diagnosed, and they cannot write
  • r-initunfetched 889d191 — Reading notesAvailability at format time | init's own doctor step writes the refspec first, so the read always returns absent and the line never appears -- measured, not assumed
  • r-initunfetched 889d191 — Widening the six-line contract | it was not needed, and a contract loosened to fit one line is loosened for every line after it
  • r-initunfetched 889d191 — Making init exit non-zero | Say the mirror went unread, instead of counting the rebuild as whole #401 settled that an unfetched mirror is not a failure of the command that built the index over it
  • r-mirrorunread 8e4bdc5 — Exiting 3 the way context does | 3 marks an incomplete answer rather than a failed build, this command's documented codes are 0 and 2, and the change would fail init and CI in every unfetched clone over an index that is correct for what git holds
  • r-mirrorunread 8e4bdc5 — Carrying the state through IndexStats and reporting it from core | the data was already right and only the report was wrong, so widening a core type to move one sentence costs every caller of the index a field it must then decide about
  • r-mirrorunread 8e4bdc5 — Adding the same sentence to init and backfill in this change | neither is silent for want of a check -- init collapses its steps to check marks unless --verbose, and backfill prints its own count -- so each is a decision about that command's output contract, which issue 400 does not make
  • r-claimsmatch 506ada4 — Softening the delivery paragraph instead | it is the accurate one, and the scene was the sentence out of step with it
  • r-claimsmatch 506ada4 — Deleting the pricing scene | it is the clearest illustration in the README of what a record contains, and the defect was its last sentence rather than the scene
  • r-m5analysis 3450656 — Computing the table now and not looking | the file would exist, and a number that exists is a number someone reads
  • r-m5analysis 3450656 — Leaving the analysis to be written after the run | that is the freedom this document was written to give up, and writing it afterwards is indistinguishable from writing it to fit
  • r-m5analysis 3450656 — Asserting the M1 control in a vitest suite instead of --validate | it belongs to the analyser, and a reader checking the verdict should be able to re-derive the control with the same command that produced the verdict
  • r-benchscope 67f4375 — A declared list of result files, in the gate or in a manifest | opt-in under another name, and a file left off it is silently ungated -- which is precisely the failure bench/verify.mjs is not run by anything, so the result schema drifted unnoticed #392 reports
  • r-benchscope 67f4375 — Validating only the three files that pass today | a description of the present rather than a rule, and it goes stale the first time a matrix is added or the schema moves
  • r-benchscope 67f4375 — A naming convention on results filenames | the runner's default output is an ISO stamp plus hex and the metric writers' is a family name plus an ISO stamp, but every run-record file in the tree was named by hand through --out (t702-m2, m5-off-design-20-tasks), so the convention is enforced nowhere and cannot carry a gate
  • r-benchscope 67f4375 — A marker file dropped beside each gated result | still a step somebody has to remember, and a forgotten step is the defect being fixed
  • r-benchscope 67f4375 — Widening result.schema.json to describe both row families | they share no required field, so the union would accept a run record with no task and a metric row with no metric, which is a schema that rejects nothing
  • r-benchscope 67f4375 — Making harness_commit and dist_digest optional in the schema | weakens the gate for every future row in order to admit six historical files, and Make per-turn token usage observable, and say what it still cannot price #390's drift was an additionalProperties failure that a laxer schema catches later rather than sooner
  • r-benchscope 67f4375 — Skipping the six pre-provenance files whole | cheaper to write, but it stops checking types, patterns and the closed property set on 380 rows in order to excuse two absent fields
  • r-pinskew 007ccbf — 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
  • r-pinskew 007ccbf — 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
  • r-pinskew 007ccbf — 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
  • r-pinskew 007ccbf — Treating a manifest with no version as a match | that is the false green the check exists to remove
  • r-priorart 507ae24 — Saying nothing | the survey found it in an afternoon and anyone evaluating this project will
  • r-priorart 507ae24 — 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
  • r-priorart 507ae24 — 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
  • r-scaleproof 4c093f2 — 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
  • r-scaleproof 4c093f2 — A latency table | three figures do not need one, and the README just had its reference material moved into docs/
  • r-extbaseline 064daf6 — 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
  • r-extbaseline 064daf6 — 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
  • r-3c9d52 dc9e769 — 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 No-index query at 100k regressed from 7.7s to 90s — cause not yet identified #163 already recorded here
  • r-3c9d52 dc9e769 — 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
  • r-3c9d52 dc9e769 — 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
  • r-ledgerresult bc31c90 — 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
  • r-ledgerresult bc31c90 — 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
  • r-ledgerresult bc31c90 — Publishing a single write-side accounting | The break-even figure does not say which token accounting it uses, and both accountings are defensible #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
  • r-surfacedeliv fae9e1e — 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
  • r-surfacedeliv fae9e1e — 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
  • r-rel060 e999b9d — 0.5.2 | three observable behaviour changes and one narrowed acceptance rule are not a patch, and an upgrader reading the number would not go looking
  • r-rel060 e999b9d — Publishing the delivery figure in the README | the measurement is one corpus and one repository, and the README already carries more numbers than a first-time reader can weigh; docs/evidence.md and bench/ hold it
  • r-pipesplit b4fa571 — Splitting on the last pipe instead of the first | this repository's own records refute the premise that reasons rarely contain a pipe: two of the three multi-pipe values carry it in the reason, so the change would break two correct records to fix one
  • r-pipesplit b4fa571 — Defining a backslash escape for the separator | that is a new grammar rather than a clarification -- parser, renderer and validator would all have to agree on unescaping, and it changes what a conforming record looks like, which needs sign-off this fix does not have
  • r-pipesplit b4fa571 — Making any value with more than one pipe a format violation | it invalidates 616005d and aa68a9a, whose extra pipe sits in the reason and whose split is already correct, and dogfood validates all of history
  • r-pipesplit b4fa571 — Having guard score both the first-pipe and last-pipe splits and keep the better one | it invents an alternative no author wrote and then reports it as recorded, which puts the silent wrong answer in a new place instead of removing it
  • r-pipesplit b4fa571 — Rendering every Ruled-out: value pre-split in the query output | it rewrites 620 lines of output to mark 3, and the annotation is only information where the split is actually in doubt
  • r-pipesplit b4fa571 — Warning on the code-span case rather than refusing it | a span that crosses the separator is provable rather than suspected, and this repository refuses what it can prove instead of hoping the author reads stderr
  • r-gcunstageable 5cd6b8f — Collecting on age alone, without the staleness test | a transaction still sitting on the HEAD it was prepared for can be staged and committed at any time, so age alone would delete work the user is still holding
  • r-gcunstageable 5cd6b8f — Stamping expires_at at prepare or verify | it moves a field ADR-0021 declares normative and whose null-before-stage shape an mcp-capture oracle pins, and it would expire a capture while the user is still being asked about it
  • r-gcunstageable 5cd6b8f — A second staleness rule inside pending-gc.ts | pending ls already computes the same question, and a listing that calls a file collectable while the collector disagrees is worse than either answer alone
  • r-gcunstageable 5cd6b8f — Deleting the transaction where the skill skips it | the skip lives in a skill a host is free not to run, and a leak avoided only on the well-behaved path is not fixed
  • r-gcunstageable 5cd6b8f — A --force for pending rm on a staged or unreadable file | an unreadable file may be a newer format this binary cannot parse and another still can, and the two phases it protects are the ones a hook may be seconds from finalising
  • r-secondtie 998bf18 — Adding a topological ordinal to the trailers table | it bumps SCHEMA_VERSION and changes the index format, which needs an ADR and the representative's approval rather than a silent column
  • r-secondtie 998bf18 — Reusing trailers.id as that ordinal | rebuildIndex inserts in git rev-list HEAD order, newest first, and updateIndex appends newer batches after it, so the rowid runs backwards within a rebuild and forwards across them
  • r-secondtie 998bf18 — Sorting both serving paths by (committed_ts, commit_sha) so they agree exactly | it buys symmetry by discarding the one real topological signal available, the order of the git log walk, making the path that has a signal as arbitrary as the path that has none
  • r-secondtie 998bf18 — Resolving the conflict deterministically and saying nothing | determinism only makes two commands agree on an answer neither of them earned, and this repository treats a confident wrong answer as worse than a declared unknown
  • r-secondtie 998bf18 — Adding a fourth Lifecycle value for the undecidable case | active plus the existing review flag already says a human must decide, and a new enum member is a resolution-contract change every consumer would have to be taught
  • r-dedupviol 18ad9c1 — Letting checkReferences stay silent on duplicate-id | it is the only reporter once an sha is resolved, so --commit and --range would lose the finding outright, and the bug-issue-92 case in validate.test.ts asserts references failed on exactly that input
  • r-dedupviol 18ad9c1 — Letting the shape check stay silent on duplicate-id | it is the only half that runs when the notes mirror is unfetched, the clone is shallow, or the message arrives on stdin, so the check would disappear precisely where a commit is being written
  • r-dedupviol 18ad9c1 — Deduping the merged list globally instead of across the seam | it would also fold two byte-identical unknown-key trailers into one, and those are two edits rather than one instruction printed twice

Truncated: 416 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 and others added 16 commits August 8, 2026 15:26
The defect history of this command is a history of values held and not
recorded. #49 had the redirected hook target in hand and printed it into prose.
#128 and #149 failed a working hook by probing a synthesized path, which a row
naming what was actually executed would have exposed on first read. #382 printed
the stale pin inside its own ok line. #63 had the fetch exit code. #335 and #458
were verdicts built on counting a different convention's vocabulary.

Every row now carries an evidence map beside its sentence: what was executed,
what it returned, and the counts a conclusion rests on. A non-ok row must carry
at least one key, so a claim with nothing behind it does not typecheck as a
complete row.

Process output is kept as a bounded excerpt rather than collapsed to
present-or-empty, because hook-runtime and inject-runtime diagnose from stderr
first lines -- the reference implementation sanitises there and this one reads
there. Paths render home-relative so a pasted bug report does not carry
someone's directory layout.

The text report is byte-identical. The pinned snapshot is what says so.

Limit: evidence answers what was observed, not whether the observation was the right one to make -- #335's wrong count would now be visible in a field rather than prevented
Ruled-out: collapsing stdout and stderr to present or empty, as the reference does | it sanitises against paths and tokens it cannot inspect, while these checks diagnose from the first line and would lose the diagnosis
Ruled-out: leaving evidence optional on non-ok rows | a conclusion without its observation is the shape every defect above shares, and optional means absent in the row nobody looked at
Warn: the 200-character cap is a judgement with no measurement behind it, and a diagnostic whose useful part sits past it will be silently cut with only the paired truncated flag to say so
Blast: local
Undo: easy
Certainty: firm
Verified: thirteen of thirteen rows carry evidence with zero empty maps, no value contains an absolute home path, the excerpt cap and its paired truncated flag hold in both directions, and the text and check-order snapshots are unchanged across the change
Provenance: authored
Record-Id: r-evidence465
Make a doctor row carry the observation its verdict rests on
Section 8.2 promised zero writes. Writing section 11's read-only test strictly
is what found the promise false: a plain run touches `index.db-shm`, and `--fix`
creates that and `-wal`. Those are SQLite's bookkeeping for a WAL database,
created by opening one at all, for readers as much as writers, and they hold no
committed data.

Two ways to close the gap. Making the run literally write-free means opening the
index outside WAL for doctor, which trades a documentation problem for the
concurrency one #420 was about. So the document moves instead, and states the
invariant that carries the meaning: `index.db` is byte-identical after a plain
run, including under failure, and nothing else in the repository changes but
those two sidecars.

The acceptance row moves with it. A table that still said "zero writes" while
the test asserted something narrower would leave the next reader to discover
which one to believe.

Limit: the exception is named for SQLite specifically, so a future dependency that touches the filesystem on open would need its own line rather than being covered by this one
Ruled-out: opening the index outside WAL so the promise becomes literally true | it reintroduces the concurrency failure #420 fixed, to make a sentence accurate
Ruled-out: leaving §8.2 and scoping only the test | the document is what a reader checks the code against, and a test quietly narrower than the promise it implements is the arrangement this milestone exists to remove
Warn: `--fix` creates the `-wal` file that a plain run does not, so the two paths differ in what they leave behind even though neither writes data
Blast: local
Undo: easy
Certainty: firm
Verified: the three read-only invariant cases pass against the amended wording, asserting `index.db` equality and a filtered inventory rather than an unqualified zero
Provenance: authored
Record-Id: r-prdwrites
Say what a read-only run actually does
The generated block declared M4 while the paragraph above it stated an M5
result. That gap was filed rather than closed during the release, because
rewriting the declaration between a CI failure and a tag is the careless
publication the declaration exists to prevent. There is no tag pending now.

Repointing it found a second thing. The block reported 1,240 runs where the
registered analysis reports 1,160, because the generator counted rows on disk
and M5 has seven shards of which three are re-runs. A study that re-runs a
shard has more rows than measurements, and a line reporting the file count
invites a reader to take the larger number for the study's size.

The fix is in the generator rather than in the number. It now counts distinct
task-arm-seed cells the way bench/m5-analysis.ts does, and says both figures
with the difference named, so the published line and the registered analysis
cannot disagree without one of them changing.

The status note carries what the M4 note carried: why three shards are re-runs,
where the supersession rule lives, the excluded error row, the unequal
truncation and which direction it pushes, and that every record rendered
`[claim]` so this is not the `[directive]` path 0.7.1 made reachable. M1, M1-b,
M2 and M4 stay unpooled for the reason the M4 note already gave.

Limit: the seven shards are declared individually, so a shard added later is invisible to this block until someone lists it -- which is the property the declaration was built for and the cost that comes with it
Ruled-out: globbing bench/results for m5-*.jsonl | the file next to them is a withdrawn design log, and a glob is how the wrong dataset gets published without anyone deciding to
Ruled-out: reporting only the 1,160 and dropping the row count | the rows are on disk and a reader who counts them would find the block understating; naming both and the reason is what makes either checkable
Warn: the generator's supersession key is task, condition and seed, which is M5's cell shape; a study whose cell is keyed differently would silently collapse rows that are not duplicates
Blast: local
Undo: easy
Certainty: firm
Verified: 1,240 rows across the seven declared shards resolve to 1,160 distinct cells with 80 superseded, matching the registered analysis exactly; the gate passes with no --write and all four READMEs regenerate identically
Provenance: authored
Record-Id: r-m5sources
Publish the study the README's claim actually rests on
One dead hook runtime produced four independent-looking findings, and a reader
had no way to tell the cause from the consequences. A dependent whose failure
only restates its dependency's now carries `blockedBy` naming the root, so a
fix plan can name causes once instead of repeating a symptom per row.

The rule the whole milestone rests on is that annotation is not suppression.
A blocked row keeps its status, its detail and its own evidence, and still
appears in the report. A suppressed row is how a diagnostic lies by not
looking, which is the defect #458 was, and a collapse that hid rows would have
reintroduced it while claiming to reduce noise.

The constructor refuses to name an `ok` blocker rather than trusting callers to
avoid it. A collapse that points at a healthy check is worse than no collapse:
it tells a reader to fix something that is not broken, and nothing downstream
can detect the mistake.

Independence is preserved deliberately. A stale stub whose runtime is also dead
keeps its own finding unannotated, because that failure survives fixing the
runtime and a fix plan that dropped it would leave the user with a repaired
dependency and an unrepaired stub.

Limit: only two edges are declared -- inject-version on inject-runtime, and the §2.2 checks on the capture chain -- so a dependency nobody wrote down still surfaces as an independent finding
Ruled-out: declaring commit-msg-hook on hook-runtime to complete the graph | that edge runs backwards against registry order, and a declared edge the emission order cannot satisfy is a lie in the structure the fix plan walks
Ruled-out: omitting a blocked row from the report and keeping it only in the JSON | the text report is what a user reads, and a row missing from it is indistinguishable from a check that was never run
Warn: `blockedBy` resolves chains to the root, so a two-link chain reports the far cause and the intermediate failure is visible only as its own row -- correct for a fix plan, and surprising if read as a parent pointer
Blast: local
Undo: easy
Certainty: firm
Verified: thirteen rows in and thirteen out with collapse active, no row omitted and no `blockedBy: null` in the serialized report; a blocked dependent keeps its detail and evidence, a chain resolves to its root, an independent failure stays unannotated, and naming an ok blocker throws
Provenance: authored
Record-Id: r-collapse466
Say which failure is the cause and which are its echoes
The gate asked whether the plugin entry point resolved, and it passed for two
releases while resolving to the wrong thing. Found running it against a fresh
v0.7.0 clone on a machine carrying a 0.6.0 install: the clone answered 0.7.0
and the entry point answered 0.6.0, both exiting zero.

`commitlore-run.sh` tries `commitlore` on PATH before CLAUDE_PLUGIN_ROOT, and
that order is deliberate -- the installer's wrapper execs node itself, so it
works where this script would otherwise have to find node, and on the hook hot
path a missing node means no context at all. The order stays. What changes is
that the gate narrows PATH and compares the version, so it asks whether the
right thing resolved rather than whether something did.

Two cases pin both halves. The entry point reaches the plugin when nothing
shadows it, and a commitlore on PATH wins when one does. The second is not a
bug being enshrined: it is the documented order, asserted so that changing it
becomes a decision someone makes rather than a side effect of an unrelated
edit.

This is the smallest of the three options #483 recorded, and the only one that
does not touch the hot path. Making the mismatch loud at runtime would mean
running --version twice on every edit; preferring CLAUDE_PLUGIN_ROOT would
reopen ADR-0011's relationship with the installer, which is a decision rather
than a patch.

Limit: the gate now catches the mismatch at release time, and doctor's inject-version check catches it for a user, but nothing tells a user who has never run doctor
Ruled-out: preferring CLAUDE_PLUGIN_ROOT over PATH | it reopens how ADR-0011's ship-dist-in-the-repo interacts with the installer's wrapper, and the node-resolution advantage that motivated the current order would need re-examining rather than reversing
Ruled-out: comparing versions inside the run script | it sits on the hot path of every edit and would spend a second process launch there to report a condition doctor already reports
Warn: the narrowed PATH in the gate includes the interpreter's directory, so a machine whose node lives beside a commitlore wrapper still shadows the plugin and the check will pass for the wrong reason
Blast: local
Undo: easy
Certainty: firm
Verified: with PATH narrowed the entry point answers the repository's own version, and with a stub commitlore ahead of it on PATH it answers the stub -- both asserted, so the documented precedence cannot change silently
Provenance: authored
Record-Id: r-plugres483
Ask the release gate which commitlore the plugin actually ran
The release gate compared the tag, `package.json` and `commitlore --version`,
and never opened `.claude-plugin/plugin.json` or `package-lock.json`. ADR-0026
makes the plugin the canonical install path, so the one manifest a plugin
user's install resolves was the one the gate did not look at.

That is not hypothetical. `package-lock.json` declared 0.1.0 from the first
release through 0.7.0 while both manifests moved, and this gate passed every
time, because it does not look there. A human found it by reading the file.

The two lock fields are checked separately because they went stale separately:
the root `version` and `packages[""].version` are independent, and a report
naming only one sends an operator to fix half of it.

A missing manifest is a failure rather than a skip. The defect being repaired
is a gate that stayed quiet about a file it did not read, and a gate that
stays quiet when the file is absent is the same gate.

Limit: the gate now covers every versioned manifest that exists today, found by searching for `"version"` across the tree -- a manifest added later is not covered until someone adds it here, which is the same standing every declaration in this repository has
Ruled-out: globbing for JSON with a top-level `version` key | node_modules and generated fixtures carry that shape, and a gate whose scope moves with the working tree fails differently on a clean clone than on a developer's machine
Ruled-out: warning on a missing manifest and continuing | the tag is immutable once fetched, so a release that publishes past an unreadable manifest cannot be corrected afterwards
Warn: the check runs against the built `dist` in the workspace, so it qualifies the artefact the workflow just built rather than one downloaded from the release -- the fresh-clone checks are what cover that direction
Blast: system
Undo: easy
Certainty: firm
Verified: five of the six new cases fail against the previous script and all six pass against this one; `check-release-version.mjs v0.7.1` reports all five sources agreeing at 0.7.1 in a tree where they do, and reports every disagreement rather than the first when three are seeded
Provenance: authored
Record-Id: r-vergate492
`publish` depended on `version-consistency` and nothing else, so pushing a tag
created the GitHub Release and the six checks that decide whether it deserved
to exist ran afterwards, if the operator remembered. That is how 0.7.0 was
published with its headline feature broken: the checks were a written
procedure, and a procedure is not a dependency.

Section 4 is now the `install-gate` job and `publish` needs it. All six rows
are automated and every one of them is blocking; none was dropped or made
advisory, because a gate claiming six checks while running four is worse than
one honestly running five.

Two rows needed real fixtures rather than approximations. The PATH-less commit
runs under `env -i` so node is genuinely absent, which is what makes a
rejection evidence that the stub used its recorded interpreter instead of an
ambient one. The stale-hook row installs a healthy stub first and then
replaces only its body, so the fixture is a stale stub rather than a missing
one.

That second fixture also produces a hook-runtime fail of its own, and doctor
exits 1 on it. The row being qualified is `commit-msg-hook`, whose required
verdict is `warn`, so the check reads that row out of the JSON rather than the
process exit -- reading the exit here would have qualified the wrong thing.

The clone is of the pushed tag from the remote and runs the shipped bundle
with no build and no install, because building in this job would qualify an
artefact the tag does not carry.

Limit: the job runs on ubuntu-latest only, so it qualifies the POSIX install; the Windows path is still covered by `install-ps1` in CI against the branch rather than against the tag
Ruled-out: reusing the workflow's checkout instead of cloning | the row asks whether a fresh clone works, and a checkout is not the thing users get
Ruled-out: making the stale-hook row non-blocking because its fixture also fails another check | the confounding finding is separable by reading the row, and a row excused for being awkward is the fail-open being repaired
Warn: `publish` now waits on a job that clones over the network, so a transient clone failure withholds a release for a tag that is already pushed -- the tag is the durable thing and re-running the job is the recovery
Blast: system
Undo: easy
Certainty: firm
Verified: the six rows run end to end against a fresh clone and exit 0; the workflow loads as YAML with publish declaring both dependencies; and the gate is not vacuous -- breaking the doctor row exits 1, and comparing the plugin version against a wrong expectation exits 1
Provenance: authored
Record-Id: r-failclosed493
The section-4 job made the installation a prerequisite, and a review found the
gap that left: the workflow accepts every `v*` tag. Nothing asked whether the
tagged commit is on the release branch, and nothing asked whether CI passed at
that exact commit. Tagging any dev commit published it, and tagging a commit
whose CI failed published that.

Two scripts rather than two `if:` expressions, because a condition in YAML
cannot be tested and these are the conditions that decide whether a wrong
artefact reaches users.

The CI gate asks whether the required checks passed, not whether any failure
is visible. Everything that is not a success refuses: failure, cancelled,
timed out, skipped, queued, still running, and a required check absent from
the response entirely. An empty result set is a refusal rather than a quiet
pass -- that inversion is the defect this repository has now published twice,
and it is the reason the required list is declared in the script instead of
inferred from whatever happens to be present at the commit.

The ancestry gate refuses when it cannot see enough history to answer, so a
shallow checkout produces a refusal rather than an accident. The job checks
out with full depth for the same reason.

`publish` now waits on all four, and has no `if:`. An omitted dependency is
itself a bypass: nothing can withhold a release for a job it was never asked
to wait for, so the test asserts the complete set rather than the absence of
an escape hatch alone.

Limit: the gates qualify the commit and its CI, not the tag's authorship or timing -- anyone who can push a `v*` tag to a qualified commit on main can still publish, and expiring or re-pointing a tag after these jobs pass is outside what any of them observe
Ruled-out: expressing either check as an `if:` on publish | a condition in YAML has no test, and a release gate whose logic cannot fail in a suite is a claim rather than a control
Ruled-out: treating a missing required check as nothing to report | that is the empty-set inversion above, and it is exactly how a release with no CI at all would have published
Warn: the required-check list is a literal, so a job renamed in the CI workflow becomes a check this gate waits for and never finds -- it fails closed, which is right, and the failure will read as a CI problem rather than a rename
Blast: system
Undo: easy
Certainty: firm
Verified: fifteen cases pass, and the two scripts were driven directly -- a tag on main accepts, a tag on a side branch refuses, a shallow clone refuses rather than passing, six successes accept, and failure, cancelled, timed out, skipped, in-progress, an absent required check, an empty payload and successes recorded for a different commit each refuse
Provenance: authored
Record-Id: r-pubprereq
Check the manifest the canonical install path actually resolves
Qualify the release before it exists rather than after
Refuse a tag that is not on main or whose CI did not pass
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