Release 1.1.3 - #760
Merged
Merged
Conversation
Two SVGs sat above the fold in incompatible art directions -- a warm editorial diagram and an animated dark terminal recording -- so the first screen read as two templates rather than one product. The recording is retired and the hero replaced. The new hero is a static 840x340 diagram: Git history on the left, current context in the middle, next edit on the right, with the active decision reaching the panel and the superseded one stopped by a dashed connector and an X. No animation, no external fetch, no gradient, 35 rendered words. Three departures from the handoff's reference skeleton, each because following it exactly would have shipped something worse: The skeleton put SUPERSEDED in the top slot, and the handoff's own rule says the superseded row must not take the eye first. The hero being replaced already had the active row on top, so copying the skeleton would have broken a property that already held. Newest-first also matches what `git log` shows, so a column labelled GIT HISTORY now reads the way the tool it names does. The skeleton's next-edit column mixed two x positions for one phrase and carried a green dot that competed with the arrow already supplying the accent -- a fourth green mark in the region the handoff ranks fourth for attention. The dot is gone under the "no decorative illustration" rule rather than under "the forbidden list does not mention it", which would be the weaker argument. Its removal is why the three lines needed realigning at all. Panel padding is 16 rather than 22 on both sides. "Checkout pricing only" at 24px lands within about two pixels of the old 284px limit on a wide system font, and this font stack resolves to whatever the reader's OS provides, so the margin was thin on somebody else's machine rather than on mine. `commitlore demo`, its scenario, and its tests are untouched. What is deleted is the README recording asset, the generator that existed only to produce it, and that generator's byte-identity test. Limit: at 375px the README embeds at width="100%", so the 840px canvas scales by 0.446 and the 18px labels land near 8px -- better than the 1200px canvas it replaces (7.5px) and still under the 12px a caption usually needs; three columns and this copy do not fit under a 700px canvas, so the canvas question is unresolved rather than answered Blast: local Undo: easy Certainty: firm Record-Id: r-onevisual Provenance: authored Verified: five negative controls each fail the intended assertion and pass after restore -- restoring the demo block, adding a `set` animation, replacing the alt with an absolute claim, deleting `desc`, and putting the superseded row back on top; 62 tests across the hero and README suites pass, and `git grep` finds no remaining reference to the retired asset or its generator outside historical tickets CommitLore-Version: 2.0.0
The visual replacement rewrote every hero alt to remove an absolute claim -- "a coding agent must not revive a decision the repository already reversed" -- because the product does not stop an agent from thinking of an idea again. `readme-positioning.test.ts` then failed in all four languages at once. That phrase was carrying two things. ADR-0022 requires decision-authority framing in the hero, and the phrasings the test accepts include the absolute ones: "must not revive", "되살려서는 안", "蘇らせてはならない", "不得复活". Removing the overclaim removed the positioning with it, and the handoff that asked for the removal did not know the sentence had a second job. Each language's accepted list also has a non-absolute first entry -- "decision authority", "결정 권위", "意思決定の権威", "决策권威" -- so the alt now carries the framing without claiming the product blocks anything. Both contracts hold. The collision is written into `readme-hero.test.ts` next to the assertion that caused it. Two tests that pull against each other pass today and say nothing about why; the next person to rewrite an alt without the authority phrase would fail a test in another file with no explanation of what the wording was for. Limit: the two contracts are still enforced in separate files, so nothing fails if one of them is deleted -- the note is what connects them, and a note is weaker than a check Blast: local Undo: easy Certainty: firm Record-Id: r-heroalt22 Provenance: authored Verified: 31 tests across readme-positioning and readme-hero pass together, and the earlier failure is reproduced by the run this fixes -- four "hero contains decision-authority positioning" cases, one per language, on head 3fc2ca7 CommitLore-Version: 2.0.0
The release does not need F16, and #742 must not close for it. Closing it as "specified, not shipped" would leave what ADR-0027 rejected in its own Rejected section -- "A deferral is a promise with no date." A specification living on `main` is not a date, an owner, or a reopening condition, and a closed issue is not a claim anybody can check. So it takes F15's shape instead, which is the one deferral in this repository that can be audited: the issue stays open, the tickets say they are not due, and two conditions are written so somebody can tell by hand whether they have fired. Neither is instrumented, and that is stated rather than papered over -- a counter nobody reads adds a number and changes nothing, while a condition written to be countable at least has an answer when asked. Condition 1 is an operator on some other machine finding out late. Condition 2 is a repository validating commits with an older build nobody chose. Both start at zero. The interesting part is what is excluded. #433 -- the plugin pinned three releases behind -- is the strongest evidence available and does not count, because it was pinned by a distribution defect rather than by somebody not knowing. F16 addresses the second and would not have helped with the first. Writing that down is what stops it being cited later as occurrence 1, which would fire the condition on a defect this feature does not address. One citation was dropped rather than carried. The claim that ADR-0032 forbade `doctor` from querying the plugin cache is not in ADR-0032, which is the doctor diagnostic model, and no ADR in this repository mentions a plugin cache at all. It reached me second-hand and read as exactly the kind of support this section wanted. Limit: neither condition can fire on its own -- both need a person to notice and write a comment, and a condition that depends on somebody remembering is one that under-reports rather than one that is safe Blast: local Undo: easy Certainty: firm Record-Id: r-f16defer Provenance: authored Verified: ADR-0027 line 89 carries the quoted sentence in its Rejected section; ADR-0032 is `ADR-0032-doctor-diagnostic-model.md` and contains no plugin-cache text, and `grep -rl 'plugin cache' docs/adr/` returns nothing, which is why that support was removed rather than reworded CommitLore-Version: 2.0.0
The previous commit removed a citation as unsupported: that ADR-0032 forbade `doctor` from querying the plugin cache. No ADR mentions a plugin cache, so the grep was right and the sentence was wrong.
The sentence was wrong; the support was not. It reached me with its subject changed -- "plugin cache" for "update lookup" -- and I checked the words rather than the claim. Reading ADR-0032:378-388 finds something stronger than what was passed on:
doctor's own process opens no socket -- no HTTP client, no update lookup
(... we do not ship the lookup at all), no telemetry, on any flag. [...]
Version skew is checked against *local* executables (inject-version), and
the #433 class of staleness is a distribution defect whose fix lives in
distribution -- `installSource` exists so the report can name which channel
to update, not so doctor can go ask the internet.
`grep -rln '#433' docs/adr/` returns that file and nothing else. It is the only place this repository names the incident, and it is where `doctor` declines the job by decision rather than by omission.
That matters to a deferral document specifically. It is not an argument that F16 is due -- this release does not need it -- but it is why nothing else will quietly cover the gap while it waits, which is exactly what a written reopening condition is for.
And it does not conflict with leaving #433 out of the counts. The same sentence calls it a distribution defect, which is the reason it is not an operator who did not know. The passage that supports the design refuses it as an occurrence.
Limit: this restores one support and the deferral does not rest on it -- if ADR-0032 were revised to ship an update lookup in doctor, the conditions above would still be the thing that schedules F16
Blast: local
Undo: easy
Certainty: firm
Record-Id: r-f16adr32
Provenance: authored
Verified: the quoted text is `docs/adr/ADR-0032-doctor-diagnostic-model.md:378-388` read in full rather than searched for a phrase, and `grep -rln '#433' docs/adr/` returns that path alone
CommitLore-Version: 2.0.0
After an upgrade, a repository wired before it refused commits under the PATH git actually gives a hook -- a GUI client, an IDE, a launcher -- until somebody re-ran `hooks install` there. #746 made that refusal say what it was; this stops it happening. I had this backwards and said so in #749: that relaxing the exit code was a fail-closed policy change I should not make alone. The exit code was never what held the property. The `exec` lives in the matching arm only, so a path the containment check refuses is already not executed by the time anything decides what to print -- `test/hooks.test.ts` asserts that directly with a witness file that must not appear, and it only sees `exit 1` at all because it passes a PATH with no `commitlore` on it. The refusal at the end is the ending for "no CLI could be resolved anywhere". Reusing it for "`current` moved and `root` did not" was a tax, not a fence. An upgrade is distinguishable by shape, and #71 is the opposite shape. `hooks install` writes `bin` as the literal `<data-root>/current/dist/commitlore.mjs` and `root` as the physical `v<x>` it resolved to. An upgrade moves the installer-owned symlink to a sibling: the recorded string does not change and the new target sits beside the recorded root. #71 replaces the string itself, and a `.git/config` editor can write neither the installer's symlink nor a directory beside its versioned trees. So that arm rebinds the trust root to what `current` resolves to now and runs the same containment check again. Two weaker rules are recorded in the comment as rejected rather than left to be rediscovered: "share a common ancestor" admits `/` and therefore everything, and "follow `current` wherever bin points" is satisfied by a planted `/tmp/current/dist/commitlore.mjs`. The second has a test, so reintroducing it fails rather than passes. Limit: this reaches a repository only when its stub already carries the arm, so one installed before this still needs `hooks install` once -- the same boundary #746 recorded, and the reason that command stays the named remedy Blast: system Undo: easy Certainty: firm Record-Id: r-upgraderebind Provenance: authored Verified: removing the rebind fails the upgrade case and leaves the other three passing, which is the shape a real guard has -- 88 tests across the five hook suites pass with it in place, including #71's witness assertion and the planted-layout case CommitLore-Version: 2.0.0
Six cases in `test/init.test.ts` failed for every developer with an editor session open and passed on CI runners. The cause was one warning: `live MCP runtime identity` reporting two CommitLore servers answering MCP, from installs `<data-root>` keeps by design. `init`'s final step is stricter than `doctor` on purpose -- any check needing attention makes the step incomplete, because `init` is the command that was supposed to take care of everything at once. That reasoning does not reach a finding about the machine. A server another session left running, from an install that may since have been deleted, is not something a checkout can act on. The check already argued this for its own status: it is `warn` rather than `fail` because "what this observes is the machine, not this repository", and because making it fail "made this suite's result depend on what happened to be running while it ran". The same sentence applies one level further, and `check()` already has the parameter for it -- the two existing overrides clear attention for the no-remote refspec warn and the ENOENT inject fail on exactly this test, that neither is something the user can act on here (#192, #221). So all three `warn` rows here clear it. They stay in the report, which is where a machine-scoped observation belongs; they stop deciding whether a repository-scoped command completed. The test pins the property rather than the symptom, by driving the check with an injected scan. Reproducing the symptom needs a stale server actually running, which is the dependency being removed -- a test that needed one would have the defect it is testing for. Limit: this is one check's classification, not a scope field -- another machine-scoped check added later will default to claiming attention again, and nothing here would notice Blast: local Undo: easy Certainty: firm Record-Id: r-machinescope Provenance: authored Verified: removing the override fails three of the four new cases; with it, 108 tests across init, doctor and the new file pass on a machine that has two stale MCP runtimes -- the same machine where those six init cases failed against origin/main CommitLore-Version: 2.0.0
The 1.1.2 fix for #735 changed the swap and the success line, and the control flow that broke still runs in no CI job. `install-macos` installs once, so `current` never pre-exists and the BSD branch is unreachable. `install-script` does upgrade over an existing install -- on ubuntu, where GNU `mv` never follows a symlink -- and asserts `wrapper --version`, which binds the versioned checkout and therefore reported the new release throughout the incident while every installed hook kept executing the old build through `current`. A regression of the rename would pass both. Isolated on Darwin, the mechanism is `mv current.tmp current` returning 0, `readlink current` still naming the old release, and the temporary link sitting inside that old release directory. Exit codes and the wrapper are both blind to it by construction, which is why the assertions here go through the link instead: `commitlore.bin` is recorded as `<data-root>/current/dist/commitlore.mjs`, so that is the interpreter every installed hook actually runs. Two assertions, and the second is the symptom rather than its consequence: the version reported through the link, and no `current*` residue left in the previous release directory. The fixture lowers `package.json` on a throwaway commit rather than only tagging the same tree. `verify_incoming_smoke` runs the incoming bundle's `--version` and dies when the tag and the report disagree (install.sh:577-589), so a tag-only fixture fails before reaching the swap and would have looked like the leg working. Building it locally found a second thing. `git add package.json` stages the lowered version, and `git checkout -- package.json` restores from the index rather than from HEAD -- so the plain form leaves the workspace on the fixture version, and `COMMITLORE_INSTALL_SOURCE` points at that workspace. Limit: the ubuntu twin is not added, and the review that suggested it was working from a claim in #719 that does not hold -- `test/install-script.test.ts` spawns the real installer, plants a `current` symlink and asserts `readlinkSync` plus the stray-link absence, so the GNU path is already pinned end to end Blast: system Undo: easy Certainty: firm Record-Id: r-macosupgrade Provenance: authored Verified: the fixture was built against a clone of this repository and the tag carries `0.0.1-upgradetest` while the workspace returns to `1.1.2`; the BSD reproduction was run on this Darwin machine before the leg was written, and 91 release-prerequisite, tag-binding and action-lint tests pass with the workflow digest re-locked CommitLore-Version: 2.0.0
Two changes from the review on the issue.
The residue assertion now filters `current.commitlore-install.*`, which is the prefix `test/install-script.test.ts` already uses for the GNU leg. Filtering `^current` would have passed on a different string and the two legs would have been asserting two contracts that happen to agree.
And the evidence the issue asked for, run on this Darwin machine against the real `install.sh` rather than an isolated `mv`:
mechanism installer exit through the link residue
main 0 1.1.2 none
v1.0.2-era `mv -f` 0 0.0.1-ci current.commitlore-install.95403
Both exit 0. That is the whole reason this leg reads through the link and looks in the previous release directory: the installer's own status and the wrapper are blind to it by construction, and a leg that asserted either would have passed under the defect.
Limit: the fixture and the assertions were exercised locally, so what is proven is that this mechanism fails under the restored defect on Darwin -- the job itself has still only ever run in this pull request
Blast: local
Undo: easy
Certainty: firm
Record-Id: r-macosevidence
Provenance: authored
Verified: the defect was restored by replacing the `mv -h || mv -T || rm+mv` chain with the single `mv -f` it replaced, a fresh HOME was installed at the fixture tag and upgraded over, and the through-the-link version reported the old release with a stray link left in its directory; restoring the chain returns both to passing
CommitLore-Version: 2.0.0
`Reuse for admin quotes` sat in the SUPERSEDED row, stopped by a dashed connector and an X. README:177-181 shows what it actually is:
Limit
[claim] r-price01 87e36511 calculatePrice owns final checkout pricing only
Ruled-out
[claim] r-price01 87e36511 Reuse checkout pricing for admin quotes | ...
One `r-price01`. The ruled-out alternative is a trailer on the record that is still in force, so it is delivered -- and delivering it is the entire point: it is how an agent learns the reuse was already considered and rejected. The first screen was stopping it at an X and saying the opposite of what the product does.
The superseded row is now an earlier, broader Limit that a later one replaced, which is what a superseded record is. The ruled-out line stays in the panel that reaches the next edit, where it already was.
The `<desc>` and all four alts follow, because they were describing the same inversion. A test pins it: the block between the SUPERSEDED label and the panel must not carry the ruled-out text, and the panel must still carry it. Restoring the draft wording fails it.
This is the second time this replacement said something the product does not do. The first was `must not revive` in the alt, which #71 caught by accident because the phrase was also load-bearing for ADR-0022. Neither was a rendering problem, and neither would have been caught by looking at the picture.
Limit: nothing checks that the hero and the payload block below it stay consistent -- the test asserts the inversion is absent, not that the two describe the same record
Blast: local
Undo: easy
Certainty: firm
Record-Id: r-heromeaning
Provenance: authored
Verified: README:177-181 carries both lines under one Record-Id, read in the file this pull request also ships; restoring `Reuse for` to the superseded row fails the new assertion, and 69 tests across the hero, positioning and readme suites pass with the correction
CommitLore-Version: 2.0.0
`#753` converged README.ko.md on 한다체 with six exceptions that follow one rule: address the reader and it stays polite, describe the product and it does not. Two lines break it, and neither was in that branch's diff -- `#751` wrote them, replacing the protocol section with a pointer, and the two branches never saw each other. Both describe where a document is. Nobody is being addressed, so both take the plain form the paragraphs around them use. The review that found it counted fourteen polite lines; my own count said twenty-five and was wrong. `아니다` contains `니다`, so a pattern matching the syllable alone reports every plain-form negation in the file as polite -- and this document has many, because refusing an overclaim is what it does. The precise pattern gives fourteen, all six documented exceptions plus the hero paragraph and the two heading lines the contents links anchor to. Limit: nothing checks this -- the rule lives in the PR that established it and in a comment, and the next section written in the wrong register will land the same way these did, from a branch that never touched the file the rule was recorded on Blast: local Undo: easy Certainty: firm Record-Id: r-koregdrift Provenance: authored Verified: after the change the precise pattern returns fourteen lines and every one is a documented exception -- hero 17-30, contents 46-47, the CTA at 105, the read-before-installing line at 192, and the two headings at 277 and 290; the conformance suite and the numbers gate both pass CommitLore-Version: 2.0.0
…-1.1.3 # Conflicts: # dist/commitlore.mjs # installer/canonical-artifact.json
Six changes, and one of them is the release: an upgrade stops leaving the repositories it upgraded refusing commits. `hooks install` records `commitlore.bin` through `<data-root>/current` so a hook follows upgrades, and `commitlore.root` as the physical tree that path resolved to. An upgrade moves one and not the other, and the commit-msg hook compares them -- so from 1.0.2 onward every repository wired before an upgrade stopped using its recorded interpreter afterwards. Under a login shell the PATH fallback hides it. Under the PATH git actually gives a hook, the commit was refused. The fix lives in the hook, and a hook is a file written when it is installed. So this release does not repair the repositories that have the problem: each needs `commitlore hooks install` run in it once. That is the first line of the release note rather than a footnote, and `install.sh` now says it at the only moment anything sees an upgrade happen. Also here: the refusal says which of three things happened rather than claiming the CLI is missing; `hooks install` reports the root it moved instead of printing `unchanged` after performing the repair; `init` stops reporting a step as incomplete because another CommitLore process is running on the machine; the macOS job upgrades over an existing install, so the branch #735 shipped in is finally executed by CI on the platform whose `mv` diverges; the protocol example and vocabulary get one owner instead of four translated copies; and the Korean README converges on one speech level. `#754` is deliberately absent. T-1503 asks that T-1502 be proven on at least one real merge rather than a green test run, and that workflow has never run. Limit: `#749` question 1 stays open -- a fix that lives in the hook reaches a repository only on its next visit, and nothing on this machine knows which repositories exist Blast: system Undo: costly Certainty: firm Record-Id: r-rel113 Provenance: authored Verified: 34 install-shape pins moved and no `v1.1.2` remains in the READMEs or installers, with the release-boundary prose about v1.0.2 untouched; the lockfile's two version fields were set by parsing JSON rather than replacing text; `artifact:verify` passes against the regenerated manifest, and 66 manifest and readme tests pass CommitLore-Version: 2.0.0
CommitLore — record lintTrailers: clean — 24 commits in Trailer violations fail this check. Active constraints are informational — they are what the repository already decided, not a verdict on this PR. |
MongLong0214
added a commit
that referenced
this pull request
Aug 18, 2026
A blind refutation round on this branch broke three of the four claims I put to it, and verifying one of them found a defect it had not been looking for. `gh pr create` renders "GitHub closes #123 as merged" into the canonical pull request's body. GitHub binds a closing keyword to the number straight after it, and a pull request closed by keyword is recorded closed with `mergedAt` null -- the opposite of the sentence containing it, and the opposite of what T-1502 accepts. Measured on #752 six hours ago: an integration body said "GitHub closes #752, #755, #756 ... as merged", the keyword bound to #752 alone, and that one was recorded closed while the five with no keyword were recorded merged. There is no API to convert it afterwards. This workflow would have reproduced it on every run, and no test read the body. Two ticket statements were also wrong against the file. "The job never checks out or executes a pull request's head" was borrowed from the rule #723 fixed for `preserve`, which only reads a pull request; this one rebuilds it, and rebuilding somebody's change means running it. Unsatisfiable as written, so it would have been dropped rather than met -- what the job split actually holds is that the runner executing that code has no credential. And the negative control the ticket named, skipping `artifact:manifest`, cannot be performed from a pull request: the step is hard-coded in a workflow loaded from the default branch and the source-only filter refuses workflow edits. A negative control nobody can run is the defect it was written to prevent, so it is replaced with one that can be: edit `dist/` on the pushed canonical branch and watch `ci.yml` go red. Limit: the canonical pull request asks for a merge commit and cannot enforce one -- squash and rebase are both enabled and the button remembers the last method used, which is how #760 closed five of six as merged Blast: module Undo: easy Certainty: firm Record-Id: r-t1502body Provenance: authored Verified: restored the keyword and watched the new test fail naming `closes #123`, then restored the fix and saw 21 tests pass across both workflow test files CommitLore-Version: 2.0.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge this with a merge commit, not a squash. It integrates six reviewed pull requests with
--no-ff, so their head commits are ancestors here — a merge commit lands them and GitHub closes #752, #755, #756, #757, #758 and #759 as merged. A squash lands new bytes and leaves all six open.Integrated rather than merged one at a time because
mainisstrict: each merge invalidates the rest and forces a full re-run, which is five rounds of ~30 minutes for changes that were already reviewed and green.What this release is
An upgrade stops leaving the repositories it upgraded refusing commits.
hooks installrecordscommitlore.binthrough<data-root>/currentso a hook follows upgrades, andcommitlore.rootas the physical tree that path resolved to. An upgrade moves one and not the other, and the commit-msg hook compares them — so from 1.0.2 onward every repository wired before an upgrade stopped using its recorded interpreter afterwards. Under a login shell the PATH fallback hides it; under the PATH git actually gives a hook, the commit was refused.And what it is not
The fix lives in the hook, and a hook is a file written when it is installed. This release does not repair the repositories that have the problem — each needs
commitlore hooks installrun in it once.Measured, not assumed:
So it is the first line of the release note, and
install.shnow says it at the only moment anything sees an upgrade happen. That line was gated on a variable that does not exist in the script and would never have printed; caught by running a real install rather than by reading it.Also here
initstops reporting a step incomplete because another CommitLore process runs on the machineinstall-macosupgrades over an existing install, so #735's branch is finally executed on the platform whosemvdivergesDeliberately absent
#754. T-1503 asks that T-1502 be proven "on at least one real merge. Not on a green test run", and that workflow has never run. Shipping it would put an unexercised workflow in the tree that reads as working — #691's shape.
Version surfaces
Known limits
execexists in both arms. It predates the rebind and is recorded on Stop charging every repository for an ordinary upgrade (#749) #756 rather than introduced by it.