Skip to content

Say when a capture was prepared and then dropped - #459

Merged
MongLong0214 merged 1 commit into
devfrom
doctor-pending-backlog
Aug 8, 2026
Merged

Say when a capture was prepared and then dropped#459
MongLong0214 merged 1 commit into
devfrom
doctor-pending-backlog

Conversation

@MongLong0214

Copy link
Copy Markdown
Owner

Closes #458. Found in the field, not in a fixture.

What happened

A repository with 815 commits, hooks installed and an index current with HEAD held zero CommitLore records — and doctor reported all ten of its checks ok.

Four captures sat in .git/commitlore/pending/, one staged with a passing validation and a record ready to attach, all four eight days old.

Every step behaved as designed

  1. capture-stage stamps expires_at = staged_at + 5 minutes
  2. The commit did not happen inside that window — 49 commits have landed since
  3. prepare-commit-msg Gate 3 requires unexpired, so it silently skipped the record
  4. pending-gc protects the staged phase, so the file is never collected
  5. Nothing told the user

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

Against the real repository

warn  pending captures — 1 staged capture(s) expired before reaching a commit and were
      dropped, alongside 3 earlier draft(s) that never staged; oldest from
      2026-07-31T05:09:57Z … so these decisions were never written to the history (#458)
      fix: commitlore pending ls

Staged losses and never-staged drafts are counted separately — merging them hides which decisions had a record ready to attach.

What this deliberately does not change

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.

Nor does gc start collecting expired staged files — that would erase the only evidence a capture was ever prepared, and this defect was found precisely because the files were still there.

Tests

Six new cases, plus the two existing assertions pinning the check list and count updated for the thirteenth check. test/doctor.test.ts: 57 passed. Dogfood re-run after committing.

One fixture note: the pending reader rejects a partial transaction, so the fixture carries its full field set. A shorter fixture made every case pass for the wrong reason — the files were counted as unreadable rather than stale.

A repository with 815 commits, hooks installed and an index current with HEAD
held zero CommitLore records, and doctor reported all ten of its checks ok.
Four captures sat in its pending directory, one of them staged with a passing
validation and a record ready to attach, all four eight days old.

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

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

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

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

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

CommitLore — record lint

Trailers: clean — 1 commit in origin/dev..458bcec55c5eb9f9b370394acad5c2c83d11232c
Active constraints: 102 limits · 236 ruled-out · 63 warnings — from 123 records over 6 changed paths

Active constraints for the paths this PR touches

Limits (102)

  • 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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-f61a2c 9114cf0 — the matcher remains deterministic and lexical; no embedding or semantic service is available to distinguish paraphrases
  • 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-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-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-4e9c72 a7a7e26 — the index is derived, so nothing about its state can make the tool give a wrong answer -- only a slower one
  • 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

Ruled out (236)

  • 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-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-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-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-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
  • r-owntmproot 6543870 — An env override such as COMMITLORE_DEMO_TMPDIR | it moves the production default off the call site, where an ambient variable can redirect a real run and nothing in the code reads as changed
  • r-owntmproot 6543870 — Keeping the before/after delta and widening it | the delta narrows the window rather than closing it, and the directory that turned this red was created inside the window it leaves open
  • r-owntmproot 6543870 — Deleting the two tests or dropping the prefix filter to make them pass | the property is real and cheap to hold, so that trades a flaky true signal for a permanent blind spot over cleanup after a crash
  • r-diffdefault 4ac8163 — Asserting on spawned capture --help output | the suite would rebuild the CLI for one string, and the option line is the thing under test either way
  • r-diffdefault 4ac8163 — Leaving the string and documenting the real default only in the skill | the skill is read by agents and --help by people, and the two would disagree
  • r-shallowlast 0913821 — Caching the shallow answer for the process | validate is a short-lived process and a cache would outlive the fetch --unshallow that changes the answer
  • r-exitonemeans 89f7af8 — Skipping the whole reference check on a shallow clone, the shape the unfetched gate already uses | it would also withdraw duplicate-id, which is answered from the message alone and is the multi-block squash shape the hook is most useful for -- a real red dropped to fix a false one
  • r-exitonemeans 89f7af8 — Letting a block resolve its own Record-Id along with its siblings | a record that follows itself still resolves to nothing, and the truncation argument does not cover it, so it stays reported
  • r-exitonemeans 89f7af8 — Reporting the shallow skip as ok with the caveat attached | ok is what a machine branches on and what a reader remembers, and this clone has no verdict to offer on that reference
  • r-failopen abc54ea — A third replaceAll turning exit 1 into exit 0 | it rewrites any future exit 1 added to the shared body too, which is how these two acquired this ending in the first place
  • r-failopen abc54ea — Adding hooks uninstall --all and leaving the default alone | the hooks left behind are the reported defect, so a default that still leaves them documents the trap instead of removing it
  • r-failopen abc54ea — Having the two hooks say nothing at all | a hook that captured no record and reported nothing is the silent-success shape doctor's own fix breaks git fetch and git pull, then reports ok #63 and inject --hook-input is byte-identical on malformed input and on no-records: silent fail-open #67 were, and the state it hides is one command away from being fixed
  • r-notereach 1e72a28 — Filtering against every ref instead of HEAD | the commit source reads rev-list HEAD, so a wider boundary for notes puts the two sources back out of step in the other direction and lets an abandoned branch supersede a live record
  • r-notereach 1e72a28 — git merge-base --is-ancestor per annotated commit | it spends a process per record to answer what the HEAD walk the callers already hold answers for nothing
  • r-fetchowed 11f04b4 — Making notesAvailability ignore the refspec | it fires on every repository configured after cloning and flips guard's exit code through the incomplete flag, which is a broader harm than the narrow one it fixes
  • r-fetchowed 11f04b4 — Having doctor --fix run the fetch | it writes reversible local config by design and touches no network, and a diagnostic that silently reaches out is a different tool
  • r-guarddisclose 8a4d0c7 — Removing the sentence without adding the measured figures | the ADR requires both, and an empty description discloses nothing about a 22% recall
  • r-guarddisclose 8a4d0c7 — Asserting the disclosure by scanning the whole tools array | a new tool that exposes guard should have to be added to the list deliberately, so the omission is visible in a diff
  • r-refspecfetch 936d206 — Making doctor --fix run the fetch | it reads git config and touches no network by design, and a diagnostic that silently reaches out is a different tool
  • r-refspecfetch 936d206 — Adding a fourth state for configured-but-unfetched | the existing vocabulary already answers the only question callers ask, which is whether an empty result can be trusted
  • r-actionsleak a6fbb4b — Publishing a placeholder package to claim the npm name | it makes a registry entry the product does not use, and the fix is to stop reaching for a registry rather than to own one more artefact
  • r-actionsleak a6fbb4b — Keeping the fallback and pinning a version | there is no published version to pin, so this would have been a slower way to fail
  • r-actionsleak a6fbb4b — Wrapping withholdBlocked at the MCP handler | the same omission would recur at the next route; the boundary that builds the data is where it belongs
  • r-recordgate335 a83ebe3 — Adding the observed noise keys to the denylist | ax, sha256 and the rest are one repository's accident, and the next repository invents different ones
  • r-recordgate335 a83ebe3 — Requiring Record-Id: to make a block a record | stale needs it for identity, but a commit carrying only Limit: is a record SPEC recognises and dropping it would lose real context
  • r-recordgate335 a83ebe3 — Inferring intent from position or neighbouring lines | that is guessing, and the failure mode is discarding a record somebody wrote on purpose
  • r-draftfirst329 0506a5d — Reporting all missing options at once | it would say the draft is unparseable and the transcript missing together, and the second is not true until the first is fixed
  • r-draftfirst329 0506a5d — Changing harvest --draft to match | it already resolves the draft first, verified by running it against the same prose rather than by reading the dispatch
  • r-uninstall1123 4ddac0d — Removing the entry whose key is commitlore | it takes a server the user named that way and the other install's entry on a two-install machine, and neither loss is reported
  • r-uninstall1123 4ddac0d — Parsing the TOML config and writing it back | the round-trip reformats the whole file, which is the "never reformat beyond the one entry removed" this ticket forbids
  • r-uninstall1123 4ddac0d — Rewriting a config that failed to parse | the failure mode is losing a config we were only supposed to edit one key of
  • r-uninstall1123 4ddac0d — Removing the Claude Code plugin cache | it is thousands of files this command did not write, keyed by plugin version; naming the step is the honest boundary
  • r-winpath1127 bdf4ac0 — Normalising only the recorded root at write time | it would fix new installs and leave every existing repository comparing a stored win32 string against pwd -P output
  • r-winpath1127 bdf4ac0 — Loosening the containment match so the legitimate bundle runs | that trades Security: commitlore.bin and COMMITLORE_BIN are executed, and the env path lacks the guard the config path has #71's property for a working hook, which is exchanging one defect for another and is what this ticket's forbidden scope names
  • r-winpath1127 bdf4ac0 — Renaming the containment helper to match its new signature | T-1125's invariant asserts the old identifier as its proxy for the property surviving, and that test belongs to another ticket
  • r-winpath1127 bdf4ac0 — Fixing the stub and leaving doctor's mirror alone | the mirror reports no problem in precisely the state where the hook is dead, so the check that should warn would keep saying healthy
  • r-nobin284 ab2f08f — Keeping the binary classification arm for the installer's wrapper | the wrapper is a shell script that execs node, so trusting it by name would re-admit any extensionless executable called commitlore while the bundle it runs is the thing with an interpreter to check
  • r-nobin284 ab2f08f — Relaxing the dogfood Evidence check to allow a missing path | it exists to catch a citation nobody can follow, and dropping the check to accommodate a deletion would retire the guarantee instead of correcting the question it asked
  • r-nobin284 ab2f08f — Leaving src/core/paths.ts alone because ADR-0026's inventory did not name it | it was the only remaining code that read files out of a compiled artifact, so leaving it would have left the invariant false while the test asserting it passed
  • r-nobin284 ab2f08f — Keeping /dist/commitlore in gitignore in case a binary returns | an ignore rule for an artifact nothing produces is a claim that something might, and the ADR says otherwise
  • r-pending311 4985a3a — Extending capture gc to also list what it would remove | gc answers what is collectable, and the transaction a caller cares about is usually the one gc will never touch
  • r-pending311 4985a3a — Storing stale in the transaction file | it is a fact about HEAD now, so a stored copy is wrong the moment HEAD moves and right only when it is redundant
  • r-pending311 4985a3a — Changing expires_at so verified transactions expire | it silently deletes work a caller may still stage, and this issue asked to see the state rather than to change the retention rule
  • r-pending311 4985a3a — Resolving an ambiguous prefix to the newest match | it guesses at the one moment the caller has proved they do not know which transaction they mean
  • r-query307 3cf3c58 — Testing the working tree with stat | a deleted file has real history and real records, so stat would report a missing path for one of the cases the diagnostic exists to distinguish
  • r-query307 3cf3c58 — A distinct exit code for a path that is not in history | context is on the hook path, where a nonzero exit is a failure to fail open around, and the caller who needs to branch has a diagnostic to read
  • r-query307 3cf3c58 — Emitting the diagnostic unconditionally from runQuery | the PreToolUse path queries a file that is often being created, so it would fire on every new file and break the hook's silence contract
  • r-query307 3cf3c58 — Searching for the nearest rename alias rather than the nearest ancestor | the alias walk is what already ran and found nothing, while the ancestor probe is bounded at four parents and answers the question that actually bit the reporter
  • r-capture309 5a9655d — Printing the reasons without unifying the two draft paths | the envelope path produced no reasons to print, so the output would still have been silent for the reported input
  • r-capture309 5a9655d — Keeping the envelope path unchecked and letting the verifier speak for it | the verifier checks whether a citation holds, not whether a field exists, so an unknown field is invisible to it by design
  • r-capture309 5a9655d — Defaulting the diff to the empty string and documenting that --diff is required | it makes the ordinary invocation useless and contradicts prepare, which needs no --diff to bind the same bytes
  • r-capture309 5a9655d — Comparing hashes leniently so an empty diff passes | the binding exists so a draft cannot be verified against sources other than the ones prepared, and loosening it removes the guarantee rather than the defect
  • r-harvest310 c055467 — Making the plain harvest message conditional on whether a diff exists | it would make the prescription accurate by narrowing it, leaving the contract still unreachable in the case that matters
  • r-harvest310 c055467 — Emitting the contract only when the transcript is also absent | that is what harvest --prompt-only refuses to print the contract without a transcript and a diff #229 landed and what this issue reports as insufficient, since a caller usually has a transcript before they have a diff
  • r-harvest310 c055467 — Keeping the old assertion and adding a new one beside it | two assertions requiring opposite outputs for one input is not a test suite, it is a coin toss
  • r-hooks296 b701559 — Making hooks install re-validate only when the hook file is unchanged | recordBinPath already runs every time, so the short-circuit was never the cause and gating on it would have left the wrong value being rewritten
  • r-hooks296 b701559 — Falling through to commitlore.node in the stub | it changes the file that carries install-root containment, and a stub already written in an affected repository is not re-read, so it would repair nobody who has the problem today
  • r-hooks296 b701559 — Having doctor --fix repoint the config itself | once the prescribed command works, the prescription is true; a second repair path would leave two places able to write the same key
  • r-hooks296 b701559 — Recording resolve(argv[1]) with a warning when it does not exist | the hook cannot act on a warning it never sees, and a recorded dead path is what stops resolution at the first step
  • r-idxhelp303 cd2ac79 — Deleting the better-sqlite3 mentions in index-db.ts as well | those comments explain why node:sqlite was chosen, and removing the reason leaves a future reader wondering whether the native option was considered
  • r-idxhelp303 cd2ac79 — Asserting the package name appears nowhere in src at all | that forbids the explanation along with the defect, so the check reads only lines that reach a user
  • r-idxhelp303 cd2ac79 — Naming node:sqlite in the message | the failure a user hits is that the binding is unavailable, and the module name adds nothing they can act on
  • r-mcpdraft291 d4444d5 — Changing the harvest contract to emit a bare array instead | the contract text, harvest's validator and the CLI all implement the object shape, so moving the contract would break two working surfaces to accommodate one broken one
  • r-mcpdraft291 d4444d5 — Accepting only the object shape and dropping bare-array support | the earlier tool description advertised an array, so a caller may exist that sends one; refusing it now would trade this defect for a different one
  • r-mcpdraft291 d4444d5 — Leaving the mismatch and documenting the array shape in the prompt | the prompt is generated by the same code the CLI uses, so the two consumers would then disagree about what the same generated text means
  • r-mcpadv273 0166937 — Omitting guard_advisory when it has no matches | absence on the wire reads as no ruled-out alternative applies, which is exactly the claim ADR-0020 forbids; an empty matches array with the disclosure attached says the true thing
  • r-mcpadv273 0166937 — Leaving the MCP response as the seven binding fields and treating the advisory as CLI-only | MCP is the first-class surface for every agent that is not using the Claude Code plugin, so a CLI-only advisory is an advisory most callers never see
  • r-t1110policy 9e7b37a — Hashing the parsed policy object instead of the file contents | ADR-0021 fixed the input as the contents, and it is the stronger choice: a reformat that changes nothing semantically still changes the identity, and reporting that the file changed is true
  • r-t1110policy 9e7b37a — Supporting a user-global policy file alongside the repository-local one | two locations need a precedence rule, and the user story this answers is repository-scoped, so one location removes the ambiguity rather than documenting it
  • r-t1110policy 9e7b37a — Ignoring an unknown key in the policy file | a silently dropped key lets a user believe a setting applied; rejecting it names the key and the allowed set
  • r-t1110policy 9e7b37a — Falling back to the defaults silently when the file cannot be parsed | the identity hash would then describe a policy the user never asked for, with nothing said about it
  • r-t1110policy 9e7b37a — Bumping the pending format version for the new field | ADR-0021 fixed the migration so no version change is needed, and producing one would falsify that ADR
  • r-t1109adv 4398856 — Emit raw GuardMatch objects in advisory | renderGuardMatch is the trust-grading boundary that withholds blocked content from model-visible surfaces
  • r-t1109adv 4398856 — Add a policy key to control advisory behavior | ADR-0020 classifies guard as non-blocking with no configuration escape
  • r-t1109adv 4398856 — Bump PendingRecord.version to 2 | ADR-0021 already fixed migration so no version bump is needed
  • r-gcwiring f21f28e — dropping --json from the gc subcommand and relying on the parent's | the subcommand would then document a flag it does not declare, and a reader of capture gc --help would not see it
  • r-gcwiring f21f28e — keeping requiredOption and giving gc its own top-level command | gc belongs to capture's surface, and moving it would make the fix a rename rather than a fix
  • r-flake221fix 2b21ed9 — downgrade evaluateInjectRun to warn | doctor.test.ts integration test asserts status=fail for ENOENT
  • r-flake221fix 2b21ed9 — remove trailers from init.test.ts repoWithRemote | weakens inject-probe test coverage without addressing the semantic defect
  • r-lb0xl89a 236229e — parameterising the contract with empty strings | prints a template with empty holes that teaches the session nothing about placeholder handling
  • r-t1009stage b5fcf4e — accepting base_head or a diff hash from the caller for efficiency | the caller is the party the transaction exists to constrain, so taking its word for the binding removes the point of the binding
  • r-t1009stage b5fcf4e — a single write_record tool that skips the transaction | PRD-F9 forbids it, and it would let a draft reach Git without ever being verified
  • r-t1005gates 15421c0 — blocking the commit when a gate fails | the commit is the user's work and the record is ours; failing their commit to protect our bookkeeping inverts whose job this is
  • r-t1005gates 15421c0 — treating a null expires_at as unexpired | null means the record was never staged, so it fails the staged requirement before expiry is consulted; reading it as "no expiry" would apply an unstaged record
  • r-t1006cli d22580b — exposing prepare, verify and stage as user-facing subcommands | the phases exist for the transaction, not for the user, and three commands is the problem this ticket removes
  • r-t1006cli d22580b — exiting non-zero when verification produces nothing | silence is the expected result for most commits, and a non-zero exit would train people to ignore it
  • r-t1007mcp b6ef112 — readOnlyHint: true | tool persists state under .git/commitlore/pending/ so marking it read-only would be dishonest
  • r-t1022sig e0c641d — keeping the score in text output behind a debug flag | a second flag for a number nobody should act on adds surface without adding information
  • r-t1022sig e0c641d — dropping the score from --json as well | a consumer that has already decided to parse structured output is not the reader being misled
  • r-t1011demo 1c0fc0c — seeding the demo into the user's repository behind a confirmation | a demo that can modify the thing it is explaining is not a demo, and a confirmation prompt is not a substitute for being unable to
  • r-t1011demo 1c0fc0c — recomputing lifecycle inside the demo to keep it self-contained | a second implementation of the rule would drift from the one under test, and the demo would stop being evidence
  • r-t1020desc dd12b42 — removing the description entirely | the first two sentences carry useful contract information a caller needs
  • r-t1020desc dd12b42 — stating only precision without recall | a caller needs both to judge the signal; recall is the more dangerous gap
  • r-t1030diag 344ada0 — a single generic message that quotes stderr without case distinction | it would still let the old "carries no node" wording survive on the exit-127 path, and would not communicate to the reader whether the probe learned anything
  • r-t1030diag 344ada0 — parsing more than the first line of stderr to classify the cause | false-uncertain is acceptable, false-confident is not; one line is enough to distinguish the obvious cases
  • r-fix191amb cb94448 — adding a guard clause only in checkReferences | leaves the predicate willing to say "resolved" about an unresolvable group; any future third caller would silently inherit the same defect
  • r-fix191amb cb94448 — removing the post-filter entirely | the original fix for cross-commit succession is correct and the release gate depends on it
  • r-fix187val 40f2436 — passing tip-scoped records directly to findIdCollisions | chronological ordering by committedAt breaks on same-second commits from git-log's newest-first output; the post-filter avoids reordering entirely
  • r-fix187val 40f2436 — duplicating hasDeclaredSuccession into validate.ts | the drift between two copies of the same predicate was the bug; a second copy would reproduce it
  • r-dupsucceed 6f77fcf — adjusting the four expectations to match the new grades | they encode that trust does not depend on the order records appear in the log, which is a property of the protocol rather than of this implementation
  • r-valdup145 bcb9563 — adding a duplicate-id scan to validate on its own | it would have to decide block boundaries again, and a second answer to that question is what let parse and validate disagree in the first place
  • r-convtrail150 57e89d2 — excluding Fixes and Closes with the rest | they carry decision context an agent can use, and discarding them would trade a projection full of attribution for one missing the issue a change answers
  • r-epipe2026 d9ee9ff — error-first spawn result handling | a completed git process can retain a real status after the input-pipe EPIPE race
  • r-doctorepipe 0420f5c — the executable being absent from PATH | the failing run's own message names EPIPE, not ENOENT — the ENOENT branch produces different text than what CI printed, and the executable resolves and runs every time
  • r-init107 f485f07 — treating every doctor warn as actionable | no remote is expected before a repository begins sharing records, while configured remote failures still need action
  • r-init107 f485f07 — promising rename following for several paths | git log --follow accepts one pathspec and the CLI already reports that limit
  • r-survsplit e73aed5 — keeping a single conservation rate with a footnote | the two failures need different fixes and one of them is not a defect in this project at all, so a shared number misdirects the reader on both
  • r-probepath 51f6446 — prepending the build output to PATH inside the check | it would make CI green by giving the probe an executable no user has, which is the reverse of testing what is configured
  • r-doctorprobe ed94491 — changing what init writes into settings.json | the installed hook was correct in every reported case; the check was reading a different world from the one it was auditing
  • r-f61a2c 9114cf0 — raising the guard threshold | the frozen corpus puts every true positive in the middle band, so a higher cutoff removes signal without repairing score order
  • r-f61a2c 9114cf0 — dropping keyword evidence | long proposals and diffs dilute Jaccard, while one-token alternatives such as Redis still need lexical containment
  • r-init96a 913c7e3 — running doctor --fix first, hooks/index after (the old README recipe's order) | doctor's report would open with findings the next two steps are about to fix, which reads as init shipping with a problem it does not have
  • r-init96a 913c7e3 — matching doctor's exit code exactly (0 unless a check is fail) | a warn init cannot resolve itself (no remote, PreToolUse hook) would then sit under a "completed cleanly" summary line — the same silent-success shape doctor's own fix breaks git fetch and git pull, then reports ok #63 and inject --hook-input is byte-identical on malformed input and on no-records: silent fail-open #67 were
  • r-fix92dupid 7f41a6e — flagging every duplicate sha in a group regardless of payload, matching parse's labelRecordBlocks exactly | validate's own collision-check array pairs repositoryRecords (already carrying the message's last block once) with a per-block candidate; an unconditional duplicate-sha rule would flag the message's own single last block against its own re-derived copy, a false positive fixed instead by building ownRecords once per message rather than loosening the predicate past correctness
  • r-fix92dupid 7f41a6e — reverting core/query.ts's collectRows dedup key back to sha+source+seq once findIdCollisions was fixed | that key was already wrong on its own terms (it does not match the trailers table's unique index), and leaving it in place would keep silently dropping a commit's second record block from any scoped-path context query, collision or not
  • r-fix90shape 7f5766d — keeping the parent-count signal and also computing it for --message-file via .git/MERGE_HEAD | that only exists while a merge is in progress (the commit-msg hook's own scenario), not when re-validating an already-made merge commit's extracted message -- which is the issue's own reproduction and stays unreconciled under that approach
  • r-parsemulti 6d39d25 — expanding findIdCollisions/index-db to flag a same-message duplicate id generally | reaches into query.ts's lifecycle fold and every consumer route (context, guard, stale) for a case bug-issue-89 only asked parse to surface; the smaller, requested fix is in parse alone.
  • r-multirec01 92aeb24 — minting a fresh Record-Id for an inherited record that never declared one | no other code path in this project invents identity; Record-Id is always author-declared, and doing it here would be new scope beyond what the issue asked for
  • r-multirec01 92aeb24 — a CI step comparing a PR's commits against its post-merge squash commit, as the primary mechanism for finding 1 | needs a GitHub API dependency this tool takes nowhere else, and can only run after the squash is already pushed; doctor catches the same mistake locally, before push, when the source branch is still in refs/heads
  • r-83d43117 8a49ddc — an install-root check for COMMITLORE_BIN | its only reason to exist is aiming the hook at a build outside the install root -- a test harness, a monorepo's local bin. Restricting its location would remove the one thing it is for; doctor's existing COMMITLORE_BIN visibility is the right amount of scrutiny for a channel that is supposed to point anywhere.
  • r-83d43117 8a49ddc — resolving commitlore.bin with an external realpath/readlink -f binary | neither is guaranteed on every machine a hook runs on, the same reasoning 1fd0d53 gave for not shelling out to shasum. cd ... && pwd -P is a POSIX-specified builtin and covers the same physical-resolution need without a new dependency.
  • r-exit065 e545dee — leaving guard's inversion in place and only documenting it | guard and inject are the two commands wired into hooks and CI, so a script written against guard today is already trusting the number; a documented inversion is still an inversion, and it gets load-bearing the longer it sits. The surface is one boolean per command right now — cheaper to fix once than to explain forever.
  • r-fix70a1 d707fc7 — exhaustive per-language phrase enumeration | unbounded phrase lists cannot provide semantic coverage, so this fix documents a bounded lexical policy and independent corpus
  • r-shwt66 5efa206 — checking --git-dir/shallow | linked worktrees keep the shallow marker in the common Git directory
  • r-fix760 fb8ba45 — testing commits with more than one parent in dogfood scope | their platform-generated merge messages carry no authored decision, so requiring a record would require one nobody wrote
  • r-refint74 572f573 — allowing a note to extend a commit record under the same Record-Id | notes are remote-reachable, so divergent content would inherit a human-approved identity
  • r-warn75 24c7cc8 — accepting run-on or indented trailers | Git treats them as prose and accepting them would make records ambiguous
  • r-doctor72 996bcde — choosing either parent doctor list | each drops a runtime check required by the other incident
  • r-fix067 a915af0 — treat an outside-repository file_path as a legitimate no-record result | only an in-repository query can truthfully establish that no records apply; silence would hide a broken matcher
  • r-fix063h f0bb995 — leave hook runtime as a separate contradictory check | a fix-bearing installation check must not report ok or warn when the installed hook demonstrably fails
  • r-fix063 0b8c496 — add the explicit refspec only after confirming the remote ref exists | setup would depend on today’s remote state and require another doctor run after the first notes push
  • r-fix053 ecc4b90 — add a public mirrored boolean | the per-record sources array already models contributing channels, so a second representation would create drift
  • r-fix055 43b40f8 — infer whether a quote supports Verified | deterministic text matching can prove presence, not that a check ran
  • r-fix055 43b40f8 — downgrade harvested Verified to reconstructed | it preserves a citation-bearing assertion the verifier cannot substantiate
  • r-fix054 664d4e2 — write X-Inherited-From into commit messages too | transport metadata would lengthen every preserved user-facing message merely to restore symmetry
  • r-fix054 664d4e2 — stop squash-preserve from writing notes | it discards the mirror instead of fixing the query seam
  • r-fix056 55cb8bc — reuse the injection omission list for blocked withholding | it includes prose-bearing Evidence and Expires, so it is not a safety boundary
  • r-7b26f1 ec070ec — retrying the read inside the transaction | a write lock held across a subprocess is a worse failure than the one being fixed
  • r-1e58d3 1fd0d53 — removing the recorded-path branch entirely | a clone is on no PATH and in no node_modules (ADR-0011), and that branch is the only thing that finds the CLI there
  • r-1e58d3 1fd0d53 — hashing the recorded binary at install time | shasum is not guaranteed on every machine a hook runs on, and a check that silently no-ops is worse than the one being replaced
  • r-9c74b3 68340e4 — withholding only in --json | a shell agent reads stdout, and the text form is what it reads
  • r-4b17f8 7efba5c — retrying the read inside the transaction | a transaction holding a write lock while it shells out to git is a lock held across a subprocess
  • r-7a48c3 b85d847 — a CONFLICTED lifecycle state for divergent declarations | see above — it would block work on records that have a correct answer
  • r-8d51a6 27f73b0 — filtering blocked matches out of the result | the caller needs to know something matched; withholding is a rendering decision, made once
  • r-8d51a6 27f73b0 — reusing exit 1 for "could not check" | 1 already means a broken invocation, and a hook that cannot tell a bad flag from an unreadable repository will treat both as noise
  • r-2f7d94 a7673d0 — an allow-list of free-text keys | it is the shape of the original bug, and a new key would be unguarded until someone remembered
  • r-2f7d94 a7673d0 — leaving the wording generic ("a trailer") | an operator needs to know which line to edit, and the key was available two frames up
  • r-4e29b7 66829bb — folding this into the existing notes field | they are independent axes and can co-occur; one enum would have to enumerate the product
  • r-4e29b7 66829bb — throwing on an unreadable repository | context runs from a hook on every edit, and an exception there is a broken editor rather than a refusal
  • r-1c47e9 0e9930b — dropping the check | the two installation failures it exists for are real and were both invisible to configuration reads
  • r-1c47e9 0e9930b — probing whichever file is newer | "which artifact is this installation" is a fact about the layout, not about timestamps
  • r-3d92a8 f85101a — keeping the searches first and fixing the shim | the shim belongs to npm, not to us, and the version-skew problem survives the fix
  • r-3d92a8 f85101a — a config-only hook check | it was written, it reported ok, and the hook failed on the next commit
  • r-7c05e2 218ea28 — fetching notes automatically when the ref is missing | a query is a read, and silently reaching the network on a read is a surprise that belongs to git fetch
  • r-7c05e2 218ea28 — leaving it to doctor | doctor is run by a person once, and the answer that misleads is the one an agent gets on every task
  • r-7c05e2 218ea28 — a diagnostic string alone | the field it qualifies is records: [], and prose is not something a consumer can branch on
  • r-9b31c7 e8d45fb — keeping the placeholder until author trust was configurable | the placeholder was the permissive direction, so waiting meant shipping the hole
  • r-9b31c7 e8d45fb — withholding blocked payloads from the CLI too | a person reading a terminal can disbelieve a sentence; a tool result is retrieved fact

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

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

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant