Repository navigation
Commit d206d08
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 to `objectstack`, `objectui` and `cloud`. `objectos` and
`hotcrm` are not governed by the row.
## What changed
**`scripts/pm/check-governed-merges.mjs` — the register and its
matchers**
- `GOVERNED_SURFACES` rows may carry a `repos` key naming the
`GOVERNED_REPOS` ids 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, … })` and `classifyCommit(commit, paths,
repo, size)` REQUIRE the repository and refuse a call without one
(`assertGovernedRepo` throws a `TypeError` naming the caller, the ids
the register knows and the fix). Never a silent "all": a scoped row
answers differently per repository.
- New helpers: `repoIdForSlug(slug)`, `surfaceRepos(row)`,
`governedSurfacesFor(repo)`, `checkoutRepoFor(origin, mode)`,
`checkoutRepoNote`.
- Where each CLI mode gets its repository: the sweep iterates
`GOVERNED_REPOS` (unchanged); `--pr owner/repo#N` resolves the
repository it names, and a slug outside the register is refused (exit 1)
rather than answered "not governed"; `--test` and `--branch` answer for
the checkout at `--root` — its `origin` resolved to a register id, the
same "this checkout's own repository" a bare `--pr N` already 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
`--repo` flag: the closed-argv ruling in the file's header keeps one
spelling, and `--root` already exists.
- The verdict carries `repo`; `surfacesChecked` counts 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.
- Self-test: a new battery "the per-row repository scope (ruled B2,
2026-10-13)" (23 cases; roster floor 32 → 33) pins the six default rows,
the workflows row's exact scope, scope ⊆ roster,
objectstack/objectui/cloud → GOVERNED Tier H, objectos/hotcrm → NOT
governed (the default rows still govern them), near misses
(`.github/ISSUE_TEMPLATE/**`, `.github/workflowsx/`,
`packages/x/.github/workflows/`), `surfacesChecked` 7 vs 6, every front
door refusing a missing or unknown repository with the caller named,
`repoIdForSlug`, `checkoutRepoFor` in both directions and the head
words. The existing register pins move with the row (seven ids in order;
Tier H list gains `workflows`). The two CLI fixture repositories gain an
`origin` naming objectstack, which is what `--test`/`--branch` now read.
**`scripts/pm/check-governed-queue-guard.mjs` — every leg passes its
repository**
- `guardRepository(env)` resolves the repository the workflow runs in
from `GITHUB_REPOSITORY` (unset = this file's own); a repository the
register does not know is `EXIT_CANNOT_RUN` with the roster named.
`main()` resolves it BEFORE the diff is read and threads it through
`liftGeneratedExceptions`, `runGuard` and `runSizeGuard`; 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 carry `repo`; the CLEAR
words say `(7 surfaces govern objectstack)`.
- Self-test: a new battery "the per-row repository scope (ruling B2,
2026-10-13)" (15 cases; floor 24 → 25): a workflow path in this queue
decomposes to one Tier H entry and is REFUSED unapproved / cleared on an
authorized approval; the same path in objectui and cloud is governed and
in objectos/hotcrm is not; the sibling `.github/` paths are clear; every
builder, both orchestrators and the lift reject without a repository;
`guardRepository` in all three directions. The source-text pin on the
size leg now reads `testVerdict([], { repo, size, approval })`.
- The guard has NO `--test` mode (it reads the event payload only); the
card's `--test .github/workflows/lint.yml` reading is the merges
predicate's, below.
**`scripts/pm/check-half-states.mjs` — the patrol binds its swept
board**
- `loadGovernedRegister(load, slug = OWNER_REPO)` resolves the swept
board's register id through `repoIdForSlug` and binds the matcher once
(`matcher(paths)` = `governedPathsIn(paths, repo)`), exposing `repo`; a
board outside `GOVERNED_REPOS` is `available: false` with 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 govern `SELF_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's
`SELF_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.md` rule 14, lines 266–268 (the enumeration
`check:pm-governed-prose` pins): `.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.md` rule 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.md` line 184 「governed 面统一定义」:
`.github/workflows/**` appended; paid by removing the spaces around `+`
(「(全量)」 kept). 120 bytes, at the cap.
- `.claude/skills/pm-dispatch/SKILL.md` line 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).
- Line budget: `AGENTS.md` 1116 → 1116 lines; `SKILL.md` 319 → 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.
- Not touched: `references/lanes/skills.md:17`,
`references/state-machine.md:9`, `scripts/invoked-as.mjs:28` — their
`--test` calls keep working because `--test` answers 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)`.
- Same `--test` with `--root` at a checkout whose origin is
`objectstack-ai/objectos` → exit 0, `0 of 1 path(s) hit the register (6
surfaces govern objectos)`, `✅ NOT governed`; at `objectstack-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 origin `someone/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 plus `pnpm
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-expressions` first answered exit
3 PREREQUISITE NOT MET — the lint and formula packages were unbuilt —
and exit 0 after `turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint` under the shared verification lock; `pnpm
check:pm-dispatch-gates` ran 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)`.
- No published package is touched (repo-root `scripts/pm/**` and
governed prose), so `skip-changeset` is the declaration; no changeset.
## Reverse verification
Ablation on the committed tree through `scripts/ablation-replace.mjs`:
deleting `repos: Object.freeze(['objectstack', 'objectui', 'cloud']), `
from the workflows row (anchor x1 → x0, blob ad718b3 →
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 by `git checkout HEAD -- …`: blob == HEAD
(ad718b3), `git diff HEAD` empty, 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
- The queue guard has no `--test` flag (confirmed: it reads
`GITHUB_EVENT_PATH` and exits 1 without it); the card's `--test` reading
belongs to `check-governed-merges.mjs`, where it answers GOVERNED, and
the guard's own battery pins the workflow case in its queue.
- The PM's suggested `--repo ID` override on `--test`/`--branch` was not
taken: the file's closed-argv section records a ruling that there is no
`--repo` (one spelling, `--pr owner/repo#N`), and `--root` already names
the checkout. The checkout's origin is the repository, printed on
stderr.
- `scripts/pm/record-recognisers.mjs` was edited beyond the claim's
declared surface (two call sites of the changed signature); declared
here and in the report.
- Observation, not filed: `.github/workflows/governed-surface-guard.yml`
has no `paths:` filter by design (its header says so), so no workflow
edit was needed; `dispatch-gates.mjs` mentions a register row only in a
comment. `check-governed-merges.mjs`'s `parsePullTarget` still accepts
any `owner/repo#N`; `--pr` now refuses a slug outside the register
before any read.
- Harness note: the harness-written `Co-Authored-By` trailer 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](https://claude.ai/code/session_012dGTuJen61ECNHXYXKChoC)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8807206 commit d206d08
7 files changed
Lines changed: 603 additions & 218 deletions
File tree
- .claude/skills/pm-dispatch
- scripts/pm
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
181 | 181 | | |
182 | 182 | | |
183 | 183 | | |
184 | | - | |
185 | | - | |
| 184 | + | |
| 185 | + | |
186 | 186 | | |
187 | 187 | | |
188 | 188 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
263 | 263 | | |
264 | 264 | | |
265 | 265 | | |
266 | | - | |
267 | | - | |
268 | | - | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
269 | 269 | | |
270 | 270 | | |
271 | 271 | | |
| |||
276 | 276 | | |
277 | 277 | | |
278 | 278 | | |
279 | | - | |
| 279 | + | |
280 | 280 | | |
281 | 281 | | |
282 | 282 | | |
| |||
0 commit comments