tooling(pm): govern hotcrm in check-governed-merges (#14867) - #14987
Merged
Conversation
`GOVERNED_REPOS` gains `objectstack-ai/hotcrm`, per the maintainer ruling recorded on #14867 (2026-09-03, verbatim 「其他同意」 adopting 纳入). The governed-surface definition already read "agent instruction files, judged the same across repos"; the 2026-08-18 ruling's list (objectui, cloud, objectos) was an enumeration of the repos in view that day, not an exclusion. The gap this closes is a silence, not a wrong row: a CONFIGURED repo that cannot be read prints a loud `⚠️ UNAUDITED` row, while a repo that was never configured prints nothing at all — indistinguishable in the output from a repo that swept clean. Meanwhile the `repo:hotcrm` seat hand-merged that repo's `AGENTS.md` chain as governed with no post-merge audit behind it. - the header's derived statement of the set names `hotcrm` and cites the 2026-09-03 ruling beside it; the 2026-08-18 quotation is untouched; - the four/five repo counts in the header, the usage line, the `--since-ref` rationale, the constant's JSDoc and the `--test` note follow the register; - the `:288` dead-mirror measurement keeps its recorded batch verbatim and now says what that batch was (a hand probe of a then-unconfigured control); - `--self-test`: the ordered-ids pin moves to the new set (the pin doing its job), plus two new assertions — `hotcrm` pinned by id AND slug so a later removal reds a check that names it, and a fixture pinning that a configured repo with no checkout renders UNAUDITED rather than nothing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
… stack `assert` in `--self-test` COLLECTS failures and prints them at the end, so a throw inside a fixture aborts the run before any collected failure is shown. Measured on the #14867 reverse verification: deleting the `hotcrm` register entry made `byId.hotcrm` undefined, and the new no-checkout fixture threw a TypeError — exit 1, loud, but the two pins that NAME hotcrm never printed. Read the row defensively so a removal reds as three named lines instead. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RfFHiRCSs3JXLK4cwcfox
This was referenced Sep 3, 2026
os-steve
marked this pull request as ready for review
September 3, 2026 16:45
os-steve
enabled auto-merge
September 3, 2026 16:46
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.
Fixes #14867
GOVERNED_REPOSinscripts/pm/check-governed-merges.mjsgainsobjectstack-ai/hotcrm.Single file;
scripts/pm/**is not itself a governed path.The ruling this executes
Maintainer ruling recorded by the director seat, comment 5523300327, 2026-09-03. Provenance:
maintainer, live PM chat, replying to decision batch #20 item 3 (this card and #14881 presented
as one question) with the recommendation 纳入; verbatim reply 「其他同意」 — adopts 纳入. Ruled
consequences, quoted from the record:
The rationale cited in the header, from the same record: the governed-surface definition already
reads "agent instruction files, judged the same across repos"; the 2026-08-18 ruling's list
(objectui, cloud, objectos) was an enumeration of the repos in view that day, not an exclusion.
The 2026-08-18 quotation in the file header (「任何对 agents.md 等文件的修改…包括 objectui
cloud仓库」) is untouched — a verbatim ruling rewritten is a ruling rewritten. What names
hotcrmis the header's own derived statement of the set, with the 2026-09-03 ruling cited besideit.
What the change buys, measured
The card's central claim — absence of coverage is indistinguishable from clean coverage — is
false after this change. The live sweep in this container, on the final commit:
2/5where it read2/4; the hotcrm row is the same loud⚠️ UNAUDITEDthe other unreadablerepos get, and the footer counts three refusals where it counted two. Before this change hotcrm
produced no row at all and the sweep still exited 2 on the other two — which is exactly the
silence the card describes.
The sharpest before/after is the one argument that validates ids against the register. Same
invocation, the base file versus this one:
5bc2f27--repos hotcrm❌ --repos names no governed repo: hotcrm. Known: objectstack, objectui, cloud, objectos.(exit 1)❌ no governed repo could be audited — not one checkout resolved/objectstack-ai/hotcrm [no-checkout](exit 1)--repo-root hotcrm=PATHpointed at a checkout whose origin is another repoobjectstack-ai/hotcrm [wrong-origin]: the checkout at ... has origin objectstack-ai/objectstack, not objectstack-ai/hotcrm— hotcrm goes through the same #13423 identity discipline as the other fourEverything in the diff
GOVERNED_REPOSgains thehotcrmentry, appended last so the positionalGOVERNED_REPOS[1]/[2]references in--self-testkeep pointing at the same repos.hotcrmand cites the 2026-09-03 ruling, withthe incident the gap cost recorded in the file's own idiom.
usage line, the deepening note, the
--since-refper-repo-window rationale, the constant'sJSDoc, the
--testrepo-agnostic note, and the--self-testsummary sentence. A count saying"four" beside a register holding five is the drift this file exists to make loud.
:288dead-mirror measurement: see the deviation below.--self-test: 241 assertions at the merge base, 243 here.four-governed-repos-declaredbecomesfive-governed-repos-declaredover the new ordered set— the pin doing its job, not a test weakened.
hotcrm-is-a-governed-repo-with-its-slug (#14867)pins membership by id and slugseparately, so a later removal reds a check that names hotcrm rather than one that reads as a
reordering.
a-configured-repo-with-no-checkout-is-UNAUDITED-never-silentpins the property this cardbought, in the resolver fixture.
Reverse verification
Fix committed first, then the register entry deleted,
--self-testre-run, then restored.Mutation proven on disk (entry lines 1 -> 0; blob
8f1afdf21a->49365e96c1); restore proven ondisk (blob back to
8f1afdf21a,git diff HEADempty). Predicted direction RED; observed:The second commit exists because the first run of this ablation did not read like that. It
threw a
TypeErroronbyId.hotcrm.statusand exited 1 — loud, butassertin this suiteCOLLECTS failures and prints them at the end, so the throw aborted the run before any of the
named pins were printed. A removal must produce named red lines, not a stack trace over them; the
fixture now reads the row defensively, and the four lines above are the result.
Deviation from the dispatched route, declared
The dispatch asked me to "replace the prose that described 'hotcrm is not configured'" at
:288.Measurement falsifies that description of the note. The only occurrence of
hotcrmin thebase file is inside the 2026-08-30 dead-mirror measurement, and it is not a statement that hotcrm
is unconfigured — it is a control-group reading: "
objectos,hotcrm,objectuiandobjectstackall resolved in the same batch, so this was a scope change for one repo and not abroken channel." That sentence is a record of what three probes returned, it is still true, and it
is load-bearing (it is what rules out a broken channel). The dispatch's own instruction — "delete
only what the configuration now states" — therefore deletes nothing there.
So the measurement stays verbatim and the note now says what that batch was: on 2026-08-30 hotcrm
was probed by hand, as a control this sweep had no row for, and it was not configured here
until the 2026-09-03 ruling. Rewriting a measured reading to satisfy a prose instruction is the
same class of error as editing a quotation, one level down.
Falsified mechanism assumption, for the record
--since-ref ID=REFids are not validated againstGOVERNED_REPOS.resolveWindowputs apinned id straight into
resolveRefDate(ref, repoId); only--reposchecks membership. Measured:--since-ref hotcrm=SHAreturns the byte-identical refusal on the base file (where hotcrm isunknown) and on this branch (where it is governed) —
does not resolve to a commit in any repo it names. So that argument picked up nothing automatically, in either direction. Stated rather thanchanged: making it validate is a behaviour change no ruling covers.
Verification
Gate family re-derived on the final commit with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no paths), harvested with--commandsso no spelling or section is dropped: 22 families. Every exit code captured byredirect before any pipe. Verdict lines are quoted in the report comment on the card.
check:pm-governed-merges—✓ check-governed-merges --self-test: 243 assertions.node scripts/check-test-completeness.mjsis NOT MEASURED locally, in its own words:PREREQUISITE NOT MET — this gate grades a saved 'turbo run test' log, and no log was named(exit3, its documented not-a-finding code).
Repo-wide ESLint was run in full rather than narrowed, through the shared verify lock:
pnpm exec eslint . --no-inline-config --format json—VERDICT command-exit 0 · held the lock 72s,5824 files linted, 0 errors, 0 warnings, the changed file present in that population.
Mirror site found, not widened
.claude/skills/pm-dispatch/SKILL.md:723carries a prose mirror of the governed-repo set(「objectui、cloud、objectos 一并在内」) that this change makes stale. It is a governed surface,
so touching it here would fork this PR. A second site says the same thing as a count —
.claude/hooks/guard-governed-enqueue.sh:54, "the same call answers for all four governed repos".Both are filed as #14984 (unassigned, unlabelled) rather than folded in.
check:pm-governed-prosedoes not cover them: its scope is
GOVERNED_SURFACES, notGOVERNED_REPOS— verified by running itgreen on this branch.
🤖 Generated with Claude Code
Generated by Claude Code
Generated by Claude Code