Repository navigation
Commit fc1a790
fix(pm): the queue guard's remedy names the Merge button's bypass-rules option, and a pin reads the ruleset (#19448)
Fixes #19344
Clause-②: no
Item 6 of the maintainer's ratified directive — verbatim 「1 2 3 4 5 6 8」
(record `5750077963`, relayed onto this card as comment `5750078192`) —
narrowed by the director's pointer `5749924838`: route **(a)** was taken
in person, PR #19024 merged by bypass (`merged_by` os-zhuang, squash
`2277d1f`). What remained is exactly what this PR does — the guard's
remedy sentences name a path that EXISTS on ruleset `main`, and a pin
fails when they name one it does not offer.
## The defect
Ruleset `main` (id 12119582, enforcement `active`, target the default
branch) carries a `merge_queue` rule and lists `Governed Surface Queue
Guard` among its seven required contexts. So the only Merge button an
ordinary account is offered is "Merge when ready" = enqueue, and the
size limb then refuses the queued group. The remedy prescribed 人工直合 —
"the maintainer's own click" — without naming WHICH click, and the only
click that is not an enqueue is the Merge button's **bypass-rules**
option, offered only while the ruleset configures a bypass actor. With
none configured, the remedy named a terminal nobody could reach: PR
#19024 was enqueued three times and refused three times.
## Before / after — the four sentences
Each keeps 人工直合 as the NAME of the act (the 2026-09-18 ruling stands
untouched); what changes is the description of how the act is reached.
**1. The header sentence (`:410`, the size limb's own contract)**
Before:
```
* maintainer's own click (人工直合). An authorized APPROVED review lifts a
```
After: the same opener, then — `main` mandates the queue and requires
this check, so the only Merge that is not an enqueue is the Merge
button's BYPASS-RULES option, offered only while the ruleset configures
a bypass actor; while none was, the remedy named a terminal nobody could
reach and PR #19024 was enqueued and refused three times; that it IS
offered is a ruleset fact the pin reads.
**2. The governed limb's early warning (`renderGuardVerdict`, Tier H
block)**
Before:
```
Unapproved, the maintainer's own direct merge (人工直合) is
the only landing this pull request has.
```
After:
```
Unapproved, the maintainer's own direct merge (人工直合) is the
only landing this pull request has, and it IS the Merge button's bypass-rules option —
offered only while ruleset `main` configures a bypass actor (#19344).
```
**3. The governed limb's refusal (`renderGuardVerdict`, remedy item 2)**
— same replacement, plus "the audit log records it".
**4. The SIZE limb's refusal (`renderSizeVerdict`, remedy item 2)** —
rendered on the #19024 shape:
```
2. Then a HUMAN MERGE — the same terminal a governed diff has: ACCEPT on the card,
`needs-user-decision` on the PR, a final 维护者速读, review requested from GOVERNED_APPROVERS
(os-zhuang, hotlong); the maintainer's own click lands it (人工直合) — and that
click is the Merge button's bypass-rules option, offered only while ruleset `main` configures a
bypass actor — ⛔ NOT a second Merge button: `main` mandates the queue and requires this check, so
with none configured every re-enqueue comes back here (#19344). The audit log records the bypass
and `check-governed-merges` lists such a landing on size.
```
## The ruleset reading this seat measured
`GET /repos/objectstack-ai/objectstack/rulesets/12119582`, with this
seat's token, on the day this PR was written: **HTTP 200**, and the
response carries **no `bypass_actors` key at all** — not `null`, absent.
The keys it does return are `id name target source_type source
enforcement conditions rules node_id created_at updated_at
current_user_can_bypass _links`, and `current_user_can_bypass` reads
`"never"`.
That is a different fact and is recorded beside it: it says THIS token
is not itself a bypass actor, which is true of every agent seat and says
nothing about whether the ruleset configures one for the maintainer. The
director's earlier pointer read the field as `null`; this seat's read
gets no key. Both are "cannot conclude", and the pin treats them
differently only in what it prints.
`check-required-contexts.mjs`'s measured header is the authority on why:
the ruleset endpoints answer 200 for `metadata=read`, but the bypass
roster is an `administration` field, and `administration` is not one of
the 17 permissions a workflow may grant its `GITHUB_TOKEN`. So no token
this repository's CI can hold will ever read `bypass_actors`.
## The pin, and its three verdicts
One new battery in `--self-test`, five cases, driving two new pure
exports (`bypassActorReading`, `remedyPathVerdict`) over a frozen copy
of the measured response:
| `bypass_actors` as read | reading | pin |
|:--|:--|:--|
| key absent (this seat's and every Actions token's answer) |
`unreadable` | **PASSES**, printing `bypass_actors: unreadable with this
token (the key is absent); current_user_can_bypass: "never"` |
| present and `[]` or `null` | `not-offered` | **REDS** — the remedy
names a path the ruleset does not offer |
| present with at least one actor | `offered` | PASSES |
Two ways to red, not one: the ruleset is READ to offer none, **or** the
remedy stops naming a path at all — which is the defect this card filed,
and a pin that only checked the first would sit green through it. The
self-test prints the reading on every run, so "unreadable" is never a
silent pass. ⛔ It never asserts a path from a field it did not read, and
⛔ it never reds CI on a permission difference.
## Recorded, not live — the four axes
The card allowed either a live read or a recorded fixture. **Recorded,
and the live read deliberately not added.**
- **实际业务需求** — measured, not supposed: `bypass_actors` is unreadable to
this seat's token AND to any Actions token (above). A live read wired
into this self-test would therefore answer `unreadable` on every CI run
in existence — it would assert nothing, on every run, while adding a
network call. The real need is that the remedy sentence names a
reachable path; the only party who can verify reachability is the
maintainer, and their verification already happened (the #19024 bypass
merge). A recorded reading is what that evidence looks like in this
file.
- **项目长远合理性** — this self-test is the **first step** of the required
`Governed Surface Queue Guard` job, run under `bash -e` as the
precondition for trusting the guard, and its own usage line declares it
`offline, no network, no git`. Making a merge precondition depend on
api.github.com reachability and on a token's permission tier is the
permanently-red-gate shape this repo has already retired. The precedent
is in-repo and exact: `check-required-contexts.mjs` keeps a frozen
`RULESET_SNAPSHOT` in its self-test and leaves the live diff to a
report-only mode that never runs in CI, for the same structural reason.
- **防 AI 写代码犯错** — a live read is the lenient-consumer shape here: it
passes for every token that cannot see the field, so the assertion would
be phantom and the next author would read green as "the path is
reachable". The recorded form makes the claim declared and falsifiable —
change the remedy, and the pin demands a reading that offers the path;
the reading is printed rather than swallowed.
- **创业阶段不扩散需求** — one battery, two pure exports, one frozen object, no
new entry point, no new script, no new workflow, no widened token scope.
Net **+60 lines**, exactly the budget.
The fixture's cost is drift, and it is bounded on purpose: it carries
its provenance in the comment above it (endpoint, id, read date, token
class), its reading is printed on every self-test run rather than
asserted silently, and the field it records is one no CI token can
re-read anyway — so a live read would not have detected drift either.
## Evidence
- `node scripts/pm/check-governed-queue-guard.mjs --self-test` :: exit 0
— **301 cases** (296 before; +5 is exactly the new battery), and the run
prints `ℹ #19344 ruleset reading — bypass_actors: unreadable with this
token (the key is absent); current_user_can_bypass: "never"`.
- **Ablation** (`scripts/ablation-replace.mjs`, wrap mode, on the
committed state): anchor `click is the Merge button's bypass-rules
option` replaced in `renderSizeVerdict` — on-disk proof `anchor 1 -> 0,
blob 5b75964 -> e6ee4e00f482` — self-test went **red, 3 of 301**,
naming `all-four-remedies-NAME-the-bypass-rules-option…`,
`a-field-this-token-cannot-see-is-UNREADABLE-and-PASSES…` and
`one-configured-bypass-actor-makes-the-named-path-REACHABLE…`. Restore
proven byte-identical: `blob == HEAD (5b75964)` and `git diff HEAD`
empty. The direction is the expected one (red), and it reds via BOTH
limbs of the contract, which is what "two ways to red" means.
- **Derived gate union** (`node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`, no paths, 31 families): 30 run with
exit captured before any pipe, **all exit 0**; `pnpm
check:pm-dispatch-gates` was still running when this PR was opened and
is reported in the card comment. `--ran` reconciliation: 31 derived, 30
accounted with real exit codes, 0 NOT-MEASURED among them.
- `node scripts/pm/check-governed-merges.mjs --test
scripts/pm/check-governed-queue-guard.mjs` :: exit 0 — **NOT governed**;
ordinary queue landing applies. `--pr 19024` :: exit 3 — the
size-decided landing is listed, which is the recognition the remedy text
now points at.
- PM mechanism assumptions: (1) confirmed — the three sentences read as
described on `origin/main`; (2) PR #19379's region `:268–:294` is
untouched by this diff; (3) confirmed — the self-test is wired at
`.github/workflows/governed-surface-guard.yml`, extended in place, no
second entry point; (4) confirmed above.
## Acceptance notes
- The ruleset response carries `current_user_can_bypass`, which an
ordinary token CAN read and which directly answers "is the bypass option
offered to the account asking". Nothing in this repo probes it live; it
is recorded in this fixture only. Observation, not filed — it is a
capability nobody has pulled on, and the four-axis call above is not to
add a live probe to a required precondition.
- `check-required-contexts.mjs`'s `RULESET_SNAPSHOT` records the
2026-08-18 reading with **six** required contexts; the live ruleset now
carries **seven** (`Governed Surface Queue Guard` joined since). That
file's assertions are deliberately written on the SHAPE, not on
membership, and its header says so, so nothing is wrong — but the
snapshot is a frozen historical reading and reads at a glance like a
current one. Observation, not filed.
- ⛔ Not touched, per the card's own "Not this card": the 5000-line
threshold (it lives in `check-governed-merges.mjs`, imported here), the
required-context set, the 「THIRD leg」 header at `:268–:294` (PR #19379's
region), and any workflow.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 04d639c commit fc1a790
1 file changed
Lines changed: 67 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
407 | 407 | | |
408 | 408 | | |
409 | 409 | | |
410 | | - | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
411 | 417 | | |
412 | 418 | | |
413 | 419 | | |
| |||
547 | 553 | | |
548 | 554 | | |
549 | 555 | | |
| 556 | + | |
550 | 557 | | |
551 | 558 | | |
552 | 559 | | |
553 | 560 | | |
554 | | - | |
| 561 | + | |
555 | 562 | | |
556 | 563 | | |
557 | 564 | | |
| |||
1376 | 1383 | | |
1377 | 1384 | | |
1378 | 1385 | | |
1379 | | - | |
1380 | | - | |
| 1386 | + | |
| 1387 | + | |
| 1388 | + | |
1381 | 1389 | | |
1382 | 1390 | | |
1383 | 1391 | | |
| |||
1469 | 1477 | | |
1470 | 1478 | | |
1471 | 1479 | | |
1472 | | - | |
| 1480 | + | |
| 1481 | + | |
1473 | 1482 | | |
1474 | 1483 | | |
1475 | 1484 | | |
| |||
1925 | 1934 | | |
1926 | 1935 | | |
1927 | 1936 | | |
| 1937 | + | |
| 1938 | + | |
| 1939 | + | |
| 1940 | + | |
| 1941 | + | |
| 1942 | + | |
| 1943 | + | |
| 1944 | + | |
| 1945 | + | |
| 1946 | + | |
| 1947 | + | |
| 1948 | + | |
| 1949 | + | |
| 1950 | + | |
| 1951 | + | |
| 1952 | + | |
| 1953 | + | |
| 1954 | + | |
| 1955 | + | |
| 1956 | + | |
| 1957 | + | |
| 1958 | + | |
| 1959 | + | |
| 1960 | + | |
| 1961 | + | |
| 1962 | + | |
1928 | 1963 | | |
1929 | 1964 | | |
1930 | 1965 | | |
| |||
2003 | 2038 | | |
2004 | 2039 | | |
2005 | 2040 | | |
2006 | | - | |
| 2041 | + | |
| 2042 | + | |
| 2043 | + | |
| 2044 | + | |
| 2045 | + | |
2007 | 2046 | | |
2008 | 2047 | | |
2009 | 2048 | | |
| |||
3851 | 3890 | | |
3852 | 3891 | | |
3853 | 3892 | | |
| 3893 | + | |
| 3894 | + | |
| 3895 | + | |
| 3896 | + | |
| 3897 | + | |
| 3898 | + | |
| 3899 | + | |
| 3900 | + | |
| 3901 | + | |
| 3902 | + | |
| 3903 | + | |
| 3904 | + | |
| 3905 | + | |
| 3906 | + | |
| 3907 | + | |
| 3908 | + | |
| 3909 | + | |
| 3910 | + | |
| 3911 | + | |
3854 | 3912 | | |
3855 | 3913 | | |
3856 | 3914 | | |
| |||
4443 | 4501 | | |
4444 | 4502 | | |
4445 | 4503 | | |
4446 | | - | |
| 4504 | + | |
| 4505 | + | |
| 4506 | + | |
4447 | 4507 | | |
4448 | 4508 | | |
4449 | 4509 | | |
| |||
0 commit comments