Let one machine differ from the committed policy without dirtying the tree (#709) - #715
Merged
Merged
Conversation
… tree (#709) `.commitlore-policy.json` is committed, which is right for a policy: `auto on` authorises a host to prepare, verify and stage with nobody in the loop, and that is the repository's decision for everyone who clones. But a contributor who needs a different answer had one route -- edit the tracked file -- and their worktree stayed modified forever. #709 reports the consequence: a release script that refuses to tag a dirty tree stopped working, and the workaround was moving the file out of the repository for the duration of the tag. So the single-location choice did not prevent the divergence. It converted it into a modified tracked file, which is the worse of the two, and that is what reverses it. `.commitlore-policy.local.json` now wins per key: an overlay setting only `unattended` leaves `mode` and `max_records_per_commit` as the repository set them, so a later change to the committed file still applies. It may set a value in either direction -- what keeps an unattended record from directing is the drafted stamp and the claim cap in grading, not this switch, and a narrowing-only rule is unenforceable anyway because the contributor who wants more edits the tracked file. The ambiguity the old choice was avoiding is paid for rather than inherited. The identity hash is taken over the effective policy whenever an overlay is present, so a record prepared under one is stamped with the policy that produced it rather than with bytes that did not; a repository with no overlay keeps exactly the digest it had, so no capture in flight is refused by the upgrade. And a new doctor check names both files, the value beneath, the value in the overlay and the one in force -- ok when they disagree, because that is the feature working, and warn only when a file cannot be used, since then neither file's values are in force. Limit: unattended is an input to the effective digest but not to the defaults digest -- #511's exclusion rests on a file's identity being its own bytes, which an overlay breaks Limit: a broken overlay falls back to the built-in defaults, not to the committed file -- layering onto a policy nobody could read states an effective policy no file states Ruled-out: let an overlay only narrow permissions | it solves the contributor who wants less, and the one who wants more still edits the tracked file, which is the reported failure Ruled-out: write a .gitignore entry for the overlay | a tool that hides a file on a repository's behalf has decided for the repository what it may not see Blast: module Undo: easy Certainty: firm Record-Id: r-overlay709 Provenance: authored Verified: three mutations, each caught by the test whose name claims it -- overlay merged per file instead of per key fails 3 cases including "leaves the keys it does not set as the repository set them"; identity kept on the committed bytes fails all 3 hash cases; the effective hash applied with no overlay present fails "does not move for a repository that has no overlay" and 3 pinned cases in capture-policy.test.ts. 209 cases pass across the 8 affected suites; npx tsc --noEmit clean; build:canonical then artifact:verify -> b5e96eb116ac71c43810c409f9864b1bad16e99616b1fe24981ee54d28d5f4fc. test/init.test.ts fails 6 cases on unmodified src here for a local reason -- two live CommitLore MCP runtimes on this machine -- confirmed by stashing the change and re-running. CommitLore-Version: 2.0.0
CommitLore — record lintTrailers: clean — 1 commit in Trailer violations fail this check. Active constraints are informational — they are what the repository already decided, not a verdict on this PR. |
This was referenced Aug 17, 2026
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.
Closes #709.
.commitlore-policy.jsonis committed with the repository, which is right for a policy —auto onauthorises an agent host to prepare, verify and stage with nobody in the loop, and that applies to everyone who clones. A contributor who needs a different answer had exactly one route: edit the tracked file. Their worktree then stayed modified forever, which is enough to stop a release script that refuses to tag a dirty tree — the failure #709 reports.Refusing the overlay never prevented the divergence. It converted it into a modified tracked file, which is the worse of the two.
Precedence
Per key, not per file: an overlay setting only
unattendedleavesmodeandmax_records_per_commitas the repository set them, so a later change to the committed file still applies.commitlore auto on --local/auto off --localwrite it, and once it exists it is the filecommitlore autowrites — existence is the signal, because anauto offthat silently created one would stop the tracked file being the answer without anyone choosing that. Nothing writes a.gitignoreentry for it.What pays for the stated precedence
unattendedis an input here though it is not in the defaults digest — M-UX: capture leaves the user's workflow #511's exclusion rests on a file's identity being its own bytes, which an overlay breaks.policy-overlaydoctor check names both files, the value beneath, the value in the overlay and the one in force.okwhen they disagree — the operator wrote it on purpose, and a warning that fires forever on a correct machine teaches people to ignore the surface carrying the real ones.warnonly when a file cannot be used, since then neither file's values are in force.Verification
Three mutations, each caught by the test whose name claims it:
capture-policy.test.ts209 cases pass across the 8 affected suites;
tsc --noEmitclean;build:canonicalthenartifact:verify→b5e96eb116ac71c43810c409f9864b1bad16e99616b1fe24981ee54d28d5f4fc.test/init.test.tsfails 6 cases on this machine for an unrelated local reason — two live CommitLore MCP runtimes (v1.0.1 and v1.0.2) answering the runtime-identity probe. Confirmed by stashing the change and re-running on unmodifiedsrc/: same 6, same cause.ADR-0035 records the reversal and what it is conditioned on.