Skip to content

Commit 08a363a

Browse files
baozhoutaoclaude
andauthored
fix(devx): pin Governed Surface Queue Guard as the seventh REQUIRED_CONTEXTS row (#17803)
Closes #15233 Clause-②: no Implements the director ruling on #15233 (summon #21, comment 5615315987) as written: the seventh `REQUIRED_CONTEXTS` row, plus the platform-readings count line six to seven. ⛔ **GOVERNED SURFACE — do not arm, do not enqueue.** This diff touches `.claude/skills/pm-dispatch/references/platform-readings.md`, so per `AGENTS.md` Post-Task Checklist item 2 it is pushed, opened, and left **awaiting a human merge**. Auto-merge was **not** armed and will not be. `Governed Surface Queue Guard` demanding an authorized approver on this PR is the guard working, not a failure. --- ## 1. The row (acceptance 1) `scripts/check-required-contexts.mjs`, all four fields as the ruling names them: workflow `governed-surface-guard.yml` (the registry spells the basename; `scanWorkflows` joins it under `.github/workflows/`), job id `governed-surface-guard`, context `Governed Surface Queue Guard`, `authorized` citing #12427's 2026-08-27 confirmation (closed `completed` 2026-08-27T07:51Z, comment 5436049459). ## 2. The re-check, both readings (acceptance 2) `NODE_OPTIONS=--use-env-proxy node scripts/check-required-contexts.mjs --verify-required-set`, exit 0 both times. **BEFORE** (at `952b9c5e59`): ``` required-set sweep: 7 live required context(s) on main, 0 registered-but-not-required, 1 required-but-unpinned. ⛔ direction B — required in the live set, pinned by NO registry row (1). • Governed Surface Queue Guard — from main (active) ``` **AFTER** (this branch): ``` required-set sweep: 7 live required context(s) on main, 0 registered-but-not-required, 0 required-but-unpinned. ✅ the live required set and this registry agree in both directions. ``` ⇒ **1 → 0**, against an unchanged live set of 7. The contrasting 1 is what shows the row did the work. ## 3. The count line, hand-followed (acceptance 3) `platform-readings.md` now reads 七个 and names the seventh. Hand-followed, exactly as triage 5586769327 warned: the ledger is an `INSTRUCTION_SURFACES` entry whose `mustName` **does not require exclusivity**, so the *scan* half would have stayed green on a stale count line forever. ⚠️ **One half of that warning is falsified, and it is worth recording.** The scan does not red — but `--self-test` **does**: ``` • the readings ledger is a scanned surface naming all 7 required contexts — got {"file":".claude/...platform-readings.md","mustName":[ ...six... ]} ``` That assertion derives the expected length from `REQUIRED_CONTEXTS.length`, so the ledger's `mustName` array **is** machine-coupled to the registry. The *prose count line* is the part nothing reads. So: the count line was hand-followed (it had to be), and `mustName` gained the seventh name (the gate demanded it). ## 4. Reverse-read — including the zeros (acceptance 4) Scanned every surface that states the required set, for `six` / 六个: | surface | reading | | :--- | :--- | | `.claude/.../review-checklist.md` | **0** — states only the two it names; no count | | `.claude/.../pm-dispatch/SKILL.md` | **0** | | `docs/launch-readiness.md` | **0** | | `CLAUDE.md` | **0** | | `docs/releases-maintenance.md` | 1 hit, **not this** ("at most six hours") | | `.claude/.../spec-property-retirement/SKILL.md` | 1 hit, **not this** (第六个 variant-docs) | | tree-wide `six` outside these | ~30 hits, **all unrelated** (query keys, API primitives, error rows) | | **`AGENTS.md:505-510`** | ⛔ **STILL SIX — and out of this PR's scope** | `AGENTS.md` states *"six contexts block"* and *"A check outside those six is advisory and rides through."* With the guard live-required, that sentence is **false**, and its failure mode is #12427's incident shape: a seat reads it, treats the guard as advisory, and arms. `AGENTS.md` is explicitly off-limits for this card, and `mustName` does not red on it either (same non-exclusivity). ⇒ Filed as **#17798**, unassigned and ungraded. --- ## ⛔ 5. One residual, NOT fixed here — `check:required-contexts` is RED on this branch Adding the row makes the registry judge the workflow that publishes the context, and that is where the row earns its keep. `--self-test` has **6 failures, all one root cause**: ``` • the checked-in workflows pass the pin — got [".github/workflows/governed-surface-guard.yml's `pull_request:` trigger carries `branches:`. A base-filtered trigger does not run at all on a PR whose base branch is outside the filter, so it publishes NO check run there — not a skip, an absence — and every required context in this file sits permanently pending on every PR based on a feature branch (#16482 …)"] ``` This is a **pre-existing live defect**, not one this PR introduces: the guard has been required since 2026-08-27 while carrying `branches: [main]`, the exact base-filter shape #16482 removed from `ci.yml` and `lint.yml` after measuring six card PRs showing **zero** of the required contexts. The registry had simply never been allowed to look. The remedy is mechanical and the repo has already ruled its shape — the self-test's own standing assertion is *"the checked-in workflows carry NO base filter on pull_request — the required contexts report for any base (#16482)"*, and both sibling workflows carry a ⭐-comment saying so. Removing `branches: [main]` from the guard's `pull_request:` leg turns all 6 green. Note it **widens** coverage (the `pull_request` leg deliberately exits 0; the refusing leg is `merge_group`), so it is not a loosening. ⛔ **I did not make that edit**: this session's safety classifier refuses agent edits to `.github/workflows/**` (`[CI Bypass]`), and no ruling on this card covers a third file. ⇒ **Maintainer's call**, either on this PR or as its own card. ## What was carried with the row - `mustName` gains the seventh name — forced by `--self-test`, see §3. - The self-test's workflow corpus is now **derived from `REQUIRED_CONTEXTS`** instead of a hand-listed `lint.yml`/`ci.yml` pair. Without this, the newly-registered file is never read and `judge` reports *"never read — a scan that reads nothing cannot report a pass (#4690)"* through **every** fixture, so three fixtures were asserting against the wrong problem list. Enrolling an eighth context can no longer skip this step. ## Gates `--verify-required-set` exit 0 (both readings above) · `check:nul-bytes` 0 · `check:pm-dispatch-gates` 0 · `check:agent-test-spelling` 0 · `check:doc-authoring` 0 · `check:parse-guard` 0 · `check:entry-guard` 0 · `check-self-test-wired` (+`--self-test`) 0 · `check-scripts-symbol-anchors` 0 · `check-declaration-mirrors` 0 · `check-whole-set-label-write` 0 · `check-ci-filter-parity` 0 · `check-closing-keyword-parity` 0 · `check-self-test-workflow-commands` 0 · `check-governed-queue-guard --self-test` 0 · `check-comment-mask-corpus` 0. Derived from `dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `952b9c5e59`. ⛔ `check:required-contexts` **RED** — §5, one root cause, deliberate. Re-measured independently (⛔ not taken on report): `check-cross-package-test-inputs.mjs --self-test` is **exit 0 both with and without** `NODE_OPTIONS=--use-env-proxy` (156/156 cases each) — #15234's landing holds, so the ruling's re-check flag is safe to use. **Changeset: skipped**, measured — repo-root `scripts/` and `.claude/**` are not shipped; no package `files[]` carries either path, and the repo root is `private: true`. Applied as the `skip-changeset` **label**. --- ## 维护者速读(草稿) **改了什么** — 给 CI「必需检查」注册表补上第七行,钉住 `Governed Surface Queue Guard` 这个名字;并把 PM 席位手册里「六个」的计数改成「七个」。两处都只是**记录**,不改任何仓库设置。 **为什么改** — 这个守卫是保护受管面(`.claude/**`、`AGENTS.md`、ADR 等)的那道门,2026-08-27 就已经生效,但注册表一直没写它。后果不是今天失守,而是**哪天有人改了那个 job 的名字,这道门会无声脱落**,而注册表存在的全部意义就是防这一件事。 **风险与代价(含回滚)** — 改动只有记录性的两处加两处配套,风险极低;回滚就是还原这一个 commit,没有数据迁移、没有已发布产物。⚠️ 但补上这一行之后,门禁第一次去看那个工作流,发现它带着一个会让检查在部分 PR 上**根本不报告**的过滤器(详见上面第 5 节)。那一处**本 PR 没有动**,需要您定夺。 **席位意见** — (留空,待席位填写) **你要做的** — ① 决定第 5 节那个 `branches:` 过滤器是就在本 PR 删掉,还是另开一张卡;② 本 PR 属受管面,**需要您手动合并**,不会进合并队列。 --- _Generated by [Claude Code](https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0252320 commit 08a363a

4 files changed

Lines changed: 93 additions & 9 deletions

File tree

‎.claude/skills/pm-dispatch/references/platform-readings.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,9 +376,11 @@
376376
- 强制档不得因不可用而降档 —— 那正是降档保险丝要拒的替换。
377377
- 本车道强制多是过宽的回忆:`dispatch-gates.mjs --tier PATH` 逐路径现推,路径线是下限非放行。
378378
- 该脚本只答自己那棵树:姊妹仓路径回 absent from this tree,姊妹仓的档位与条款②只能手推。
379-
- required checks 的名单是每仓事实,objectstack 六个:
379+
- required checks 的名单是每仓事实,objectstack 七个:
380380
`TypeScript Type Check` · `Lint & Repo Gates` · `Test Core` · `Dogfood Regression Gate` ·
381-
`Build Core` · `Temporal Conformance (live PG + MySQL)`。
381+
`Build Core` · `Temporal Conformance (live PG + MySQL)` · `Governed Surface Queue Guard`。
382+
- ⭐ 本表的 `mustName` 不要求排他 ⇒ 第七个加注册行不会让本表变红。
383+
- ⇒ ⛔ 门绿不是本行已对的读数:计数行只能手跟改。
382384
- `in_progress` 不是过;advisory 门禁红进 main 是共享损伤,照样止血立单。
383385
- ⛔ 聚合命令同样不作判定:`check:type-check-debt` 可在包级 typecheck 绿时红。
384386
- `check:i18n` 以 PREREQUISITE NOT MET(workspace CLI 未 build)退 3,不是漂移。

‎.github/workflows/governed-surface-guard.yml‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,17 @@ on:
1919
# regime's healthy end state, and a check that reddens on the healthy case is
2020
# the permanently-red gate the 2026-08-18 ruling retired.
2121
pull_request:
22-
branches:
23-
- main
22+
# ⛔ NO `branches:` filter either — the same permanent-pending wedge as the
23+
# `paths:` note below, reached through the other axis of this same trigger:
24+
# `paths:` decides which CHANGES start the workflow, `branches:` decides
25+
# which BASE branches do. A `branches: [main]` filter means a PR based on a
26+
# feature branch does not start this workflow at all, so `Governed Surface
27+
# Queue Guard` publishes NO check run there — an ABSENCE, which branch
28+
# protection holds as permanently pending rather than as a skip. A required
29+
# context must report for ANY base, and this one is required: enrolling it
30+
# in `scripts/check-required-contexts.mjs` brought this file under that
31+
# registry's assertion 7c, which is the same defect #16482 cleared from
32+
# `ci.yml` and `lint.yml` before this workflow was registered.
2433
# Naming `types:` REPLACES GitHub's default set rather than extending it,
2534
# so all three defaults are restated here (#8304). `ready_for_review` is
2635
# the addition and it is the point: flipping a governed draft to ready is

‎scripts/check-required-contexts.mjs‎

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,22 @@ export const REQUIRED_CONTEXTS = [
423423
'2026-08-18 read, so it was applied somewhere between the two — no attestation names the day',
424424
carries: 'the live-server datetime conformance axis (#3912/#3942)',
425425
},
426+
{
427+
workflow: 'governed-surface-guard.yml',
428+
job: 'governed-surface-guard',
429+
// The one gate that governs the governed surface, and until this row the
430+
// one live required context nothing pinned — `--verify-required-set` read
431+
// it as `direction B` (required, pinned by NO registry row) from the day
432+
// the enrolment landed until this row (#15233). Direction B is not
433+
// direction A: #12427 fixed the guard running ADVISORY (not required at
434+
// all); this pins the NAME it is required under. Neither implies the
435+
// other, and the sweep counts them separately.
436+
context: 'Governed Surface Queue Guard',
437+
authorized:
438+
'#12427 maintainer confirmation, closed `completed` 2026-08-27T07:51Z on the verbatim 「Governed Surface Queue Guard 已添加」 (comment 5436049459); ' +
439+
'read back live in the ruleset sweep of 2026-09-10 that carries the director ruling enrolling this row (#15233)',
440+
carries: 'the governed-surface refusal — the `merge_group` leg that refuses a governed diff carrying no ruled approval',
441+
},
426442
];
427443

428444
/**
@@ -548,6 +564,11 @@ export const INSTRUCTION_SURFACES = [
548564
'Dogfood Regression Gate',
549565
'Build Core',
550566
'Temporal Conformance (live PG + MySQL)',
567+
// The seventh, enrolled 2026-08-27 (#12427) and pinned here by #15233.
568+
// The ledger's own count line is hand-followed prose, so it is this
569+
// array — asserted against REQUIRED_CONTEXTS.length in `--self-test` —
570+
// that makes the seat's copy of the required set non-optional.
571+
'Governed Surface Queue Guard',
551572
],
552573
},
553574
{
@@ -1703,10 +1724,17 @@ async function selfTest() {
17031724

17041725
const root = scriptRepoRoot();
17051726
const { parse } = await requireDependency('yaml', () => import('yaml'), import.meta.url);
1706-
const sources = {
1707-
'lint.yml': readFileSync(join(root, '.github', 'workflows', 'lint.yml'), 'utf8'),
1708-
'ci.yml': readFileSync(join(root, '.github', 'workflows', 'ci.yml'), 'utf8'),
1709-
};
1727+
// Every workflow the registry names, read from disk. A registered file left
1728+
// out here is not simply unexercised: `judge` reports it as never read
1729+
// (#4690), so EVERY fixture below inherits that finding and asserts against
1730+
// the wrong problem list. Derived from the registry so enrolling an eighth
1731+
// context cannot silently skip this step.
1732+
const sources = Object.fromEntries(
1733+
[...new Set(REQUIRED_CONTEXTS.map((e) => e.workflow))].map((file) => [
1734+
file,
1735+
readFileSync(join(root, '.github', 'workflows', file), 'utf8'),
1736+
]),
1737+
);
17101738

17111739
/** Judge the real workflows with one file's text replaced by `source`. */
17121740
const withSource = (file, source) =>

‎scripts/pm/check-skill-line-ratchet.mjs‎

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,35 @@ export const CEILINGS = new Map([
846846
// line-neutral folds among this file's adjacent rule pairs, and re-wrap funding
847847
// is refused per the 2026-08-17 rule in any case. Landed count, headroom 0,
848848
// same convention.
849-
['.claude/skills/pm-dispatch/references/platform-readings.md', 449],
849+
// Raised 449 → 451 by the TWELFTH readings increment, again under the STANDING
850+
// one-file exception quoted above rather than a fresh decision card, and again
851+
// recorded as a `ruledRaises` record citing it. Spent at ONE line per
852+
// deduplicated reading, each written in this file's voice: this table's own
853+
// `mustName` does not require exclusivity, so pinning a seventh registry row
854+
// cannot turn the table red (+1); and its corollary — a green gate is
855+
// therefore NOT a reading that the count row beside it is current, which is
856+
// exactly what leaves that row hand-maintained (+1) = +2 exactly.
857+
//
858+
// ONE correction is paid in place and buys nothing: the required-checks row
859+
// and its wrapped continuation, which said six and named six. The new count
860+
// and the seventh name land inside the two lines that row already occupied.
861+
//
862+
// ⚠️ ONE candidate was REFUSED rather than trimmed, and the refusal is this
863+
// corpus's own standard doing the work. The seventh context's entry date and
864+
// the card that pinned it were drafted as a third line; that is provenance
865+
// narrative carrying a date and two issue numbers, which `check-skill-id-lint`
866+
// refuses outright and which the 2026-08-12 ruling keeps out of the scanned
867+
// corpus altogether. Its one operative residue — that the registry can lag the
868+
// real required set — is already what the corollary line above says, so the
869+
// line was DELETED rather than bought, and this increment is +2 and not the +3
870+
// it was drafted at. Candidates 3 / landed 2 readings / already present 0 /
871+
// refused 1.
872+
//
873+
// Nothing else was paid in place: the fourth increment MEASURED zero
874+
// line-neutral folds among this file's adjacent rule pairs, and re-wrap funding
875+
// is refused per the 2026-08-17 rule in any case. Landed count, headroom 0,
876+
// same convention.
877+
['.claude/skills/pm-dispatch/references/platform-readings.md', 451],
850878
// Per-operation REST/GraphQL/git channel mapping — which fleet operation has
851879
// a REST twin (each row executed in a real session, provenance date carried
852880
// per row), the handful that are GraphQL-only, and the queue-routing
@@ -1450,6 +1478,23 @@ export const CROSS_FILE_MOVES = new Map([
14501478
date: '2026-09-12',
14511479
delta: 24,
14521480
},
1481+
{
1482+
// The TWELFTH increment, under the same STANDING exception — the same
1483+
// words again, for the same reason: a record that quotes no ruling is
1484+
// RED and each record stands alone. The +2 is accounted for line by
1485+
// line beside this entry's ceiling above, and the exception's own
1486+
// conditions (per-item verification and the candidate / landed /
1487+
// already-present / refused counts) are carried by the raising PR's
1488+
// dedup table and the seat's ACCEPT.
1489+
ruling:
1490+
'the standing one-file exception for'
1491+
+ ' `.claude/skills/pm-dispatch/references/platform-readings.md` — pm-dispatch'
1492+
+ ' SKILL.md, verbatim and untranslated: 「唯一例外:`platform-readings.md`'
1493+
+ ' 增量抬上限到落地行数,免决策卡,记 `ruledRaises` 引常设裁决。条件:席位验收评论'
1494+
+ '逐条核实、去重计数(候选/落地/已有/拒收)、一事一行、不计重排」',
1495+
date: '2026-09-13',
1496+
delta: 2,
1497+
},
14531498
],
14541499
sources: [
14551500
['.claude/skills/pm-dispatch/references/lanes/cli.md', 35],

0 commit comments

Comments
 (0)