Skip to content

ci: Spec Main Shape Gate leaves the required set — advisory on PRs, hourly on main; the required-check pin declares the seven ruled names (objectui#12240) - #12250

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-12240-spec-gate-advisory
Oct 12, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-12240-spec-gate-advisory

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #12240
Clause-②: no

Spec Main Shape Gate leaves the merge queue's required set on the maintainer's ruling B (objectui#12214, ACCEPT 6123370050, open question 1 answered B, filed as objectui#12240). It keeps running and keeps posting its verdict; nothing it verifies changes (the bypass, the anti-false-green marker with the injected sha and time, the attribution). This pull request moves every in-tree declaration of the required set. The ruleset edit itself is the maintainer's act.

The required names, before and after

Before (live, read 2026-10-12 with gh api repos/objectstack-ai/objectui/rules/branches/main, ruleset 11776024, in the endpoint's order):
Lint · Type Check · Build & E2E · Build Docs · Changeset Declaration · Test · Spec Main Shape Gate · Governed Surface Queue Guard

After the maintainer's edit (the same list minus one, order kept):
Lint · Type Check · Build & E2E · Build Docs · Changeset Declaration · Test · Governed Surface Queue Guard

The "after" list is byte-identical to the seven names scripts/check-required-check-set.mjs now declares (PINNED_CONTEXTS = Type Check, WATCHED_CONTEXTS = the other six) and to the committed control fixture ruling-12240.json.

The maintainer's act, in the same sitting as the merge

Settings → Rules → Rulesets → ruleset 11776024 (main) → "Require status checks to pass" → remove Spec Main Shape Gate. Nothing else in the ruleset changes.

Either order is safe, because spec-main-shape-gate.yml keeps its merge_group trigger (see the trigger decision below). One case forces "edit first": if Spec Main Shape Gate happens to be red when this pull request is enqueued (objectstack drift), its own queue build is still gated by the context until the edit.

What required-check-set-patrol reads in between: GREEN, not red

Read from scripts/check-required-check-set.mjs, not assumed: a required context the pin does not declare makes the verdict drifted; exitCodeFor('drifted') is 0; the patrol's "Fail the run" step runs only on a non-zero exit. Measured with this branch's script over the live answer:

  • Merged, edit not yet made (live = 8 names): DRIFTED — every pinned context present; 0 watched missing, 1 undeclared (8 required context(s) read), summary line "Required but not declared here: Spec Main Shape Gate.", exit 0. Pinned by the test on the dated verbatim reading live-2026-10-12.json.
  • After the edit (the ruling-12240.json fixture): INTACT — 1 pinned and 6 watched context(s) all present (7 required context(s) read), exit 0.
  • Today on main, before this PR: the same live answer already reads DRIFTED, naming Governed Surface Queue Guard as required but not declared (enrolled under objectui#6596, never added to the pin). This PR declares it, as the ruling's seven names include it.
  • Edit made before the merge: main's old pin reads DRIFTED naming both halves, exit 0.

The patrol cannot go red from this change in any order: its one pinned name, Type Check, is untouched.

The trigger decision: merge_group stays (H2 refuted on sequencing)

The PM's suggestion was to drop merge_group. Kept, deliberately; the workflow header now says why.

  • Sequencing. The ruled order is merge first, ruleset edit second, and this pull request itself lands through the queue. A merge group runs the workflow file of its own head, so with the trigger dropped this PR's queue build, and every entry built on top of it (max_entries_to_build: 5), would never report a context the ruleset still requires and would wait out check_response_timeout_minutes: 60 and fail: objectui#3523's shape, on the way out. Keeping it makes this PR order-independent.
  • Pin. scripts/__tests__/spec-main-shape-gate.test.ts › "subscribes merge_group, so a maintainer can enrol it without stalling the queue" reds on the drop. Measured: an ablation deleting the two trigger lines ran the five affected test files at 1 failed / 211 passed (that test only), restored to the HEAD blob. merge-queue-reporting.test.ts's derived merge_group floor stops covering this workflow once the Dependabot gate classifies the name NOT_A_GATE (below), so that pin does not bind.
  • Cost of keeping it, from the Actions API: the newest 100 completed merge_group runs of this workflow (2026-10-09T12:50Z to 2026-10-12T12:17Z; total_count 1255) are about 33.6 runs a day at a 13.1-minute median wall, about 440 runner-minutes a day; public repository, not billed. After the edit those runs gate nothing and do not lengthen a queue entry (the queue waits only on required contexts); they occupy runner slots. For scale, pull_request runs: 100 in the newest 1.08 days, 12.1-minute median.
  • Proposed, not taken here: once the patrol reads INTACT on seven, a one-line follow-up drops merge_group and rewords that pin to "re-subscribe before re-enrolling". The report carries it as an open question.

H1: partly refuted, there was a second blocking mechanism in the tree

Whether a check blocks the queue is the ruleset's property, so the workflow needs no mechanism: no continue-on-error (it would hide the verdict), and no placeholder or plumbing step exists in the file. The concurrency rule that never cancels a queue build exists for the required role and is not dead while merge_group is subscribed; left as is. But scripts/dependabot-merge-gate.mjs declared the name in REQUIRED_CONTEXTS, so the Dependabot auto-merge gate waited for the check and refused a red: a pull-request-side block the ruleset edit would not remove, and the line the workflow header itself called the in-tree half of the enrolment. It moves to NOT_A_GATE with its reason; the test pins the move by name with a control (the same red on Type Check still refuses). Window note: between merge and edit the Dependabot gate no longer waits for it while the queue still does; a red there is caught by the queue build.

H3: confirmed

The hourly schedule (cron: '48 * * * *') is in spec-main-shape-gate.yml itself (PR #12232, 21fdeb34). This card owns no line of ci.yml and touches none. NOT MEASURED: the first scheduled run (PR #12232 merged 12:10Z; at 12:55Z the Actions API listed no schedule run of this workflow yet).

H4: every enumeration moved, and the grep

git grep -n 'Spec Main Shape Gate' on main (923504f4): 52 hits in 32 files.

Moved (enumerations of, or claims about, the required set):

  • .github/workflows/spec-main-shape-gate.yml: the header section that said the context was "NOT required yet" is replaced by the advisory posture, the main-red rule and the merge_group reasoning. Comment-only: the parsed YAML is identical to main's.
  • scripts/check-required-check-set.mjs: the pin (tombstone naming this card and ACCEPT 6123370050's answer B; Governed Surface Queue Guard declared).
  • scripts/__tests__/check-required-check-set.test.ts and its fixtures: new dated live reading live-2026-10-12.json, new control ruling-12240.json, type-check-removed.json regenerated from the new control; ruling-9499.json and ruling-9969.json retired (read by nothing else).
  • scripts/dependabot-merge-gate.mjs and its test: REQUIRED_CONTEXTS to NOT_A_GATE.
  • .github/workflows/repo-gates.yml: its header's present-tense list of required names is dated to the fold (comment-only).
  • content/docs/guide/ci-cd-pipeline.md: the inventory row, the gate's section, the fold paragraph; scripts/__tests__/ci-cd-pipeline-doc.test.ts: the two command-parity reasons for that section re-worded (keys unchanged).

Count enumerations without the name, moved: required-check-set-patrol.yml ("requires nine check contexts", "the eight WATCHED contexts"), the guide's patrol section ("the other eight", "the nine"), and the script header's present-tense "currently holds nine contexts". Each now points at the script instead of carrying a number (AGENTS.md #9). No script or test reads any of these counts, so nothing was loosened to "at least".

Left, because they are not enumerations of the required set: three pending changesets (11573, 12093, 12170) and three published CHANGELOG entries describe the gate as a compile target; 21 hits in 16 files under packages/ name it as the compile against objectstack main; scripts/spec-main-shape-gate.mjs prints it as a summary heading. None says the context is required. AGENTS.md and CONTRIBUTING.md carry no enumeration of the set.

Gates, on head 22124378

objectstack's dispatch-gates.mjs --commands --repo objectstack-ai/objectui refuses by design (it derives only from an objectstack checkout), so the list is hand-derived from this repository's package.json and workflows:

  • pnpm exec vitest run scripts/ eslint-rules/: 191 files passed, 2 skipped, 1 failed, 5778 tests passed (lock VERDICT command-exit 1). The one failure is dollar-dialect-alias-census.test.ts timing out at 15 s with the import phase at 344 s on a shared box; it reads none of the changed files and passes alone (37/37, 8.1 s).
  • The six core files (check-required-check-set, dependabot-merge-gate, merge-queue-reporting, spec-main-shape-gate, ci-cd-pipeline-doc, check-merge-queue-head): 252/252.
  • node scripts/check-required-check-set.mjs --self-test: 32 cases pass, exit 0. Live: node's fetch answers 401 through the agent proxy (as on objectui#12214), so the live answer was taken with gh and fed through --fixture: DRIFTED, exit 0, as above.
  • pnpm type-check:scripts: exit 0 (--listFilesOnly includes the three edited test files).
  • eslint --no-inline-config --format json over the five changed .mjs/.ts files: 5 files, 0 errors, 0 warnings. The narrowing is proven: population from eslint.config.js (flat-config default js/mjs plus files: ['**/*.{ts,tsx}']; no ignore covers scripts/); count from the JSON output; no parserOptions.project / projectService (type-aware linting off) and no rule under eslint-rules/ reads the disk, so this diff cannot move the verdict on an untouched file.
  • Exit 0 each: check-governed-queue-guard --self-test (and --test over the 12 paths: NOT GOVERNED), check-merge-queue-head --self-test, check-changeset-presence (no changeset owed), check-control-bytes, check-doc-links, check-doc-fence-languages, check-action-ref-convention, check-new-cross-file-line-citations (0 new), check-pre-install-import-graph, check-lint-coverage, check-test-path-roots.
  • YAML parse of the three touched workflows: ok, and each parses identical to main's. actionlint: NOT MEASURED (not installed).

Ablations (committed first, through ablation-replace.mjs with its trap and blob proof):

  1. Spec Main Shape Gate re-added to WATCHED_CONTEXTS: 6 failed / 25 passed in check-required-check-set.test.ts; restored to blob 76373853.
  2. Spec Main Shape Gate put back in REQUIRED_CONTEXTS: 4 failed (the new block and the partition test) / 53 passed across dependabot-merge-gate and merge-queue-reporting; restored to blob 21f065a2.
  3. The merge_group counterfactual above: 1 failed / 211 passed; restored to blob ba27aba4.

Acceptance notes

  • The card's acceptance (three consecutive PRs with every required check green inside the Type Check bound; the gate visible as non-required; the hourly run carrying it) is measurable only after the maintainer's edit and the first scheduled runs.
  • The workflow's concurrency and timeout-minutes comments still reason about queue builds; true while merge_group is subscribed and the context is required, moot for an advisory check afterwards. Left untouched (timeout-minutes is out of this card's scope); a merge_group follow-up would be their carrier.
  • .github/workflows/** is called Tier H in the claim; node scripts/check-governed-queue-guard.mjs --test answers NOT GOVERNED for all 12 paths. The PR stays a draft either way: it is a dispatched PR.
  • No changeset: CI, docs and tests only (check-changeset-presence exit 0).

维护者速读(草稿)

改了什么:Spec Main Shape Gate(用 objectstack main 现编出的 spec 编译本仓)从合并队列的必过检查里拿掉。它照样在每个 PR 上跑、照样给出结论,只是不再挡合并;每小时在 main 上跑一次,作为完整测试的一部分。仓内所有"必过检查清单"的记录一起改成新的七项,Dependabot 自动合并也不再等它。它检查的内容一个字没动。

为什么改:你在 objectui#12214 的问题 1 上选了 B(「我接受你的建议」)。这个检查是每个 PR 最慢的一环(约 13 分钟),而且它红的时候多数是 objectstack 那边在变,不是这个 PR 的错。拿掉之后,PR 的等待时间降到 Type Check 那一档。

风险与代价(含回滚):一个 objectstack 改坏 objectui 编译的变化,不再在 PR 上被拦住,而是在下一次整点运行时在 main 上变红;那时按两档 CI 的规则算作 devx 这条线的 main-red,下一个 PR 先修它。巡检(required-check-set-patrol)在合并和你改规则之间读作"有漂移",但不会变红。回滚:在规则集里把这个检查加回去,并还原本 PR 的三个提交;工作流仍订阅合并队列,所以加回去不会卡住队列。

席位意见:

你要做的:批准并合并本 PR;同一次操作里到 Settings → Rules → Rulesets → 11776024(main)→ 必过状态检查,删掉 Spec Main Shape Gate,剩下七项保持不动。先后顺序都可以;只有当这个检查在入队时正好是红的,才需要先改规则再入队。


Generated by Claude Code

… and a main-red on its hourly run (objectui#12240)

The context leaves the merge queue's required set on the maintainer's
ruling B (objectui#12214 ACCEPT 6123370050, question 1). Requiredness is
the ruleset's property, so the workflow needs no mechanism for it: no
`continue-on-error`, no trigger dropped, nothing it verifies touched. The
header now says what each event is for, that a red on the hourly `main`
run is a main-red for the domain:devx lane under objectstack#22998's
rule 3, and why `merge_group` stays subscribed: until the maintainer's
ruleset edit the queue still requires the name, and a workflow that
stopped reporting there would stall every queue build at the 60-minute
timeout.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqNgdPZB2Jnr5HpGbaqLeB
…mbstones Spec Main Shape Gate (objectui#12240)

The maintainer's ruling B (objectui#12214 ACCEPT 6123370050, question 1)
takes `Spec Main Shape Gate` out of the merge queue's required set. The
patrol's declaration (PINNED `Type Check` plus WATCHED) becomes the seven
names the ruling installs: `Spec Main Shape Gate` leaves under a tombstone,
and `Governed Surface Queue Guard`, enrolled under objectui#6596 and never
declared here (every live reading printed it as required but not
declared), joins WATCHED.

The test pins the merge-then-edit window on a verbatim live reading of
2026-10-12: the patrol reads it as DRIFTED naming `Spec Main Shape Gate`,
exit 0. The control fixture is that reading minus the one context; the
ablation fixture is the control minus `Type Check`. The two #9969-era
fixtures, read by nothing else, are retired.

The in-tree half of the removal: `scripts/dependabot-merge-gate.mjs`
moves the name from `REQUIRED_CONTEXTS` back to `NOT_A_GATE` with its
reason, so a Dependabot bump stops waiting for an advisory check; its
test pins the move by name with a control. That also takes the workflow
out of the derived `merge_group` floor, while the workflow keeps the
trigger (see its header). Two other enumerations move with it: the
present-tense ruleset list in repo-gates.yml's header, and the stale
counts in required-check-set-patrol.yml's comments.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqNgdPZB2Jnr5HpGbaqLeB
…urly run, and the patrol section carries no count (objectui#12240)

The inventory row and the gate's section say the context left the
required set by the maintainer's ruling B, what each event is for now,
that a red on the hourly `main` run is a main-red for the domain:devx
lane, why `merge_group` stays subscribed, and that the Dependabot gate
classifies it `NOT_A_GATE`. The fold paragraph's present-tense "their
contexts are required" is dated to the fold. The patrol section's
"the other eight" / "the nine" counts become pointers to the script's
declaration. The command-parity ledger's two reasons for this section
are re-worded to the new classification; their keys are unchanged.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqNgdPZB2Jnr5HpGbaqLeB
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)· PR #12250 · 席位已对照 diff 逐行校正,2026-10-12T13:17Z

改了什么:Spec Main Shape Gate(用 objectstack main 现编出的 spec 编译本仓)从合并队列的必需检查里拿掉。它照样在每个 PR 上跑、照样给出结论,只是不再挡合并;每小时在 main 上跑一次(48 * * * *,PR #12232 已加),作为完整测试的一部分。三个 workflow 文件只改了注释(解析后与 main 逐字相同);仓内所有"必需检查清单"的记录(check-required-check-set.mjs 的七项、Dependabot 自动合并的门、巡检与 CI 指南的计数)一起改成新的七项。它检查的内容一个字没动,没有 continue-on-error。

为什么改:你在 objectui#12214 的问题 1 上选了 B(「我接受你的建议」)。这个检查是每个 PR 最慢的一环(约 13 分钟),且它红的时候约七成是 objectstack 那边在变,不是这个 PR 的错。拿掉之后,PR 的等待时间降到 Type Check 那一档(约 10 分钟,再由 objectui#12222 往下压)。

风险与代价(含回滚):一个 objectstack 改坏 objectui 编译的变化,不再在 PR 上被拦住,而是在下一次整点运行时在 main 上变红;按两速 CI 的规则算 devx 线的 main-red,下一个 PR 先修它。巡检(required-check-set-patrol)在合并与你改规则之间读作"有漂移"但不变红(席位已对照脚本核实:漂移退出码为 0)。回滚:在 ruleset 里把这个检查加回去,并 revert 本 PR 的三个提交;工作流仍订阅合并队列,加回去不会卡队列。

席位意见:建议批准。改动的实质只有一处清单(七项)和 Dependabot 门,工作流本身零语义改动;收益是每个 PR 少等约 3 分钟并止住"objectstack 漂移把 objectui 的 PR 踢出队列"这一类红。dev 提出的后续项(规则改完后让该门不再在合并队列上跑,省每天约 440 runner 分钟的公共 runner 占用)席位取 B,另立一张小卡排在你改规则之后。

你要做的:① 在 PR 上 Approve(席位随后翻正式并入队);② 同一次操作里到 Settings → Rules → Rulesets → main(11776024)→ 必需状态检查,删掉 Spec Main Shape Gate,其余七项不动。先后顺序不限;只有当这个检查在入队时正好是红的,才需要先改规则再入队。

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 12, 2026 13:22
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 12, 2026 13:22
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 12, 2026
Merged via the queue into main with commit 5039e81 Oct 12, 2026
31 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-12240-spec-gate-advisory branch October 12, 2026 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd configuration documentation Improvements or additions to documentation tests

Projects

None yet

2 participants