Skip to content

doctor --fix no longer breaks git fetch (#63) - #68

Merged
MongLong0214 merged 3 commits into
devfrom
bug-issue-63
Jul 27, 2026
Merged

MongLong0214 merged 3 commits into
devfrom
bug-issue-63

Conversation

@MongLong0214

Copy link
Copy Markdown
Owner

Closes #63. The published install sequence left every reader's clone with a broken git fetch.

The incident

README.md §"Install from the clone" step 3 is doctor --fix. It wrote +refs/notes/commitlore:refs/notes/commitlore into remote.origin.fetch. An explicit refspec naming a ref the remote lacks is fatal to the entire fetch. doctor then read back its own config line and reported ok.

Verified in both directions, against the incident

dev            → doctor --fix → git fetch   exit 128    ← reproduces
bug-issue-63   → doctor --fix → git fetch   exit 0
README's four lines, real origin, patched bundle → git fetch  exit 0

Run by me in three clean clones, not taken from the delegation report.

The wildcard +refs/notes/*:refs/notes/* is not fatal when it matches nothing; the explicit form is. Ruled-out: records the option not taken.

The check now proves the outcome

before:  ok  notes fetch refspec — origin fetches refs/notes/commitlore
after:   ok  notes fetch refspec — git fetch succeeds for origin and covers refs/notes/commitlore

f0bb995 applies the same correction to hook health, binding it to its runtime probe rather than to the presence of a hook file.

Why it was invisible here

git ls-remote origin 'refs/notes/*' was empty for this repository — CommitLore had never pushed its own notes mirror, while gitseed's origin carries one. The bug could not appear in the repository where it was being dogfooded and appeared in every new adopter's.

38aaff6 publishes the transport ref and records the uncomfortable half in Limit: — the mirror contains zero notes. HANDOFF.md claimed "refspec configured and ref exists"; it now states both facts.

CTO gate

  1. AC — incident reproduced on dev, fixed on the branch, README sequence re-run clean
  2. Tests run here — 1178 passed / 33 files, against dev's 1174 (+4)
  3. Adversarial — checked the fix does not break the working case: once a real notes ref exists, records still arrive
  4. Scope — Shallow clone: answers from 1 commit of history without saying history is truncated #66 and inject --hook-input is byte-identical on malformed input and on no-records: silent fail-open #67 share this shape and were deliberately excluded so each is reviewed against its own incident
  5. Consistency — HANDOFF's false line corrected rather than left standing
  6. Records — Ruled-out: carries the refspec option not taken; Limit: carries the empty mirror

The remote notes ref now exists, while the mirror still contains zero notes. The handoff now states both facts instead of implying records exist.

Ruled-out: leaving the notes ref unpushed | the handoff already claimed a remote ref existed, and publishing the empty transport ref makes that state observable without inventing records
Limit: the published mirror contains zero notes
Blast: local
Undo: easy
Certainty: firm
Record-Id: r-fix063m
Evidence: HANDOFF.md
CommitLore-Version: 2.0.0
Use a wildcard notes refspec that stays valid before the first mirror is published. Doctor now dry-runs fetch before reporting the refspec healthy, repairs the old exact refspec, and compares the local notes object with the remote before reporting push state as healthy.

Ruled-out: 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
Limit: doctor performs remote probes; an unreachable remote reports could not verify instead of ok
Warn: the wildcard fetches every ref under refs/notes, including notes owned by other tools
Blast: system
Undo: easy
Certainty: firm
Record-Id: r-fix063
Follows: r-7c05e2
Evidence: test/doctor.test.ts
Evidence: test/notes-availability.test.ts
Verified: 1178 tests passed and 1 skipped across 33 files; typecheck, build, and spec verification exit 0
CommitLore-Version: 2.0.0
The installation check can no longer report a valid stub and config as healthy when the paired hook execution fails.

Ruled-out: 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
Blast: module
Undo: easy
Certainty: firm
Record-Id: r-fix063h
Follows: r-fix063
Evidence: test/doctor.test.ts
Verified: 1178 tests passed and 1 skipped across 33 files; typecheck and build exit 0
CommitLore-Version: 2.0.0
@github-actions

Copy link
Copy Markdown

CommitLore — record lint

Trailers: clean — 3 commits in origin/dev..f0bb995965d3d234869b1c732708e885c206a58d
Active constraints: 11 limits · 50 ruled-out · 28 warnings — from 34 records over 11 changed paths

Active constraints for the paths this PR touches

Limits (11)

  • r-fix063 0b8c496 — doctor performs remote probes; an unreachable remote reports could not verify instead of ok
  • r-fix063m 38aaff6 — the published mirror contains zero notes
  • 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
  • r-c4d9a2 e64f777 — New session inherits no memory of this one
  • - 2778d12 — New session starts with no memory of this one

Ruled out (50)

  • 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-fix063m 38aaff6 — leaving the notes ref unpushed | the handoff already claimed a remote ref existed, and publishing the empty transport ref makes that state observable without inventing records
  • 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-3b57e2 30f2d5f — converting the three translated READMEs for consistency | they are the product, not the record, and two checks exist specifically to keep them
  • r-9e46b8 b1989dc — reproducing the July 26 sections verbatim | the tickets they sequence are all delivered and the subagent failure they warn about no longer applies
  • r-2c94f7 3208e31 — reproducing the July 26 version | it describes a state forty commits old — before the production re-review, the branch migration, and the benchmark void — and restoring it would re-create the exact document whose staleness was the reason to delete it
  • 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-4d18e6 3f0acb0 — rewriting it against today's state | it would be true for one day and then quietly wrong again, which is the whole problem
  • 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
  • r-9b31c7 e8d45fb — dropping blocked records from the MCP answer entirely | an agent that silently receives less than there is cannot notice, and cannot audit
  • r-6c48b2 aaadedf — matching the whole file at edit time | the file contains everything the agent did not write, and GUARD-CANNOT-BLOCK measured prose surfaces producing false alarms specifically on compliant agents
  • r-6c48b2 aaadedf — blocking on a match | the score bands overlap, measured
  • r-6c48b2 aaadedf — running T-705 · guard route benchmark — measure the path SPEC §5 assigned to Ruled-out #37 without this pre-check | an arm that fires zero times measures nothing, and 120 runs is an expensive way to learn that
  • r-5b9e37 010782c — baking the resolved path into the hook stub | hooks status is a byte comparison against commitMsgStub(), so every hook installed from a different checkout would report outdated forever
  • r-5b9e37 010782c — an npx fallback | the existing comment is right — npx --no still queries the registry when the package is absent, putting a network call on every commit and breaking offline commits
  • r-2f9c40 07f47ca — wiring guard into the plugin as a blocking hook | true and false positives occupy the same score band on real agent output, so the only precision-safe threshold catches 1 of 5 and every useful threshold blocks four compliant edits in twenty-five
  • r-2f9c40 07f47ca — raising RECORD_ID_WEIGHT's threshold instead of gating the signal | the false alarms scored 1.0000, so no threshold below the maximum excludes them and the maximum excludes everything
  • r-2f9c40 07f47ca — semantic matching to separate the populations | ADR-0002 keeps the core LLM-free and zero-cost, and B-04 · Optional embedding-search tier #31 registers embeddings as opt-in — nothing measured here justifies moving that into the core
  • r-7a3e91 cf859e4 — inlining spec/SPEC.md and the schema into the bundle | SPEC.md would need a codegen step that itself needs a drift guard, and the package-root walk removes the reason to want it
  • r-7a3e91 cf859e4 — replacing the tsc output with the bundle | test/cli.test.ts, test/hooks.test.ts and test/mcp.test.ts import dist internals by path
  • r-4e9c72 a7a7e26 — fail when the index is missing or stale | it would be the first thing users learn to ignore, and then a real failure is quiet too
  • r-9a5e17 6d68703 — let each command edit src/cli.ts | guaranteed conflict, and the conflict surfaces only after every worker has finished
  • r-9a5e17 6d68703 — npx fallback in the hook stub | a network call on every commit, and offline commits start failing
  • r-c4d9a2 e64f777 — leave the old handoff | it predates the spec landing and the teammateMode fix, so it would send the next session down a path that is already done
  • - 2778d12 — rely on git log alone | decisions and the outage are not reconstructable from commits

Warnings (28)

  • r-fix063 0b8c496 (claim) — the wildcard fetches every ref under refs/notes, including notes owned by other tools
  • r-fix055 43b40f8 (claim) — Verified remains valid protocol vocabulary for facts recorded from actual command or test execution; only harvest refuses it
  • r-3b57e2 30f2d5f (claim)bench/PREREGISTRATION.md is append-only and was translated in place. Its section numbering and order are unchanged, but a translation is still an edit to a file whose whole discipline is that it is not edited. Recorded here rather than left to be noticed
  • r-9e46b8 b1989dc (claim) — this file still has no gate. It is accurate at this commit and nothing enforces that it stays so — the first line says as much
  • r-2c94f7 3208e31 (claim) — this file has no gate. phase-gate.py does not check it and CI does not read it. It will drift, and the first line tells the reader that
  • r-7b26f1 ec070ec (claim) — the concurrency test is deterministic rather than sleep-based, so it proves the transaction boundary and not the absence of every race
  • r-1e58d3 1fd0d53 (claim)COMMITLORE_BIN still accepts any executable, deliberately — a harness must be able to aim the hook at a specific build. It is now reported rather than restricted
  • r-9c74b3 68340e4 (claim)context now prints [blocked] beside a record whose payload is gone, which is more visually alarming than the old silent leak. That is the intended direction — a withheld record should be conspicuous — but it changes what a clean repository's output looks like the first time someone commits a Warn: that trips a pattern by accident
  • r-1a63f5 2bb4993 (claim) — "CI is green" was said five times today against a red CI, including in the commit that introduced the rule saying to check CI before saying it. The rule is in docs/RELEASE-GATE.md §5 and it was not followed by its own author. This commit is not claiming CI is green; that claim comes after the run reports
  • r-4b17f8 7efba5c (claim)deleteNoteRows opens its own transaction inside the new outer one. better-sqlite3 nests these as savepoints; node:sqlite has neither, so ADR-0012's migration must flatten this rather than assume it works
  • r-7a48c3 b85d847 (claim) — these two changes were developed concurrently in one worktree and share a built dist/. Splitting them would leave one commit whose dist/ did not match its src/, so they land together and are described together
  • r-5c92e0 73b1285 (claim) — the delegate reported "943 passed" for a suite whose baseline is 1108. It ran while another task was writing to the same worktree and collected a partial set. The real count, verified here on a quiet tree, is 1109 across 31 files — but a delegated test count is now a claim to check, not a result to accept
  • r-8d51a6 27f73b0 (claim) — guard stays advisory. Nothing here makes it block, and GUARD-CANNOT-BLOCK still holds — the point is that it no longer lies about what it saw
  • r-2f7d94 a7673d0 (claim)Evidence: and Expires: are now scanned. Both usually hold paths and dates, so a false positive there withholds a legitimate record. No case is known; a legitimate record carrying a path, a URL and a date was checked and passes
  • r-4e29b7 66829bb (claim)historyAvailability spends two git invocations per query. Both are metadata reads, but this is a hot path and nothing measures it yet
  • r-1c47e9 0e9930b (claim) — this is the second defect in three days from assuming the development checkout is the deployment. The first was exec node in the run script
  • r-3d92a8 f85101a (claim)hook-runtime executes the hook on every doctor run. The probe message is valid so nothing is written, but it is no longer a read-only command
  • r-3d92a8 f85101a (claim) — the check pins PATH to /usr/bin:/bin, which assumes git is there. On a system where it is not, this reports a hook failure that is really a probe failure
  • r-7c05e2 218ea28 (claim)notesAvailability runs git rev-parse and up to two git config reads on every query. Config-only, no network, but it is not free on a hot path
  • r-9b31c7 e8d45fb (claim) — the default is now fail-closed on every route — with no --trusted-author, every Warn: grades claim. That is SPEC §7 and it is what inject already did, but a user who saw [directive] yesterday will see [claim] today
  • r-6c48b2 aaadedf (claim) — recall here is against four re-proposals. It is a go/no-go signal for whether an arm has anything to measure, not an effect size
  • r-5b9e37 010782c (claim)commitlore.bin and commitlore.node are local config, so they do not survive a fresh clone of a repository whose hook was installed elsewhere — re-run hooks install there, which is what the failure message now says
  • r-2f9c40 07f47ca (claim) — --require-content changes precision, not recall; it removes a false-alarm class and catches nothing new
  • r-7a3e91 cf859e4 (claim) — hardcoding ../ counts back to the package root is what broke this — new code reads assets through installedPath(), never through import.meta.url
  • r-4e9c72 a7a7e26 (claim) — doctor reads the index read-only and never rebuilds it -- a diagnostic that repairs on sight hides how often the repair was needed
  • r-9a5e17 6d68703 (claim) — commands are advertised in --help only once they work -- test/cli.test.ts holds the landed and unlanded lists, and moving a name between them belongs in the commit that wires it
  • r-c4d9a2 e64f777 (claim) — Delete HANDOFF.md once the new session picks it up
  • - 2778d12 (claim) — Delete HANDOFF.md once the new session has picked it up

git log --follow accepts exactly one pathspec, so renames are not followed for 11 paths; query one path at a time to follow its rename chain

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 ca1f4ce into dev Jul 27, 2026
5 checks passed
@MongLong0214
MongLong0214 deleted the bug-issue-63 branch July 27, 2026 11:03
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.

doctor's own fix breaks git fetch and git pull, then reports ok

1 participant