Repository navigation
tooling(pm): .github/workflows/** joins the governed register, scoped to objectstack, objectui and cloud by a per-row repository key (#23048) - #23129
Conversation
….github/workflows/** joins it for objectstack, objectui and cloud Ruled B2: GOVERNED_SURFACES rows may carry a repos key (default: every repository in GOVERNED_REPOS; the six existing rows untouched), and the matchers — governedPathsIn, governedTierFor, testVerdict, classifyCommit — take the repository and refuse a call without one. --test and --branch answer for the checkout at --root; --pr for the repository it names. Claude-Session: https://claude.ai/code/session_012dGTuJen61ECNHXYXKChoC Co-authored-by: Claude <noreply@anthropic.com>
…ory; the prose names .github/workflows/** The queue guard resolves the repository its workflow runs in and threads it through every leg (guardRepository); the half-states patrol binds its swept board at load; the prose checker demands the rows that govern this repository; record-recognisers' pin names the queue it drives. AGENTS.md rule 14 and the pm-dispatch skill's governed-definition line are rewritten on their own lines, both files at their line ceilings. Claude-Session: https://claude.ai/code/session_012dGTuJen61ECNHXYXKChoC Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #23048 (body and all eight comments, among them the decision analysis 6134774406, the ruling record 6135970276 with execution parameters (1)–(6) and the fork clause, the claim 6136107596 and the dev report 6136862799); PR #23129's body, file list and the net diff Check-runs on the head, read after the last shard completed (02:39Z): 45 runs — 34 success, 11 skipped, 0 failure, 0 in progress. The skips are the paths-filtered lanes ( ① Derived judgmentsThe ruling's six execution steps and the fork clause (6135970276):
Fail-OPEN check — none found for objectstack, objectui or cloud. Every matcher refuses without a repository; The governed prose, true against the register after the change:
The dev's deviations:
Also judged: the ② Semver level
③ Boundary flags
What the maintainer must look at (after the fix push and a fresh PASS record):
Implemented-by: VERDICT: FAIL Generated by Claude Code |
…o-agnostic; the tier table names the scoped workflows row Comments only (rework round 1 on the contract review): the 'governed REPOS' section states the ruled shape — every row governs every repository by default, a row may be scoped (workflows: objectstack, objectui, cloud), the sweep still covers all five, --test/--branch answer for the checkout at --root — and the Tier H line lists the workflows row instead of claiming the list is unchanged. Claude-Session: https://claude.ai/code/session_012dGTuJen61ECNHXYXKChoC Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #23048 (body and all ten comments, among them the decision analysis 6134774406, the ruling record 6135970276 with execution parameters (1)–(6) and the fork clause, the claim 6136107596, the round-1 dev report 6136862799, the rework scope 6137045911 and the rework report 6137254453); PR #23129's body, file list and comments (the FAIL record 6137036198 on the previous head Check-runs on the head, read after the last one completed (03:01Z): 41 runs — 33 success, 8 skipped, 0 failure, 0 in progress. The skips: ① Derived judgmentsWhat this head is. One commit on The rework against scope 6137045911, passage by passage, each read against the code at the head:
Remaining present-tense statements contradicting B2 — NONE. My own case-insensitive re-grep of the whole head file for Adopted from record 6137036198 as readings of unchanged code (the compare touches only comment lines of Accept-set and public-surface changes the whole diff implies, named: for objectstack, objectui and cloud, ② Semver level
③ Boundary flags
What the maintainer must look at (carried forward from 6137036198; all three still stand, none changed by the rework):
Implemented-by: VERDICT: PASS Generated by Claude Code |
维护者速读(终稿)PR #23129 · card #23048 · head 改了什么(按你裁的 B2)
为什么改
风险与代价(含回滚)
席位意见
你要做的
Generated by Claude Code |
Fixes #23048
Clause-②: no
Ruled B2 (ruling record 6135970276, maintainer 「同意」 2026-10-13T01:17Z, on the decision analysis 6134774406): the governed register gains a per-row repository scope, and
.github/workflows/**joins it as a Tier H row scoped toobjectstack,objectuiandcloud.objectosandhotcrmare not governed by the row.What changed
scripts/pm/check-governed-merges.mjs— the register and its matchersGOVERNED_SURFACESrows may carry areposkey naming theGOVERNED_REPOSids they govern; a row with none governs every repository (the default). The six existing rows are untouched and keep their tiers. The new row:{ id: 'workflows', prefix: '.github/workflows/', glob: '.github/workflows/**', tier: GOVERNED_TIER_H, repos: ['objectstack', 'objectui', 'cloud'], what: 'CI, deploy and release workflows: …' }.governedPathsIn(paths, repo),governedTierFor(paths, repo),testVerdict(paths, { repo, … })andclassifyCommit(commit, paths, repo, size)REQUIRE the repository and refuse a call without one (assertGovernedRepothrows aTypeErrornaming the caller, the ids the register knows and the fix). Never a silent "all": a scoped row answers differently per repository.repoIdForSlug(slug),surfaceRepos(row),governedSurfacesFor(repo),checkoutRepoFor(origin, mode),checkoutRepoNote.GOVERNED_REPOS(unchanged);--pr owner/repo#Nresolves the repository it names, and a slug outside the register is refused (exit 1) rather than answered "not governed";--testand--branchanswer for the checkout at--root— itsoriginresolved to a register id, the same "this checkout's own repository" a bare--pr Nalready means — and printℹ️ judged for REPO (SLUG, the checkout at --root)on stderr. A checkout whose origin is not a governed repository is refused. No--repoflag: the closed-argv ruling in the file's header keeps one spelling, and--rootalready exists.repo;surfacesCheckedcounts the rows that govern it; the head line reads(7 surfaces govern objectstack)instead of(6 surfaces, repo-agnostic). The register docblock's "repo-agnostic" sentence is rewritten (default every repository, a row may be scoped), and a header section records the ruling..github/ISSUE_TEMPLATE/**,.github/workflowsx/,packages/x/.github/workflows/),surfacesChecked7 vs 6, every front door refusing a missing or unknown repository with the caller named,repoIdForSlug,checkoutRepoForin both directions and the head words. The existing register pins move with the row (seven ids in order; Tier H list gainsworkflows). The two CLI fixture repositories gain anoriginnaming objectstack, which is what--test/--branchnow read.scripts/pm/check-governed-queue-guard.mjs— every leg passes its repositoryguardRepository(env)resolves the repository the workflow runs in fromGITHUB_REPOSITORY(unset = this file's own); a repository the register does not know isEXIT_CANNOT_RUNwith the roster named.main()resolves it BEFORE the diff is read and threads it throughliftGeneratedExceptions,runGuardandrunSizeGuard; the reader's slug is the same reading.decomposeGovernedWork(rows, repo),guardVerdict({ repo, … }),runGuard({ repo, … }),sizeReading(pr, size, repo, approval),sizeGuardVerdict({ repo, … }),runSizeGuard({ repo, … }),liftGeneratedExceptions(root, base, rows, notes, recompute, repo)— all refuse a missing repository. Verdict objects carryrepo; the CLEAR words say(7 surfaces govern objectstack)..github/paths are clear; every builder, both orchestrators and the lift reject without a repository;guardRepositoryin all three directions. The source-text pin on the size leg now readstestVerdict([], { repo, size, approval }).--testmode (it reads the event payload only); the card's--test .github/workflows/lint.ymlreading is the merges predicate's, below.scripts/pm/check-half-states.mjs— the patrol binds its swept boardloadGovernedRegister(load, slug = OWNER_REPO)resolves the swept board's register id throughrepoIdForSlugand binds the matcher once (matcher(paths)=governedPathsIn(paths, repo)), exposingrepo; a board outsideGOVERNED_REPOSisavailable: falsewith the board named (NOT MEASURED on H43/H48), never judged against another repository's rows. The H43/H48 call sites are unchanged. Four new self-test cases drive the loader with explicit boards (cloud → governed, objectos → not, foreign → NOT MEASURED, and this run's binding equals its board).scripts/pm/check-governed-prose.mjs— demands the globs of the rows that governSELF_REPO_ID(governedSurfacesFor), since the instruction files it reads are this repository's; the success line names the repository.scripts/pm/record-recognisers.mjs— its cross-tool pin drives the guard for this repository (GUARD_REPO, read from the register'sSELF_REPO_ID); two call sites. Not on the claim's declared surface: it is a caller of the changed signature, so it is on the ruling's "(3) every caller passes its repository".Prose regions touched (the card's acceptance item)
AGENTS.mdrule 14, lines 266–268 (the enumerationcheck:pm-governed-prosepins):.github/workflows/**is named as a code span; paid by deleting "the file you are reading is one" → "this file is one" and "on a single path hit" → "on one path hit"; "The" moved to the next line. Same eight lines, no line bought.AGENTS.mdrule 14, line 279 (the Tier H parenthetical):(人合: every register row except .claude/**)— the explicit Tier H list is replaced by its complement so that the register, not this paragraph, holds the tier column (Tier S is already stated as all of.claude/**on line 282–283)..claude/skills/pm-dispatch/SKILL.mdline 184 「governed 面统一定义」:.github/workflows/**appended; paid by removing the spaces around+(「(全量)」 kept). 120 bytes, at the cap..claude/skills/pm-dispatch/SKILL.mdline 185 (the adjacent line of the same definition): 「GOVERNED_REPOS各仓同治理待遇」 → 「GOVERNED_REPOS同治理、行可限仓」, so the skill no longer says every repository receives every row; paid by removing the spaces around+. 120 bytes. In-place fix under the four conditions (same defect class as the card, mechanical, no other claim holds the file, same gate family).AGENTS.md1116 → 1116 lines;SKILL.md319 → 319 lines (both at their ceilings). Edited-line widths (content bytes): AGENTS.md 266: 117 → 114; 267: 116 → 116; 268: 116 → 120; 279: 117 → 81. SKILL.md 184: 102 → 120; 185: 119 → 120.references/lanes/skills.md:17,references/state-machine.md:9,scripts/invoked-as.mjs:28— their--testcalls keep working because--testanswers for the checkout it runs in.scripts/pm/dispatch-gates*.mjs(frozen) enumerate no surfaces; no workflow file is edited.Acceptance readings (all on bd5ad0e)
node scripts/pm/check-governed-merges.mjs --pr 23043→ exit 3,governed-surface predicate: 1 of 1 path(s) hit the register (7 surfaces govern objectstack)./⛔ GOVERNED/⚖️ landing tier: H(人合)/.github/workflows/** ×1.node scripts/pm/check-governed-merges.mjs --test .github/workflows/lint.yml(from this checkout) → exit 3, GOVERNED, Tier H; stderrℹ️ judged for objectstack (objectstack-ai/objectstack, the checkout at --root).--testwith--rootat a checkout whose origin isobjectstack-ai/objectos→ exit 0,0 of 1 path(s) hit the register (6 surfaces govern objectos),✅ NOT governed; atobjectstack-ai/cloud→ exit 3, GOVERNED Tier H,(7 surfaces govern cloud); at/home/user/objectui(origin objectstack-ai/objectui) → exit 3, GOVERNED; at a checkout with originsomeone/fork→ exit 1,❌ --test answers for the repository the checkout at --root belongs to … names someone/fork, which is not a governed repository.governedPathsIn(['.github/workflows/lint.yml'])with no repository →TypeError: governedPathsIn wants the repository the paths belong to, as a GOVERNED_REPOS id (objectstack, objectui, cloud, objectos, hotcrm); it got null …(pinned in the self-test).pnpm check:pm-governed-prose→ exit 0:✓ check-governed-prose: 2 instruction surface(s) name all 7 registered governed surfaces that govern objectstack (docs/adr/** · .claude/** · skills/** · AGENTS.md · CLAUDE.md · docs/NORTH-STAR.md · .github/workflows/**) and claim no others.node scripts/pm/check-governed-queue-guard.mjs --self-test→ exit 0, 312 cases pass.pnpm check:pm-governed-merges→ exit 0, 500 assertions.pnpm check:pm-half-states→ exit 0, 4916 cases.pnpm check:pm-record-recognisers→ exit 0, 136 cases.pnpm check:pm-skill-ratchet→ exit 0.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths, the real changeset at bd5ad0e) derives 54 families: the dispatch's 53 pluspnpm check:pm-record-recognisers. All 54 ran alone in the foreground, exit codes read from the shell status; all exit 0 (pnpm --filter @objectstack/lint run check:doc-formula-expressionsfirst answered exit 3 PREREQUISITE NOT MET — the lint and formula packages were unbuilt — and exit 0 afterturbo run build --filter=@objectstack/formula --filter=@objectstack/lintunder the shared verification lock;pnpm check:pm-dispatch-gatesran via nohup and was waited for:✓ dispatch-gates self-test (fast tier): 1833 cases pass; 6 slow section(s) deferred).node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran RAN_FILE(lines annotated:: exit N) →✓ dispatch-gates --ran: 54 derived famil(ies) accounted for — 54 run, 0 NOT-MEASURED (a DERIVED zero — all 54 recorded an exit code and none of them is 3).scripts/pm/**and governed prose), soskip-changesetis the declaration; no changeset.Reverse verification
Ablation on the committed tree through
scripts/ablation-replace.mjs: deletingrepos: Object.freeze(['objectstack', 'objectui', 'cloud']),from the workflows row (anchor x1 → x0, blob ad718b3a906d → 46ec4a7ba233) turned the merges self-test red with 5 failures —the-workflows-row-is-scoped-to-objectstack-objectui-and-cloud-exactly,an-objectos-workflow-path-is-NOT-governed,a-hotcrm-workflow-path-is-NOT-governed,surfacesChecked-counts-the-rows-that-govern-the-repository-not-the-table,the-verdict-head-names-the-repository-and-counts-its-rows(7 surfaces govern objectos). Restored bygit checkout HEAD -- …: blob == HEAD (ad718b3a906d),git diff HEADempty, anchor x1 again. Direction observed: red, as expected.维护者速读(草稿)
改了什么:治理面登记表每一行可以标注「适用于哪些仓库」,没标的行默认五仓通用(六条旧行一字未动);新增一行把
.github/workflows/**(CI、部署、发布工作流)列为 Tier H 受管面,只覆盖 objectstack、objectui、cloud。读登记表的每个工具(落地前判定、队列守卫、半态巡检、规则文本校验)现在都必须说清自己问的是哪个仓,不说就报错,绝不默认「全部」。AGENTS.md规则 14 与 pm-dispatch 技能的「governed 面统一定义」行同行改写,两份文件行数不变。为什么改:您裁的「两仓加治理行」要求两个公开仓的工作流改动先等授权批准;原登记表对五仓一视同仁,直接加行会把 objectos、hotcrm 也管进去,而那两个仓没人裁过。B2 让表上写的恰好等于裁定的范围,cloud 同列是因为 cloud 自己的治理表已有同一行,中央审计与之一致。
风险与代价(含回滚):工作流 PR 从此在三仓都要等授权批准(本轮十个 CI PR 实际已如此),objectos、hotcrm 照旧绿灯即落。工具内部签名加了必填的仓参数,漏传即响亮报错而非静默判错;五个脚本的自测与 54 个门禁族全绿,反向消融证实自测能变红。回滚 = revert 本 PR 一个 commit 序列;登记表无持久化状态。
席位意见:(留空,由席位定稿)
你要做的:本 PR 含
AGENTS.md(Tier H)与.claude/**(Tier S),保持 draft;请以GOVERNED_APPROVERS账号给出一次 APPROVED review,之后由认领席位落地。回一个字即可。Acceptance notes
--testflag (confirmed: it readsGITHUB_EVENT_PATHand exits 1 without it); the card's--testreading belongs tocheck-governed-merges.mjs, where it answers GOVERNED, and the guard's own battery pins the workflow case in its queue.--repo IDoverride on--test/--branchwas not taken: the file's closed-argv section records a ruling that there is no--repo(one spelling,--pr owner/repo#N), and--rootalready names the checkout. The checkout's origin is the repository, printed on stderr.scripts/pm/record-recognisers.mjswas edited beyond the claim's declared surface (two call sites of the changed signature); declared here and in the report..github/workflows/governed-surface-guard.ymlhas nopaths:filter by design (its header says so), so no workflow edit was needed;dispatch-gates.mjsmentions a register row only in a comment.check-governed-merges.mjs'sparsePullTargetstill accepts anyowner/repo#N;--prnow refuses a slug outside the register before any read.Co-Authored-Bytrailer carrying a model name was refused by the pre-push hook on the first push; the commits carry the repository's model-free pair.Generated by Claude Code