Uh oh!
There was an error while loading. Please reload this page.
fix(devx): teach the os-regen pre-commit hook the deferred merge - #8205
Merged
Conversation
Two in-repo authorities contradicted each other on main. `scripts/pm/os-regen-merge.sh` — the sanctioned landing sequence for a branch touching `merge=os-regen` paths — commits the merge BEFORE regenerating, deliberately: the driver exits 0 while silently dropping one side, so only a separate regeneration commit on a known-good base lets a reviewer read "what main brought" apart from "what the change produces". The `os-regen` pre-commit hook refused exactly that commit. The way out people learned (measured on PR #7851) was to skip the entire pre-commit hook, which trades one false positive for a blanket bypass. Maintainer ruling 2026-08-12: the hook moves. The merge commit is now DEFERRED, not passed: `pre-commit` records `deferred-at <head> <merge-head>` in the existing marker and lets the commit through. Two properties keep that a split rather than an escape hatch. One commit deep, by construction. A deferral is entered only while MERGE_HEAD exists, a second merge attempted while one is outstanding is refused, and every non-merge commit after it is refused by the ordinary staleness check — so nothing can land between the merge and its discharge. "The immediately following commit" is enforced by there being no other commit it could be. Collected where collection becomes possible. At the moment the merge commit is created, the commit that discharges it does not exist, so pre-commit can only record. The two events that can follow are the next commit (this same check) and the push, so `.githooks/pre-push` — new, registered by the existing `core.hooksPath=.githooks`, 77ms and a marker stat on every push that did not defer — refuses a push that still owes one. An undischarged deferral cannot leave the machine. Prose reconciled across all three authorities that disagreed: the script header (the authority), AGENTS.md §11, and what the hook prints. The hook's refusal now names `scripts/pm/os-regen-merge.sh` instead of advertising a bypass. Tests: `--self-test` grows a decision table over all five states plus a fixture that builds throwaway git repos and replays the two-commit sequence — behaviour about a PAIR of commits, which no single-tree assertion can express. Gates are redirected at the fixture's own package.json so the replay costs no spec build; a mistake there fails safe (a directory without those scripts reads as stale). The pre-push hook's index mode is asserted too — git ignores a non-executable hook in silence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q983j8EGKjUs8r14n76TeH
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
`check:pm-skill-id-lint` refuses issue numbers in AGENTS.md operative text: a lesson has to be self-contained (failure mode + discipline + boundary) and a ruling keeps its date, so the number carries no information a reader needs. The bullet already states the mechanism in full; the citation becomes the ruling's date. Gate green locally, self-test included. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q983j8EGKjUs8r14n76TeH
hotlong
marked this pull request as ready for review
August 12, 2026 20:35
This was referenced Aug 12, 2026
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 12, 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 freeto 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.
Fixes#8047. Implements the maintainer ruling of 2026-08-12 (option 1): the
os-regenpre-commit hook learns the deferred case.The contradiction this removes
Two in-repo authorities disagreed on
main.scripts/pm/os-regen-merge.sh— the sanctioned landing sequence for a branch touchingmerge=os-regenpaths — commits the merge before regenerating, deliberately: the driver exits 0 while silently dropping one side, so only a separate regeneration commit on a known-good base lets a reviewer read "what main brought" apart from "what the change produces". The pre-commit hook refused exactly that commit. Measured on PR #7851, the way out people learned was to skip the entire pre-commit hook — one false positive traded for a blanket bypass.Both halves
Accept. A merge commit whose artifacts are stale is now deferred, not passed:
pre-commitappendsdeferred-at <head> <merge-head>to the existing marker and lets the commit through, printing what it recorded.Collect. A change that only stopped refusing would convert a false positive into no coverage. Two properties prevent that:
MERGE_HEADexists; a second merge attempted while one is outstanding is refused; every non-merge commit after it is refused by the ordinary staleness check. Nothing can land between the merge and its discharge, so "the immediately following commit" is enforced by there being no other commit it could be..githooks/pre-pushis new: registered by the existingcore.hooksPath=.githookswith no registrar change, and 77 ms plus a marker stat on every push that did not defer. An undischarged deferral cannot leave the machine.Prose reconciled
All three authorities that disagreed now say the same thing:
scripts/pm/os-regen-merge.sh's header (the authority — its four-step is unchanged),AGENTS.md§11, and what the hook prints. The refusal points atos-regen-merge.shinstead of advertising a bypass.Measurements behind the design
$GIT_DIR/os-regen-pendingexists and self-clears; there was no deferral path — the clearing message refers to artifacts the driver deferred, not to a deferred merge commit. The deferral record is new state, deliberately inside the existing marker so there is one state machine, not a marker plus a flag.git mergedoes not runpre-commitfor a merge it completes itself (verified, git 2.43). A clean auto-committed merge lands with the marker untouched and the refusal falls on the next commit — the same collection point. Only a merge finished withgit commitreaches the new accept path, which is the PR feat(spec,lint): gate managedapiMethods⊆ affordances where the author is (#7521) #7851 shape.scripts/regen-artifacts.mjs, andcheck:merge-driverreconciles that table against.gitattributesin both directions already. Nothing to fix.deferred-atline as an unknown pending path and refuses — no silent pass.Out of scope, unchanged as directed:
.gitattributes's merge-driver registration, andscripts/git-merge-regen.mjs's behaviour.Tests
node scripts/check-regen-pending.mjs --self-test(the second half ofpnpm check:merge-driver) grows three sections: the pre-push hook's index mode, a decision table over all five states including the two a fixture cannot reach, and a fixture that builds throwaway git repos and replays the two-commit sequence. The behaviour is a property of a pair of commits, which no single-tree assertion can express. Gates are redirected at the fixture's ownpackage.jsonviaOS_REGEN_GATE_CWD, so the replay costs no spec build and a mistake fails safe — a directory without those scripts reads as stale.Reverse verification, direction predicted before running: on one fixture state (MERGE_HEAD present, marker set, artifacts stale)
origin/main's hook exits 1 — the false positive, and its trailer is the--no-verifyadvice — while this one exits 0 with the deferral recorded. One commit later, still stale, both exit 1.Generated by Claude Code