Skip to content

Bind the version a caller asked for to the runtime that answers - #566

Merged
MongLong0214 merged 2 commits into
mainfrom
bind-installed-version
Aug 12, 2026
Merged

Bind the version a caller asked for to the runtime that answers#566
MongLong0214 merged 2 commits into
mainfrom
bind-installed-version

Conversation

@MongLong0214

Copy link
Copy Markdown
Owner

Blocking finding 1 of the third production-readiness review, plus the recovery gap it sits next to.

The defect

place a clean v0.7.1 checkout at .../commitlore/v0.8.0/
install exit=0
wrapper --version -> 0.7.1

The installer checked that a checkout matched its own manifest and that --version ran. It never asked whether it was the version requested. A colleague upgrading is told the new release is installed and keeps running the old one.

Binding, two-sided

side question catches
checkout HEAD ↔ requested tag is this directory the release it is named for? a mislabelled or hand-copied tree, before anything is activated
runtime --version ↔ requested version does the thing the wrapper runs agree? a tree that passes the tag check and still ships something else

Failing either is a refusal, never a deletion — a directory the installer cannot identify may not be its own.

The refusal now goes somewhere

It named the unusable path and stopped. It now prints the single deliberate removal that returns the transaction to an installable state, quoted for a shell and scoped to the checkout alone. Verified end to end:

refusal exit=3          wrapper exists? no
(follow the printed rm -rf verbatim)
after repair exit=0     commitlore --version -> 0.8.0

The plugin entry point had the same shape

scripts/commitlore-run.sh tried commitlore on PATH before the plugin's own bundle, so a machine with both served every edit from whichever CLI was installed — silently.

plugin route (CLAUDE_PLUGIN_ROOT set):   0.8.0     <- its own bundle
non-plugin route:                        0.7.0     <- PATH, still correct there

A plugin is a versioned release artifact, so its hook runs its own bundle; outside a plugin invocation PATH is still right, because there is no bundle to bind. test/plugin-entry-point.test.ts asserted the old order on purpose, so the expectation is flipped on purpose and its comment says why.

Why CI changed

The install gate tagged its checkout v9.9.9 and installed that. Version binding makes that permanently unsatisfiable, so the gate now tags and installs the version in package.json and asserts the wrapper reports it. Windows gains the wrong-tag refusal and a repair round trip — a repair path is only evidence if it is executed.

Verification

  • 97 cases pass across install-script, install-ps1, plugin-entry-point, manifest, uninstall
  • the reproduction above, run against this branch, in both directions
  • typecheck and the separate bench typecheck clean; two builds byte-identical
  • PowerShell cannot run here — only the install-ps1 required job is evidence for Windows

The installer verified that an existing checkout matched its own manifest and
that `--version` ran. It never asked whether the checkout was the version that
had been requested. A directory named for one release could hold another, and
the install reported success:

    place a clean v0.7.1 checkout at .../commitlore/v0.8.0/
    install exit=0
    wrapper --version -> 0.7.1

A colleague upgrading is told the new release is installed and keeps running the
old one, including every defect the upgrade was for.

Binding is now two-sided, because either side alone can lie. The checkout's own
HEAD must resolve to the requested tag, so a mislabelled directory is caught
before anything is activated; and the runtime that the wrapper will actually
execute must report the requested version, so a tree that passes the tag check
and still ships something else is caught by the smoke test. A checkout that
fails either is refused, never deleted -- destroying a directory because it
failed a version check is a worse answer than declining to use it.

The refusal now carries a way forward. It named the unusable path and stopped,
which left an operator with a message and no next step; it now prints the one
deliberate removal that returns the transaction to a state a rerun can install
into, quoted for a shell and scoped to the checkout alone.

The plugin entry point had the same shape. `scripts/commitlore-run.sh` tried
`commitlore` on PATH before the plugin's own bundle, so a machine carrying both
served every edit from whichever CLI happened to be installed, silently. A
plugin is a versioned release artifact and its hook now runs its own bundle;
outside a plugin invocation, PATH is still the right answer, because there is no
bundle to bind. When the bundle cannot run, the hook fails open rather than
answering with an unrelated installation's context and presenting it as the
plugin's.

`test/plugin-entry-point.test.ts` asserted the old order deliberately, so the
expectation is flipped deliberately, and its comment says why rather than
leaving the reversal to be inferred.

The CI install gate tagged its checkout `v9.9.9` and installed that. Version
binding makes that permanently unsatisfiable -- the tree reports its real
version -- so the gate now tags and installs the version in `package.json` and
asserts the wrapper reports it. Windows gains the wrong-tag refusal and a repair
round trip, because the repair path is only evidence if it is executed.

Limit: this binds the requested tag to the runtime that answers, not the tag to its content -- a tag moved after publication installs whatever it now points at, which is a signing question rather than a version-binding one
Ruled-out: deleting a checkout that fails the version check | a directory the installer cannot identify may not be its own, and refusing costs an operator one command while destroying it may cost them something unrecoverable
Blast: system
Undo: easy
Certainty: firm
Verified: ninety-seven cases pass across install-script, install-ps1, plugin-entry-point, manifest and uninstall; the reproduction at the top now exits 3, activates nothing and leaves the previous wrapper untouched, and following the printed repair verbatim installs and reports the requested version; the plugin route answers from its own bundle while a shadowing PATH entry still wins outside a plugin invocation; both typechecks clean and two builds produce a byte-identical dist
Provenance: drafted
Record-Id: r-vbind001
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

CommitLore — record lint

Trailers: clean — 2 commits in origin/main..99667f31cc1297b6b73218198d481c7193581abd
Active constraints: 32 limits · 103 ruled-out · 20 warnings — from 49 records over 7 changed paths

Active constraints for the paths this PR touches

Limits (32)

  • r-wrongtag1 99667f3 — PowerShell cannot be run here, so this is reasoned from the shared design and proven against the shell twin; only the windows-latest job is evidence for install.ps1
  • r-vbind001 2c88d24 — this binds the requested tag to the runtime that answers, not the tag to its content -- a tag moved after publication installs whatever it now points at, which is a signing question rather than a version-binding one
  • r-insttxn1 afb7bfb — this establishes that the installed tree is complete and its commands run on this machine at this moment -- not that the machine will still have a working node tomorrow, and not that any agent host will load what was installed
  • r-codexreg1 5933aa4 — an entry can be correct when the installer reads it and wrong afterwards -- a later install, a moved data root or a hand edit all leave the name intact, and nothing revisits it until the installer runs again
  • r-codexplug e5fe95a — a plugin can put a skill in front of a session; it cannot make the session follow it, and nothing here reports whether one did
  • r-hermesx01 2eb8176hermes skills inspect resolves remote sources only in this Hermes version, so discovery was verified through hermes skills list --source all in a fresh isolated profile rather than from inside a live conversation; that a session then follows the procedure is not something an installer can establish
  • r-codexwire 955f290 — an instruction file is guidance, not enforcement -- a host may ignore it, summarise it away, or never read it, and nothing here can tell whether any session followed the procedure
  • r-pindigest c5a7cfa — pinning fixes the tree, not its behaviour -- a pinned action still runs with the job's token and can read this repository, and upgrading now requires deliberately resolving a new digest rather than inheriting one
  • 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-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-winsupported1124 6333251 — repositories that installed the hook before Windows: the commit-msg hook hangs instead of returning, and #71's containment can never match there #321 keep the old stub and must re-run commitlore hooks install; a corrected release does not reach them, and this row's claim is about a working install
  • r-winderive1124 282693b — a diagnostic that retypes the code it describes goes stale silently, because nothing fails when the two drift apart -- it has to be read out of the artifact that ships
  • r-winbound1124 156deed — an unbounded hang is not evidence; it is a job that dies at the runner timeout having printed nothing, so every hook-invoking commit here has to carry its own bound
  • r-winassert1124 616005d — an assertion whose only oracle is an absent side effect cannot distinguish "refused" from "the mechanism never worked", so each one needs a positive control that fires before the attack
  • r-winshell1124 aa68a9a — a GitHub Actions bash step is invoked as bash -eo pipefail, so set -uo pipefail inside the step does not clear -e and any bare command that fails ends the step at that line
  • r-ps1scope282 72f23df — the hook-runtime probe spawns /bin/sh, so it cannot report on a Windows hook at all until T-1124 changes it
  • r-ps1stderr282 e37b4d3 — Windows PowerShell 5.1 turns a native command's stderr into a terminating error under $ErrorActionPreference = Stop, so no native call in this script may merge stderr into its output
  • r-ps1shell282 97735d6 — a step's shell key takes no expression, so a per-host matrix cannot select the interpreter
  • r-t1120nodeinst 14deeb4 — git and node are hard prerequisites now, so a host without them installs nothing and says which one is missing
  • r-instverify256 3715677 — the root cause of the signal kill is unestablished; this makes the installer honest about it rather than fixing it, and Documented install exits 137 on upgrade: a killed verification turns a successful install into a failure #256 stays open for the cause
  • r-instpath119 9e1fce7 — a user who ignores the printed line still gets "not found" on the next command
  • 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-fix067 a915af0 — PreToolUse hook failures must always exit 0 and never change stdout's hookSpecificOutput contract
  • 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-3b8f52 1f8b4be — the figure is one machine, one run — it establishes the order of magnitude, not a regression baseline anyone should tune against
  • r-0c5d38 aeb54a6 — the suite runs against the source tree, so no test in it can observe what packaging drops
  • r-6f2e58 ea9ae6d — a library test and a binary test cover different failures, and the packaging layer between them is exactly where a working module becomes a broken install
  • r-7e5f02 e5f5e00 — npm installs through an engine mismatch, so the ecosystem's own signal cannot be relied on to stop anything
  • r-3a9d68 6a3fc3b — a test runner reports what ran, and nothing in its summary distinguishes "did not run" from "does not exist"
  • r-8e2d51 ef93c0e — git is the parser, so a git version that folds or bounds trailers differently is a correctness problem for us, not a compatibility footnote

Ruled out (103)

  • r-vbind001 2c88d24 — deleting a checkout that fails the version check | a directory the installer cannot identify may not be its own, and refusing costs an operator one command while destroying it may cost them something unrecoverable
  • r-insttxn1 afb7bfb — treating "verification could not run" as fatal | it fails a good install over a missing optional tool, which is the failure the non-fatal policy was introduced to stop
  • r-codexreg1 5933aa4 — replacing any entry named commitlore | a user may run their own server under that name, and taking it because the name matched is the failure this fix is about, pointed the other way
  • r-hermesx01 2eb8176 — writing the bundle into the profile directory and regenerating its manifest | it would be undone by the next sync and disagree with the manifest until then
  • r-codexwire 955f290 — a Codex-specific integration | the instruction surface is shared, and writing one integration per host would leave the same gap open for the next five
  • r-pindigest c5a7cfa — upgrading to the latest major while pinning | two changes in one, and a version bump that arrives inside a security fix gets reviewed as a security fix
  • r-pindigest c5a7cfa — a dependency bot to keep digests current | the pin is what removes silent movement, and a bot that updates it automatically restores the property being removed
  • 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-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-winsupported1124 6333251 — Flipping the cell on the throwaway verification head | the ticket requires the attacks to pass in a required job in this ticket's own pull request, and a head that exists to be discarded is not that
  • r-winsupported1124 6333251 — Leaving the assertion pinned and editing only the document | the two contradict each other, so the suite would fail; a guard that blocks the change it was guarding for is a defect in the guard
  • r-winsupported1124 6333251 — Widening this ticket to the hook fix so it could be self-contained | the stop condition sent that to T-1127 for good reason, and this ticket's job was to measure and then say the word once measurement allowed it
  • r-winderive1124 282693b — Updating the two hardcoded lines to match the fixed stub | it would be correct today and wrong at the next change, for the same reason it is wrong now
  • r-winderive1124 282693b — Dropping the two diagnostics and relying on the commit alone | the commit says whether the hook worked, not which branch decided it, and that is the reading the next Windows question starts from
  • r-winbound1124 156deed — Fixing the walk in src/hooks/commit-msg.ts as part of this change | the stub is not this ticket's to own, and the ticket says to record a Windows failure as its own issue rather than absorb it
  • r-winbound1124 156deed — Instrumenting the stub to report its internal state | same ownership objection, and the chained-hook slot already reads the same environment without touching it
  • r-winbound1124 156deed — Raising the job timeout and letting the hang run to completion | it never completes, and the assertions behind it would never execute
  • r-winassert1124 616005d — Keeping the extensionless payload for attack 2 | the stub execs a recorded value only inside the .mjs|.js arm, so an extensionless target is refused by a branch that was never going to run it, and the step asserted its own control flow back to itself
  • r-winassert1124 616005d — Calling attack 2 evidence of containment once the payload is a .cmd | it guards a loosening, it does not exercise the install-root check; conflating the two is what would let a green sticker land on an untested branch
  • r-winassert1124 616005d — Keeping the chained-hook slot as the probe | it was never invoked, and a scratch repo with its own hook measures the platform without depending on the stub's argv0 handling
  • r-winassert1124 616005d — A trailing rm for the planted file | it is skipped by every early exit, and the install root is shared by every later step on the runner
  • r-winshell1124 aa68a9a — set +e at the top of each step | it also disables the abort for genuinely unexpected failures, where stopping is the right answer; the || form is scoped to the one command whose failure is a measurement
  • r-winshell1124 aa68a9a — Reading the outcome from the step's own exit code instead | 124 tells you a bound fired somewhere, not which commit, and it discards the probe output that explains why
  • r-ps1scope282 72f23df — Making the hook probe pass by installing a shim named commitlore with no extension | Windows will not execute it, and inventing a second artifact to satisfy a probe is the wrong direction; the probe is what needs to learn about Windows
  • r-ps1scope282 72f23df — Committing through the hook in this job to prove the hook works | the hook path on Windows is exactly what is unestablished, so the assertion would be testing T-1124's subject in T-1121's job
  • r-ps1scope282 72f23df — Dropping the doctor call so the log stays clean | its output is the evidence T-1124 starts from, and a clean log that hides it would cost the next ticket the finding
  • r-ps1stderr282 e37b4d3 — Setting $ErrorActionPreference to Continue for the whole script | it would also silence the failures this script exists to report, to fix one call that should not have merged streams
  • r-ps1stderr282 e37b4d3 — Dropping --depth 1 so the warning never appears | the warning is specific to a local source, which only the tests use; a shallow clone is right for the remote case this ships for
  • r-ps1stderr282 e37b4d3 — Treating any stderr output as failure and reporting it | that is the behaviour being removed, and it cannot tell a warning from a fatal
  • r-ps1node282 bce9d3e — Choosing the newest of the matches | the shim has to run the node the user's own PATH resolves, and picking a different one would install a tool that behaves unlike their terminal
  • r-ps1node282 bce9d3e — Failing when PATH carries more than one node | two entries is an ordinary Windows machine, not a misconfiguration to refuse
  • r-ps1shell282 97735d6 — Duplicating the job once per host | eighty lines twice, kept in step by hand, to assert the same things about two interpreters
  • r-ps1shell282 97735d6 — Running everything through cmd with the host as a variable | every assertion would become a quoted one-liner passed to -Command, which is the least readable form of the most important job here
  • r-ps1shell282 97735d6 — Testing only PowerShell 7 | 5.1 is what a Windows machine has before anyone installs anything, and a 7-only construct is a parse error for the whole file
  • r-instps1121 7bf6ced — Writing the user-scope PATH with SetEnvironmentVariable | two active records on install.sh reject an installer that changes the user's environment behind their back, and the Windows registry spelling of that act is not a different decision
  • r-instps1121 7bf6ced — An extensionless shim matching the shell wrapper's name | Windows will not execute it, and the git hook resolves through recorded config rather than PATH, so the name would buy nothing
  • r-instps1121 7bf6ced — Testing only under PowerShell 7 | 5.1 is the version a Windows machine already has, and a 7-only construct is a parse error for the whole file rather than a failure in one branch
  • r-instps1121 7bf6ced — Requiring a helper for JSON merges the way install.sh needs jq | PowerShell parses and writes JSON natively, so the cannot-merge branch has no counterpart and inventing one would be a copied limitation
  • r-instps1121 7bf6ced — Passing the version through $args so irm | iex could take one | iex gives a piped script no arguments, so the pinned form is documented as a scriptblock invocation and COMMITLORE_VERSION is accepted as well
  • 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-inst305grep e55df45 — Leaving grep -m1 because every shell that matters supports it | that argument applies word for word to sort -V, which was removed two commits earlier, so accepting it here makes the POSIX constraint something the file follows selectively
  • r-inst305grep e55df45 — Keeping the assertion specific to -m | the flag that got in was not the one being watched for, so the check has to cover the option set rather than a known offender
  • r-inst305grep e55df45 — Replacing mktemp -d in the same change | it is not POSIX either, but it is inherited rather than introduced here, and one fix per cause keeps the reasoning reviewable
  • r-inst298sort eb93ba5 — Keeping sort -V and documenting the requirement | the record on this file already rules out bash arrays for being non-POSIX, and a sort extension is the same assumption with a quieter failure
  • r-inst298sort eb93ba5 — Sorting with sort -n on each field separately without padding | the major field is the one that matters and -n on a field containing a leading v does not compare it numerically, which is how the original got this wrong
  • r-inst298sort eb93ba5 — Allowing pre-release tags in the resolution | they tie with the release on the padded numbers and then win on string length, so an rc would be installed by default
  • r-inst298sort eb93ba5 — Asserting the absence of the string sort -V anywhere in the file | that forbids the comment explaining the fix, so the check reads code lines only
  • r-t1120fatal 0412668 — Reading the remaining failure by inference from the generic advice line | two cycles already went that way, and the informative line was present in the log the whole time
  • r-t1120cijob 1871106 — Keeping the separate staged clone and adding more mount configuration | each addition would be configuration protecting a copy that exists for no reason once the checkout can be cloned directly
  • r-t1120cijob 1871106 — Dropping the container steps and relying on the vitest suite | the suite runs on a machine that has node and git; the point of a bare image is that the prerequisite is genuinely absent, which is the assertion the active record on this file asks for
  • r-t1120clonemsg 55ffb0a — Keeping the guessed message and fixing only the CI mount | the guess would still be printed for every other clone failure, and this one was found only because a container happened to hit it
  • r-t1120clonemsg 55ffb0a — Letting the clone's stderr through to the terminal unfiltered | it interleaves with the installer's own log lines and a piped installer should speak in one voice; the last lines quoted into one named error keep both
  • r-t1120nodeinst 14deeb4 — Hardcoding the release version in install.sh as the default | it would add a bump obligation to every release and a stale default between a tag and the bump; git ls-remote resolves the newest tag with no API token and no rate limit
  • r-t1120nodeinst 14deeb4 — Defaulting to a branch when no tag is given | installing a moving target is what pinning exists to prevent, and the release gate already forbids a dev-referencing one-liner
  • r-t1120nodeinst 14deeb4 — Writing the wrapper with cp over the destination | an in-place overwrite of a file that may be executing is the defect that forced a same-day patch release; the wrapper is written beside the target and renamed
  • r-t1120nodeinst 14deeb4 — Letting post-install verification decide the exit code | that was the other half of the same defect, where a killed --version became the installer's exit status; verification now retries once and reports, and an install that succeeded exits 0
  • r-t1120nodeinst 14deeb4 — Editing the shell profile when the wrapper directory is not on PATH | an active record on this file rejects it, and printing the line is what this script does about PATH
  • r-t1120nodeinst 14deeb4 — A test-only environment hook to force a verification failure | a source repository whose bundle exits non-zero is a real broken release and needs no seam in shipped code
  • r-t1120nodeinst 14deeb4 — Checking for git with command -v alone | a git that cannot execute is as useless here as a missing one, so the check runs git --version and catches both
  • r-instverify256 3715677 — silencing the verification | a verification that cannot run is worth saying out loud, and hiding it would trade a visible wrong exit code for an invisible unknown
  • r-instverify256 3715677 — treating the kill as fatal and failing louder | the install genuinely succeeded, so failing it is the wrong answer to a verification problem
  • r-instpath119 9e1fce7 — editing the user's shell profile from the installer | printing the line is honest, rewriting .bashrc silently is what makes people distrust curl-to-shell installers, and this project sells verifiability
  • r-rel0200a a074754 — bumping ci.yml's "v0.1.0 was published with zero attached assets" comments | those describe a historical fact about the actual v0.1.0 release, not a version this project declares; the check they document (releases/latest/download/SHA256SUMS returning 200) is written to start exercising the real path automatically the day any release ships assets, v0.2.0 included, with no workflow edit
  • r-rel0200a a074754 — touching docs/adr/ADR-0001-scope-v010.md, docs/tickets/release.md, bench/VERDICT-M1.md, HANDOFF.md, bench/ROUTE-GAP.md | planning and historical-record prose that names v0.1.0 as a past decision or measurement subject, not a live version carrier
  • r-rel0200a a074754 — changing test/mcp.test.ts's CommitLore-Version: 0.1.0 fixture trailer | that's protocol-version content inside a synthetic seed commit (what an old commit's trailer looked like), unrelated to and never asserted against package.json's version
  • r-instci99a 318b9a2 — pre-installing curl/jq/tar/git in the containers before running install.sh | would hide exactly the gap this work exists to find — Debian ships neither curl nor wget by default, and asserting that failure cleanly is the point of the bare debian:stable-slim step, not an obstacle to route around
  • r-instci99a 318b9a2 — publishing a -musl release target so Alpine could install successfully | a release.yml/build-matrix change, not an install.sh or CI-verification fix; out of scope here (DO NOT: no Docker in the release build matrix) and orthogonal to making the existing failure clear instead of a raw crash
  • r-instci99a 318b9a2 — heuristically detecting musl before download (checking for /lib/ld-musl-*.so.1) | executing the freshly extracted, already checksum-verified binary directly is a stronger, more general check — it catches any reason the binary cannot run on this machine, not only the one musl signature this repository happens to know the path for
  • r-instci99a 318b9a2 — a matrix job splitting debian/alpine | the two images need different assertions (debian: bare failure, then success once curl is added; alpine: one clean failure, nothing more to add) and share one staged release + HTTP server; one job with named, ordered steps keeps that shared setup and reads linearly in the log
  • r-inst96a 0f86924 — a second, separate installer script for agent wiring | one curl-pipe-sh command is the entire point of this file; a second command to discover reintroduces the problem Onboarding is 4-5 commands, and the install instructions sit ~100 lines into the README #96 exists to remove
  • r-inst96a 0f86924 — writing every agent's config unconditionally | never overwrite an existing config without saying so, never write one for an agent that is not installed — each wire_* function checks both before touching disk
  • r-inst96a 0f86924 — bash arrays for the wired/skipped/not-found accumulators | this script is POSIX sh; arrays are a bash extension. Plain files under the already-trap-cleaned $work directory instead
  • r-relinstall c6e1d04 — guessing the current version to build the asset URL directly | would need either the GitHub API (rate-limited, needs no-auth headers handled correctly) or trusting a redirect's final Location header parsing. Downloading the fixed-URL SHA256SUMS first and reading the real asset name back out of it needs neither and is what the checksum step has to fetch anyway.
  • r-relinstall c6e1d04local for scoping — not POSIX per se, but supported by dash, bash, and every shell this script is realistically piped into (verified directly, see Verified) | not used in the end; the script has few enough variables that scoping was not needed, only noted here because it was considered.
  • 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-5e91c7 f991389 — leaving pull_request as the only check for develop | a direct push to an integration branch is exactly what nobody reviews
  • 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-9c07e2 9c4d25a — invoking npx on every Edit | it puts a registry round trip on the hot path of every tool call, which is how a hook earns being uninstalled
  • r-9c07e2 9c4d25a — committing dist/ so the plugin is self-contained from a git clone | it puts build output in review diffs forever to save one background install
  • r-3b8f52 1f8b4be — enabling the large fixture for the whole default suite | generating 100k commits costs ~22s and the other tests in that file gain nothing from it, so it runs as its own step against its own file
  • r-7f31c9 750ab17 — reporting both datasets from one source list | readSources groups by condition and cannot separate repositories, so any second dataset with a commitlore-on arm silently corrupts the headline test
  • r-0c5d38 aeb54a6 — smoke with --version only | it passes with the schema missing, which is the failure worth catching
  • r-0c5d38 aeb54a6 — assert a file list in package.json | it goes stale silently, and installing is the actual question
  • r-6f2e58 ea9ae6d — pin the adoption sha in the workflow | it is the same stale cutoff the dogfooding suite was written to avoid
  • r-6f2e58 ea9ae6d — spawn git per commit to read trailers | one pass with %(trailers:key=…) uses git's parser and does not scale with history
  • r-7e5f02 e5f5e00 — hold Node 20 and downgrade both dependencies | paying maintenance cost to keep supporting an unpatched runtime
  • r-7e5f02 e5f5e00 — floor at 24 | excludes Node 22 users today for no benefit, and 22 outlives v0.1.0
  • r-7e5f02 e5f5e00 — drop engines entirely | the failure moves from install time to runtime, where it is someone else's confusing bug
  • r-3a9d68 6a3fc3b — pin an expected test count | it goes stale on the next file added, and a stale count reads as passing
  • r-3a9d68 6a3fc3b — keep threads and skip the index tests in CI | that is the same hole with the sign flipped, made deliberate
  • r-8e2d51 ef93c0e — pin one git version in CI | it would hide exactly the divergence the matrix exists to surface
  • r-8e2d51 ef93c0e — allow shallow checkout and skip dogfooding | a skipped gate reports the same green as a passing one

Warnings (20)

  • r-pindigest c5a7cfa (claim) — no comment can keep a digest honest, so the # v4 beside each is a label rather than a check; the version it names is verified once, here
  • r-plugres483 7dab3e3 (claim) — 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
  • r-instps1121 7bf6ced (claim) — nothing on this machine can execute this file -- there is no PowerShell here, so every claim about its behaviour comes from the Windows runner in this pull request rather than from a local run
  • r-nobin284 ab2f08f (claim) — an extensionless COMMITLORE_BIN is now ignored rather than honoured -- the hook falls through to the recorded install and to PATH, so a caller who pointed it at the wrapper still gets a working hook, by a different route
  • r-inst298sort eb93ba5 (claim) — only vMAJOR.MINOR.PATCH tags are auto-resolved now -- a release published under any other tag shape must be installed by passing it explicitly
  • r-t1120nodeinst 14deeb4 (claim) — keep install.sh ASCII only -- a non-ASCII character in a string silently terminated /bin/sh here while sh -n accepted the file
  • r-rel0200a a074754 (claim) — scripts/commitlore-bootstrap.sh is orphaned -- no hooks.json entry invokes it, and its npm-install strategy contradicts ADR-0011. It still carries a live version default, now bumped for consistency, but nothing exercises it. Worth a follow-up issue: either wire it up correctly or delete it.
  • r-instci99a 318b9a2 (claim) — the new binary-execution guard's die() message names musl/glibc specifically because that is the only way this repository's own release actually fails to execute today (glibc-only "-gnu" targets, verified against Alpine). If a -musl target is ever published, or the binary can fail to execute for an unrelated reason, this message will misdiagnose it — revisit the wording if that guard ever needs to distinguish causes.
  • r-inst96a 0f86924 (claim) — opencode's config shape is {"mcp": {"<name>": {"type": "local", "command": [...], "enabled": true}}} — a different key and a different command shape than every other client here ({"mcpServers": {"<name>": {"command": ..., "args": [...]}}}). Do not fold it into wire_mcp_servers_json without re-checking opencode's own docs if this ever gets refactored
  • r-fix067 a915af0 (claim) — the accepted tool set is the union of the plugin and settings matchers; aligning those matchers remains Shallow clone: answers from 1 commit of history without saying history is truncated #66
  • r-7f24a9 96d960a (claim) — this file is still NOT validated as a GitHub workflow. actionlint is unavailable and the sandbox cannot reach the API. Conflating a YAML parse with workflow validation broke gitseed's CI earlier today; the first run is the proof
  • r-7f24a9 96d960a (claim) — the same task was told to update the factory skill's Gitflow section and its phase-gate.py, and could not — it was launched rooted at this repository and had no write access to ~/.claude/skills. The gate still checks for a branch named develop and fails both repositories for having dev. My spec error, not the delegate's; it is the next task
  • r-5e91c7 f991389 (claim) — these files are NOT validated as GitHub workflows. actionlint is unavailable and the sandbox cannot reach the API, so only the YAML parse and structural assertions ran. Conflating those two broke gitseed's CI four commits ago; the first GitHub run is the proof
  • 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-9c07e2 9c4d25a (claim) — hooks here must exit 0 on every path — a non-zero exit from a PreToolUse hook blocks the edit, and no record is worth that
  • r-7f31c9 750ab17 (claim) — adding a file to README_SOURCES pools it into every aggregate in the block, including the significance test — check the arm names first
  • r-0c5d38 aeb54a6 (claim) — spec/ ships on purpose -- alternative implementations are told the fixtures are the contract, and this step is what keeps that true
  • r-7e5f02 e5f5e00 (claim) — raising the floor again needs an ADR superseding ADR-0010 -- a package.json-only change will pass tests and lie to users
  • r-3a9d68 6a3fc3b (claim) — any native addon added later needs the fork pool -- moving back to threads will silently drop whatever loads it
  • r-8e2d51 ef93c0e (claim) — keep fetch-depth 0 on any job that runs npm test -- the dogfooding suite fails deliberately on a shallow clone, and "fixing" that by relaxing the assertion removes the gate

the index is unavailable (the index has no baseline commit); answering with a full scan

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

withheld the content of 4 record(s) graded blocked: a Limit, Ruled-out, Verified trailers matching an injection pattern is reported, never quoted (SPEC §7)

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

The step meant to prove version binding on Windows tagged the requested version
and the wrong version at the same commit. A `--depth 1 --branch <wrong>` clone
of that repository therefore carried the requested tag too, pointing at its own
HEAD, so binding was satisfied and the install succeeded. The step asserted a
refusal, so it failed -- correctly, and for a reason that was the test's rather
than the installer's.

A name is not a difference. The wrong tag now names a commit built with
`commit-tree` over HEAD's own tree: the checkout stays internally valid, so the
runtime manifest and the smoke test still pass and cannot be what rejects it,
and the only thing left to catch it is the binding under test. Locally, against
the shell installer that mirrors this check, that shape exits 3, names the
mismatch and activates nothing.

The shell installer's own case never had this problem -- its fixture tags a
genuinely earlier commit -- which is why the gap showed up only on the side that
could not be run before pushing.

Limit: PowerShell cannot be run here, so this is reasoned from the shared design and proven against the shell twin; only the windows-latest job is evidence for install.ps1
Blast: local
Undo: easy
Certainty: firm
Verified: the corrected fixture produces a clone whose HEAD differs from the requested tag's commit, and the shell installer refuses it with exit 3, names the mismatch and leaves no wrapper behind; the test tags created for that check were deleted afterwards
Provenance: authored
Record-Id: r-wrongtag1
@MongLong0214
MongLong0214 merged commit 37eea66 into main Aug 12, 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