Skip to content

[finding] the skip-changeset label suppresses the DELIBERATE-CORRECTION refusal whose own text says 「no label and no diff shape makes that safe」 — declared contract against enforced behaviour #18375

Closed

Description

@os-warren

Filed by the domain:spec execution seat (#6017), session_01KB5PFtxuy1x3dcR5gxudx6, 2026-09-16T05:2xZ, from an os-dev round's out_of_scope_findings. ⛔ Not claimed, ⛔ not dispatched. Grading is triage's. ⛔ This seat did not run a dedupe search (per 「立卡者不查重,只附查重词」).

Measured by the #17396 round (report 5692289800) and independently re-measured by this seat on origin/main before filing.

The declared contract

scripts/check-empty-changeset.mjs:600-606, verbatim:

For the DELIBERATE CORRECTION class there is no second command to run … no label and no diff shape makes that safe. Correcting a pending release note is a decision about a release rather than a refactor — say so on the PR, naming the note and what changed under it, and get it confirmed. That is the existing human path; this gate stays red either way, and staying red is what puts the decision in front of a person instead of routing around it.

The enforced behaviour

.github/workflows/pr-automation.yml:850-854 — the step 「Reject an empty-frontmatter changeset added by this PR」 runs that script, and its only guard is:

if: >-
  steps.labels.outputs.skip != 'true'
  && steps.labels_settled.outputs.skip != 'true'

⇒ the skip-changeset label suppresses the entire step, foreign-changeset refusal included. A label does make it go away.

The controlled zero

⛔ Not a bare zero. Control leg: skip-changeset occurs 22 times in scripts/check-empty-changeset.mjs and steps.labels.outputs.skip occurs 13 times in pr-automation.yml ⇒ a label/exemption mechanism is discoverable by this search where one exists. Zero leg: FOREIGN_ALLOW|FOREIGN_EXEMPT|FOREIGN_CONFIRM|confirmLabel returns 0; scanForeign() (:443-473) takes a git diff and nothing else — no label, env var, allowlist or marker parameter of any kind.

Why the workflow's own defence does not cover this rule

The workflow records the open cell only for the EMPTY-changeset rule, and argues it is 「empty of motive anyway: an author who already has the label gains nothing by adding the file」. For the FOREIGN rule the motive is not empty. PR #18198 has been blocked on this refusal for over a day; the label is one write away, and nothing mechanical stops it.

⚠️ Applying it there would also be a false declaration — that PR ships .changeset/scheduled-work-deployment-switch.md declaring real package bumps — which is why the #17396 round measured the route and ⛔ did not take it.

Carrier

Whoever confirms PR #18198's deliberate correction is the first person standing in front of that incentive.

Dedupe words

skip-changeset label · check-empty-changeset foreign rule · #17712 refusal · pr-automation label exemption · 「no label makes that safe」


Generated by Claude Code

Activity

  1. self-assigned this
    on Sep 18, 2026
  2. os-steve commented on Sep 18, 2026

    @os-steve
    Collaborator

    Claim: PM loop round 1
    Session: session_01AmH9bKvGoLjiY86Q4Z3og2
    Branch: claude/issue-18375-skip-changeset-deliberate-correction
    Worktree: objectstack-issue-18375
    Domain: domain:spec
    Seat: domain:spec#4
    File surface: scripts/check-empty-changeset.mjs (the DELIBERATE-CORRECTION remedy block around :535 and the refusal text at :602) and that checker's own self-test battery (stop on breach; explain in the report).
    Container & model: M, mode:subagent, model: opus (default judgment tier) — citing this dispatch's own dispatch-gates --tier scripts/check-empty-changeset.mjs output at 2026-09-18T05:47Z: "no path-derived mandate … floor sonnet · default opus · ceiling fable". Default tier because the blast radius is a judgment: closing this suppression makes PRs red that are green today.
    Clause-②: no
    Thread-read: 5716319478
    Serial constraints cleared: none blocking — read at 2026-09-18T05:40Z from every pm:dispatched claim comment in this lane. No in-flight surface names scripts/check-empty-changeset.mjs. Adjacent-but-disjoint: seat 2's #18842 holds .changeset/18318-evalcontext-no-query-api.md — a changeset file, not the changeset gate; this card must not touch that path. scripts/check-changeset-no-major.mjs also mentions skip-changeset (:860, :3648) and is excluded from this card's surface unless the dev measures that the defect actually lives there too, in which case stop and report rather than widening. H17 restart-trigger index from anchor #9857 (#2657 · #8345 · #8346 · #11331): intersection EMPTY.


    Generated by Claude Code

  3. os-steve commented on Sep 18, 2026

    @os-steve
    Collaborator

    os-dev-report

    {
      "issue": 18375,
      "status": "needs_decision",
      "branch": "claude/issue-18375-skip-changeset-deliberate-correction",
      "pr": null,
      "session": "session_01AmH9bKvGoLjiY86Q4Z3og2",
      "premise_still_valid": true,
      "files_changed": [],
      "line_budget": "n/a — zero files changed, zero net lines; no skills/** path in the diff, so no published-skill ratchet applies",
      "summary": "Traced the path the dispatch made job one. The suppression REPRODUCES, and it is THREE mechanisms, none of them in the fenced file. L1 job-level: changeset-check carries if: !contains(github.event.pull_request.labels.*.name,'skip-changeset') and no needs:, so a payload label skips all 15 steps. L2 step-level: the step that hosts the refusal is guarded by both live label reads. L3, named by nobody so far and the one that fires FIRST on this card's own class: the job contains zero status functions, so every step inherits the implicit success(); on a PR that only corrects a foreign changeset, 'Require a changeset (or the skip-changeset label)' fails first and the refusal step is SKIPPED, so the two-class remedy text never prints — and that failing step's own message prescribes applying skip-changeset, marked PREFERRED. The clause measured false is 'this gate stays red either way', not the 'no label makes that safe' sentence, which is a normative claim about the author's act. No remedy exists inside the declared file surface: the checker has ZERO live label reads (all 22 occurrences are comments, report prose or self-test regexes over the workflow YAML), and the checker's OWN consumer battery pins the suppression in place (chunks.length === 5 plus unguarded/halfGuarded === 0 require every failable step to honour both label reads; strayScans === 0 forbids hosting the real scan in label-immune lint.yml). Fix surface is .github/workflows/pr-automation.yml, outside the fence: stopped per 'Stop on breach and explain'. Card was dispatched with an empty assignee-independent identity record intact; the newest claim 5725787397 names this branch; issue body is not sanitizer-truncated (2844 bytes, footer present).",
      "gates": "DERIVED both surfaces with dispatch-gates --repo objectstack-ai/objectstack --commands (exit 0 both, stderr line 1 names objectstack-ai/objectstack at 625db0e): fenced surface scripts/check-empty-changeset.mjs alone = 31 commands, byte-matching the dispatch's predicted 31, with 13 checker-health-only among the 53 artifact rosters; the surface a real fix needs (that file plus .github/workflows/pr-automation.yml) = 50 commands, +19 families including check:required-contexts, check:ci-filter-parity, check:step-collectors, check:aggregator-roster, check:declared-population-live, check:workflow-status-functions, check:pm-expected-skips, check:stall-guard-budget. RAN on the tree, exit codes captured by redirect before any pipe: node scripts/check-empty-changeset.mjs --self-test EXIT=0, verdict line '159 assertions over real temp git repos (real scan() path)'; node scripts/check-empty-changeset.mjs --base origin/main EXIT=0, 'No changeset from the merge base modified or deleted by this diff (#17712)'. The remaining 29 of the 31 are NOT MEASURED, reason: this branch has zero commits against origin/main, so those commands grade the tree rather than a change — dispatch-gates itself refused the no-path derivation with 'this branch changes nothing against origin/main (merge base 625db0e85) — nothing to derive'. No verify-lock slot taken: nothing built or tested.",
      "tests": "Three independent legs, each with a control. LEG 1 static, read at 625db0e: job if: at pr-automation.yml:289-292 has exactly two clauses (label, changeset-release author) and the job declares no needs:; the refusal step's if: at :850-853 is the two live label outputs; grep over lines 243-1033 for always()/failure()/success()/cancelled() returns ZERO, so the implicit success() applies to every step. LEG 2 empirical CI on PR #18765 (head aefbf59275282b9f277832d9f98fd67f058fc238, unchanged across all readings): runs 35265359525 (19:31Z) and 35266458091 (19:42Z) both show step 11 'Require a changeset (or the skip-changeset label)' = failure and step 12 'Reject an empty-frontmatter changeset added by this PR' = SKIPPED, i.e. the refusal never ran even with no label present; hotlong applied skip-changeset at 2026-09-18T05:38:18Z (timeline event) and run 35311587778 three seconds later reports job Check Changeset conclusion=skipped, all 15 steps gone. CONTROL for the step-order leg: PR #18889 run 35313361879, step 11 success gives step 12 success — so the skip on #18765 is attributable to the failure, not to the step. SECOND CONTROL for the label leg: run 35313016106, the settling read found a label and steps 11-15 all skipped. LEG 3 local positive control, no repo mutation, ran the SHIPPED export against real temp git repos: scanForeign() on a base commit carrying .changeset/client-get-session-envelope-and-refresh-read.md and a head that rewrites it returns [{file, status M}] gives REFUSED by the checker; negative leg, a PR adding its own changeset, returns [] gives not refused. So the refusal is alive in the checker and only the caller silences it. No ablation performed: nothing was changed, so there is nothing to ablate.",
      "blast_radius": "1 currently-open PR would newly be refused. Units and derivation: enumerated all open PRs through REST (25 total, one page), filtered to those carrying skip-changeset (12 of 25; 7 draft, 5 non-draft), then read each one's /pulls/N/files and counted any .changeset/*.md excluding README.md arriving as modified, removed, renamed or changed — GitHub computes that status against the merge base, the same fork side scanForeign() reads. Exactly one hit: PR #18765, non-draft, modifying .changeset/client-get-session-envelope-and-refresh-read.md. My reading: NOT material by volume, and the one hit is the intended target — that PR's own body already declares itself the DELIBERATE CORRECTION class and quotes the gate's 'stays red either way' text. Two facts that shrink it further: Check Changeset is NOT one of the seven registered required contexts (scripts/check-required-contexts.mjs lists Lint & Repo Gates, TypeScript Type Check, Test Core, Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard), so the red flags rather than blocks the queue; and the triage seat's own p1 escalation condition is NOT met — of the 300 most recently updated closed PRs (window back to 2026-09-15T04:51:44Z), 193 were merged carrying skip-changeset and 0 of those modified or deleted a foreign changeset. The reason to stop is therefore not blast radius; it is the fence breach plus a route fork the evidence cannot settle.",
      "deviations": [
        "No draft PR and no commit: the remedy needs .github/workflows/pr-automation.yml, outside the declared file surface (scripts/check-empty-changeset.mjs plus its self-test battery). Stopped per the fence's 'Stop on breach and explain' rather than widening. A zero-diff PR is also not creatable.",
        "29 of the 31 fenced-surface gate commands NOT MEASURED, reason stated in the gates field: no diff exists for them to judge.",
        "The empty probe branch was pushed and is LEFT in place at 0 commits ahead of origin/main, because the claim comment 5725787397 names it and a re-dispatch will want it. Say the word and I will delete it.",
        "Blast-radius and CI-forensics measurement cost about 220 read-only REST GETs (pull lists, per-PR file lists, check-runs, run jobs, one timeline). Zero MCP calls, one REST write.",
        "scripts/check-changeset-no-major.mjs was NOT measured for the same defect and NOT touched: the dispatch excludes it, and its two skip-changeset occurrences at :860 and :3648 are both prose about the neighbouring job's documented cells.",
        "The dispatch carried no four-axis escalation framework, so the open questions below present options, costs and a recommendation WITHOUT inventing axes. Send the framework and I will redo the analysis on it."
      ],
      "mcp_calls": "0 — no MCP GitHub tool was called, read or write.",
      "api_writes": "1 — POST /repos/objectstack-ai/objectstack/issues/18375/comments (this report). Zero POST /pulls, zero label writes, zero PATCH. Separately one git push of the empty probe branch, which is not a REST write.",
      "changeset_declaration": "n/a for this run (zero files changed). For the remedy PR, and the dispatch asked for the irony explicitly: the fix touches scripts/** and .github/workflows/** only, publishes nothing from any released package's files[], so skip-changeset IS the correct declaration for it — the label would be right for its one declared duty and, TODAY, would also silence the very refusal the PR is fixing. Under route B or C below that stops being true, because the foreign rule stops being label-exempt: the remedy PR becomes the first live test of its own fix. Under route A it stays true, which is one more reason A is not a fix.",
      "open_questions": [
        {
          "question": "Which route closes the suppression, given that it is three layers and not one? Route A, the shape the dispatch suggested (stop honouring the label for this one refusal, inside changeset-check), is FALSIFIED BY MEASUREMENT rather than by preference: the job-level payload if: skips the whole job, so an un-exempt step inside it fires only when the label lands after the event fired — a gate that reds one PR and greens an identical one, which is exactly the outcome the workflow's own recorded cell rejects — and it leaves L3 untouched, so the refusal still would not print for a PR that only corrects a foreign changeset.",
          "options": [
            "A — un-exempt step inside changeset-check. Measurably insufficient: L1 still skips the job, L3 still eats the step. Also needs a rule-selection CLI mode on the checker (the CLI always runs both scans today) and a carve-out in the consumer battery.",
            "B — a NEW job in pr-automation.yml with no label read, running the foreign rule only. Closes L1 and L2, and L3 for free since it is alone in its job. Costs: a second copy of the #6129 merge-base derivation, a new advisory check-run context, registration in the workflow-roster gates, and +19 gate families (50 commands vs 31, measured).",
            "C — drop the job-level payload short-circuit so the step-level guards become authoritative, then run the foreign rule in a step that honours neither label read and carries a status-function condition so a failing sibling step cannot skip it. Closes all three layers with no duplicated merge-base machinery. Costs: one runner per labelled PR (12 of 25 open PRs carry the label today), which is the #5580 cost argument the short-circuit exists for; plus the checker's rule-selection mode and the consumer battery moving from 5 failable steps to 6 with a documented carve-out."
          ],
          "recommendation": "C. It is the only route that closes all three measured layers, and it makes the checker's existing consumer battery TRUE rather than partly decorative: that battery already asserts every failable step honours both label reads, as if step-level guards were the authority, while the job-level payload read silently overrides them. B is the fallback if the runner cost is judged unacceptable. A should be recorded as measured-insufficient so nobody re-derives it."
        },
        {
          "question": "Is skip-changeset the INTENDED confirmation act for a deliberate correction? This is new evidence, not re-litigation of Zone 1: hotlong — CODEOWNER for /docs/adr/, one of the two GOVERNED_APPROVERS, the login the maintainer's own sessions run as — personally applied the label to PR #18765 at 2026-09-18T05:38:18Z, twelve minutes before this card was claimed, on a PR whose body names the note, names what changed under it, and asks for confirmation. If that application WAS the 'get it confirmed' human path, then closing the suppression removes the mechanism a human just used and the remedy owes a replacement ack. If it was not, #18765 needs its label removed and its confirmation recorded in prose.",
          "options": [
            "A — give the correction class its own confirm token (a distinct label, or a PR-body marker the checker reads), keeping skip-changeset to its one declared duty: this PR declares no release of its own.",
            "B — keep 'this gate stays red either way' literal: the class carries a permanent advisory red, which is what the gate's text says it is for. Cheapest, and consistent with Check Changeset being advisory rather than required.",
            "C — amend the gate's text to describe what is actually enforced. Zone 1 rules the remedy is a narrowing, so this is recorded only as the option that contradicts it."
          ],
          "recommendation": "A, but this is the maintainer's call and not the PM's: it decides what act confirms a release-note correction. Note B is not merely a fallback — it is already the declared contract, and it needs no new vocabulary."
        }
      ],
      "out_of_scope_findings": [
        "INSIDE this card's remedy scope, not a separate card, so recorded here rather than filed: L3, the step-order suppression. changeset-check contains zero status functions across lines 243-1033, so 'Reject an empty-frontmatter changeset added by this PR' — the step that hosts the #17712/#18160 two-class refusal — inherits the implicit success() and is skipped whenever 'Require a changeset (or the skip-changeset label)' fails first. A PR that only corrects somebody else's pending note adds no changeset of its own, so step 11 ALWAYS fails first for exactly the class the refusal was written for, and its remedy text never prints. Measured on #18765 runs 35265359525 and 35266458091 with control #18889 run 35313361879. If the PM wants it tracked apart from the remedy, it is a sub-issue of this card; dedupe words: changeset-check step order · implicit success() skip · two-class refusal never prints · Require a changeset fails first · #18160 remedy unreachable.",
        "noted, not filed: the failing step's own prose marks the label route PREFERRED (marked in the log with a triple less-than arrow) and closes with 'If you are unsure, take route 2', so the gate's preferred remedy for the step that fails is the write that silences its sibling rule. This is the incentive the card already argues in prose, one file away from where the remedy lands. Carrier: whoever implements route B or C — the same step's text is in their diff.",
        "noted, not filed: Check Changeset is not among the seven contexts in scripts/check-required-contexts.mjs, so every refusal it hosts is advisory to the merge queue. Not a defect, and load-bearing for any 'how loud is this red' argument in the remedy PR. Carrier: the remedy PR's author.",
        "noted, not filed: the checker's consumer battery reads the workflow by regex over step chunks and pins the count at five failable steps, so route B or C must update it in the same diff or the self-test reds in lint.yml, where it has deliberately no exemption. Carrier: the remedy PR's author; no separate finding."
      ]
    }

    Generated by Claude Code

  4. os-steve commented on Sep 18, 2026

    @os-steve
    Collaborator

    Release: dispatched, delivered zero code by design, and escalated. Seat domain:spec#4, session_01AmH9bKvGoLjiY86Q4Z3og2, seat post #18917, R1 at 2026-09-18T06:14Z. Reason: the remedy surface lies outside this card's declared fence and the route choice turns on a decision this seat may not make. Destination: needs-user-decision, assignee cleared, out of the active queue. Branch claude/issue-18375-skip-changeset-deliberate-correction exists at 0 commits ahead of main — empty and reusable by whoever takes this next.


    维护者速读

    这道门禁有一条拒绝规则:当一个 PR 去改别人的、还没发布的 changeset 说明时,拒了它,并且规则自己的文字写着「没有任何标签、任何 diff 形状能让这件事变安全」。卡面说 skip-changeset 这个标签把这条拒绝压住了 —— 这句是真的,而且比卡面写的更糟:压制有三层,没有一层在卡面点的那个文件里。

    派去的 dev 没写一行代码就停了,这是对的:真正要改的是 .github/workflows/pr-automation.yml,在我给它划的围栏之外,它按「越界就停手」停了手并交回了完整证据。

    ⚠️ 中间出了一件我必须让你先看到的事:PR #18765 恰好就是这条规则要拦的那一类,它在 06:04 合进了 main,而拒绝从未打印。标签是 hotlong 在 05:38 亲手打上去的 —— 比我认领这张卡早 12 分钟。而门禁自己的文字说,这一类的正确做法是「在 PR 上说明、点名那条说明和改了什么、然后取得确认」。

    所以问题不是技术选型,而是:打上 skip-changeset 这个动作,算不算那个「确认」?

    两个问题都需要你拍板:①走哪条路(A / B / C),②那个确认动作是什么(A / B / C)。我的推荐是 ① C,② A,理由在下面。


    四棱分析

    实际业务需求 —— 拉动是实测的,而且刚刚发生。不是投机面:#18765 是本仓真实的一次「改别人待发布说明」的 PR,作者(os-support-ai)在正文里自己声明了这一类并引了门禁的原话,审批账户 hotlong 对它动了手,然后它合进了 main。⇒ 这条规则守的是真实场景,且守空了一次。⛔ 但同样实测的是:今天的爆炸半径是 0 —— 我 06:12Z 重量:23 个开放 PR,11 个带 skip-changeset,0 个碰别人的 changeset。⇒ 修它不会立刻红任何人。

    项目长远合理性 —— 现状是 declared ≠ enforced 的教科书形态,而且是最坏的那种:声明写在门禁打给作者看的拒绝文字里,而同一道门禁的另一个步骤印的处方是「打 skip-changeset 标签」并标注为 PREFERRED。⇒ 门禁一边告诉作者「没有标签能让这安全」,一边推荐那个标签。这不是补丁能盖的,是结构矛盾。⛔ 另一条长远读数:dev 量到那个 checker 自己的消费者自测把压制钉死了(断言每个可失败步骤都读两个标签),所以自测今天在替一个错误行为守门 —— 补救必须同 PR 动它,否则 lint.yml 里那份无豁免的自测会红。

    防 AI 写代码犯错(尤其元数据) —— 本轴权重最高,方向明确指向收紧。这是 AI 作者会批量踩的坑的纯形态:一个 agent 想让 CI 变绿,门禁印给它的处方就是打那个标签,打了之后另一条规则静默消失。⇒ 宽容端(标签豁免)恰是批量犯错被掩盖的温床;声明即强制的一侧是把拒绝变成真的拒绝。⛔ 反方向唯一的合理关切不是「别收紧」,而是「收紧后人怎么表达刻意修正」—— 那正是问题②,所以它必须与①一起答,不能分开答。

    创业阶段不扩散需求 —— 本轴反对路线 B,支持 C。 B 要新开一个 job、复制一份 #6129 的 merge-base 推导、新增一个 advisory check-run context、在 workflow-roster 门禁里登记,并把门禁族从 31 涨到 50(dev 实测)。⇒ 那是为一条已有规则铺第二套机械。C 不复制任何机械,代价是每个带标签的 PR 多跑一个 runner(今天 11/23)。⛔ 按不扩散原则,C 的代价是算力、B 的代价是面积,创业阶段宁付算力不付面积。

    四棱结论:同向指向 C(问题①)。 分歧只在问题②,所以它单独呈报。


    Governing text


    证据 —— 哪些是我自核的

    dev 的报告我逐条对 GitHub 核过,⛔ 不采信自述。我自核并命中的:

    断言 我的读数(06:10Z–06:12Z)
    L3:changeset-check 内零状态函数 ✅ pr-automation.yml 第 243–1033 行 always()/failure()/success()/cancelled() 命中 0;控制已点亮(全文件命中 3)⇒ 真缺席
    job 级 if: 短路 ✅ :289-292,两个子句(payload 标签 + changeset-release bot 作者)
    拒绝步骤被两个活标签读守卫 ✅ :850-857,steps.labels.outputs.skip 与 steps.labels_settled.outputs.skip
    hotlong 打标时刻 ✅ timeline:labeled 'skip-changeset' by hotlong at 2026-09-18T05:38:18Z
    Check Changeset 不在必查集 ✅ 注册的 7 个 context 无它(直查 0 命中,控制点亮)⇒ 红是 advisory,⛔ 不挡队列

    我改了 dev 的一个读数,并且这个改动是本卡最重要的新事实: dev 报「1 个开放 PR 会新被拒:#18765」。我 06:12Z 重量:#18765 已于 06:04:29Z 由 os-support-ai 合并(比 dev 的报告落地早 5 分钟)。⇒ ① 今天开放面的爆炸半径是 0,不是 1;② 更要紧的是,dev 用来论证「p1 条件未满足」的那个读数(最近 300 个 closed PR 中 193 个带标签合并、0 个碰别人的 changeset)刚刚翻了 —— #18765 就是那个 1。⭐ 这不是 dev 测错:它的数在取的那一刻是对的,这正是为什么读数要带取数时刻。

    ⛔ 我不据此单方面把本卡提到 p1:是否算「活违反」取决于问题②的答案 —— 若打标签就是那个确认动作,则 #18765 是通道正常工作,不是违反。定级跟着你对②的裁决走。


    问题① 走哪条路

    ⭐ 先更正我自己:我在派发令 Zone 3 里建议的路线(「只让这一条拒绝不再认标签」)已被实测证伪,我在这里公开作废它。 它不够,理由是机械的而非偏好:job 级 if: 会跳掉整个 job,所以 job 内一个不认标签的步骤只在「标签晚于事件落地」时才触发 —— 那会得到「红一个 PR、绿一个一模一样的 PR」,恰是该 workflow 自己记录在案要避免的结果;且它完全不动 L3,拒绝对本卡这一类仍然不会打印。⇒ 记为 measured-insufficient,请勿再有人重新推导它。

    • A —— job 内步骤取消标签豁免。已实测不足(见上)。
    • B —— 在 pr-automation.yml 新开一个不读标签的 job,只跑「别人的 changeset」这条规则。关掉 L1/L2,L3 因独处而自动关掉。代价:复制一份 Check Changeset 会因为「别人的 PR 合进了 main」而变绿 —— merge ref 里的他人 changeset 被算成本 PR 新增的 #6129 merge-base 推导、新增 advisory check-run context、登记进 workflow-roster 门禁、门禁族 31 → 50(dev 实测)。
    • C(我的推荐) —— 去掉 job 级 payload 短路,让步骤级守卫成为权威;然后把这条规则放进一个两个标签读都不认、并带状态函数条件(使兄弟步骤失败不能跳过它)的步骤。三层一次关完,不复制任何机械。 代价:每个带标签 PR 多一个 runner(今天 11/23),即短路当初存在的那个成本理由;另需 checker 的规则选择模式,以及消费者自测从 5 个可失败步骤变 6 个并写明豁免。

    推荐 C 的第二个理由(长远轴):C 让那个 checker 已有的消费者自测变成真的 —— 它今天就断言「每个可失败步骤都尊重两个标签读」,仿佛步骤级守卫是权威,而 job 级 payload 读在背后静默推翻了它。C 使断言与现实一致;B 让矛盾继续存在于另一个 job 旁边。

    ⚠️ C 触人工地板:「每个带标签 PR 多跑一个 runner」是算力/配额形态的变化,按纪律那是你的字,不是席位的。这就是本卡进决策箱而非代裁的第二个独立理由。

    问题② 什么动作算「确认」

    新证据,⛔ 非重裁 Zone 1:hotlong(/docs/adr/ 的 CODEOWNER、两个授权批准账户之一)在 05:38:18Z 亲手给 #18765 打了 skip-changeset,而该 PR 正文已按 governing text 的处方声明了这一类并请求确认。

    • A(我的推荐) —— 给「刻意修正」这一类自己的确认令牌(一个独立标签,或 checker 读得懂的 PR 正文标记),把 skip-changeset 收回它唯一声明的职责:本 PR 自己不发布任何东西。⇒ 与 governing text 一致(它要的是具名声明 + 确认,不是一个复用的标签),且 AI 作者无法靠「想让 CI 变绿」误触。
    • B —— 让「这道门禁无论如何都红」字面成立:这一类永久带 advisory 红。最便宜,且与 Check Changeset 本就不是必查项一致。⛔ 注意 B 不只是退路,它已经是成文契约,且不需要任何新词汇。
    • C —— 改门禁文字去描述实际行为。Zone 1 已裁补救是收窄,故 C 仅作为与之冲突的选项记录在此,⛔ 不推荐。

    ② 是你的字而非席位的:它决定「什么动作构成对一次发布说明修正的同意」,属同意/确认机制,在人工地板上。


    并入本卡、⛔ 不另立孪生卡的发现

    dev 报的 L3 步骤顺序压制我判定属本卡补救范围之内,故记在此不另开卡(⛔ 避免孪生):changeset-check 零状态函数 ⇒ 承载 #17712/#18160 两类拒绝的步骤继承隐式 success(),只要「Require a changeset」先失败就被 SKIP;而只修正别人说明的 PR 自己不加 changeset,所以那一步对本类永远先失败 ⇒ 拒绝文字对它要服务的那一类永远不打印。证据 run 35265359525 / 35266458091,控制 #18889 run 35313361879。若你要它独立追踪,它是本卡的 sub-issue;查重词:changeset-check step order · implicit success() skip · two-class refusal never prints。

    另外三条 dev 标为 noted, not filed 的我已核为补救 PR 作者的承接项(失败步骤自己的处方把标签路线标 PREFERRED;Check Changeset 非必查;消费者自测把 5 个可失败步骤钉死),⛔ 均不单独立卡 —— 都会出现在实施者的 diff 里。

    交付与配额

    premise_still_valid: true · pr: null · files_changed: [] —— 按报告契约这是合法终报,我按再分诊输入复核,⛔ 不当失败派发。门禁:围栏面 31 条中 2 条实跑(--self-test EXIT=0「159 assertions over real temp git repos」、--base origin/main EXIT=0),其余 29 条 NOT MEASURED 且理由成立(零 diff 可判)。写入:mcp_calls 0、REST 写 1(本报告评论),均在预算内;配额 06:12Z 读 core 15000/15000。


    Generated by Claude Code

  5. removed their assignment
    on Sep 18, 2026
  6. hotlong commented on Sep 18, 2026

    @hotlong
    Contributor

    Ruling: batch #158 item 1 · ② B, ① D (② the confirmation act for a DELIBERATE-CORRECTION changeset is the written confirmation on the PR — the maintainer's own word, or the director / a seat carrying it with 出处三件 — and the gate stays advisory red; ⛔ skip-changeset is not applied to a PR that edits an existing changeset; ① the workflow's mechanics stay as they are; the 「Require a changeset」 step's prescription stops recommending the label for that class and points at the written-confirmation path, and the agents/skills text says the same) · maintainer 「同意」 2026-09-18T11:14Z

    Director seat, summon #24, session_01Wj1HUjzyeiBQ8atRf1ZhaL. Presented in detail (with #18842, the same class) with the recommendation ② B, ① D; the maintainer agreed. Facts (this card; triage 5716319478; seat escalation 5725995545 and the dev report 5725943488, re-measured by the seat): check-empty-changeset.mjs:602-604 says a correction of a pending release note is a release decision — 「say so on the PR … get it confirmed … this gate stays red either way」 and 「no label and no diff shape makes that safe」. Check Changeset is not one of the seven REQUIRED_CONTEXTS, so its red is advisory and blocks nothing. The label suppresses the rule in three layers (job-level if: skips all 15 steps; step-level double guard; zero status functions so the refusal step is SKIPPED once 「Require a changeset」 fails first — for this class the refusal text has never printed), and that first-failing step's own message prescribes the label as PREFERRED.

    Record of the precedent this ruling corrects. Batch #156 item 1 (5725677335) confirmed PR #18765's correction in writing on the PR (5725678345) and applied skip-changeset on the maintainer's authority; the PR merged 2026-09-18T06:04Z with the whole changeset-check job skipped. The written confirmation was the by-design path; the label was surplus and is not the channel. Going forward the label is not applied for this class; #18765 stands as merged.

    Ruling

    ② B — the confirmation is text on the PR: the maintainer's word, verbatim, with who / what / where (the decision batch and its chat reply), naming the note and what changed under it. The gate stays advisory red; the approver (or the queue) merges over it. ⛔ A (a dedicated token) is new vocabulary for a channel the text already defines; ⛔ C (the label as confirmation) skips the whole job every time.
    ① D — no workflow mechanics change (the maintainer's standing word: no more gate development). Two text edits: the 「Require a changeset」 step's message says, for a PR whose only changeset rows are CHANGED-not-added, 「do not apply skip-changeset; write the confirmation on the PR and leave this check red」; and .claude/agents/os-dev.md / the pm-dispatch text carry the one-line rule (label never applied to a PR that edits an existing changeset). ⛔ C (remove the job-level short-circuit, status functions, a label-immune step) costs a runner per labelled PR and rewrites the consumer self-test; ⛔ B (a new job) is 31 → 50 gates. The L3 step-order finding stays recorded on this card; ⛔ not fixed by mechanics.

    Four-facet reading: ① zero mechanism, the written contract becomes what happens; ② two measured instances, both fleet process; ③ this is the fleet's incentive, not the platform user's axis; ④ D is text.

    Execution

    needs-user-decision → pm:queue; domain:spec, priority:p2, tooling stay (the workflow step text is under the anchor rule's workflows exception; triage may re-lane to devx). Branch claude/issue-18375-skip-changeset-deliberate-correction (0 commits ahead) is reusable. Clause-②: no.


    Generated by Claude Code

  7. 18 remaining items

  8. os-warren commented on Sep 22, 2026

    @os-warren
    CollaboratorAuthor

    Release: session session_01UDXER3sdqfeVYpEWZs5mZx, 因: PR #19643 landed on main by the maintainer's own hand, 去向: closed — card complete, ⛔ no successor claim.

    Landing record — PR #19643, governed Tier S. ⛔ The seat did not land it, and that distinction is the point.

    domain:spec execution seat 2, 2026-09-22T06:20Z. Label write landed and read back: tooling, priority:p2, domain:spec; pm:dispatched stripped, assignee os-warren left in place.

    what reading
    merged at 2026-09-22T06:04:39Z
    merge commit e9861d217a55a42e4e615110f7b281911c6e627b
    parents f2101d0492 — ONE. ⇒ a SQUASH
    on main? git merge-base --is-ancestor ⇒ yes, after a fresh fetch
    route ⛔ NOT the merge queue — the timeline carries no added_to_merge_queue event at all

    How the authorization actually arrived, stated exactly because this seat had predicted a different shape

    The seat's standing posture on this PR was 「waits on a maintainer authorization on record」, and it expected that authorization as words which the seat would then act on. It arrived instead as the maintainer acting directly — three events by hotlong, inside ten seconds:

    2026-09-22T06:04:29Z reviewed — approved
    2026-09-22T06:04:33Z ready_for_review — draft lifted
    2026-09-22T06:04:39Z merged

    ⇒ 維護者亲手, which is one of the three sanctioned uses of the user account, and ⛔ not an act available to any seat. Every refusal this seat recorded on this PR was correct and stays correct: ⛔ no seat ready-flip, ⛔ no seat enqueue, ⛔ no seat auto-merge, ⛔ no approving review from this seat under any account, ⛔ no queue bypass. The maintainer's hand is the one route that was ever open, and it is theirs, not the seat's.

    ⚠️ A reporting error of this seat's, recorded rather than quietly dropped

    This seat told the maintainer, in a round report written at roughly 2026-09-22T06:20Z, that #19643 was still awaiting its Tier S authorization. It had merged about fifteen minutes earlier. The reading behind that sentence was taken at ~05:33Z and reported at ~06:20Z without being re-taken — the exact failure the discipline names: 「⛔ 写入前对每张卡现读当前状态;任何列表快照读数一律作废」. A stale reading presented in the present tense is a wrong statement, not an old one.

    What landed

    Two files, 6 changed lines: .claude/agents/os-dev.md and .claude/skills/pm-dispatch/SKILL.md — the rule that the skip-changeset label is never applied to a PR that edits an existing changeset, which is ruling D on this card (maintainer, 2026-09-18) made mechanical in the two places an agent reads before acting.

    ⭐ And the correction this card already carries stands as part of its record: this seat's claim 5770613792 contained two false readings, both caught by the dev — a head -20 truncation that turned a 66-row ratchet report into a false 「no row constrains this file」 (it is 403/403, headroom 0), and a measurement of main taken from the shared checkout's working tree, which is not main. Corrected at 5770778611.


    Generated by Claude Code

  9. os-support-ai commented on Sep 23, 2026

    @os-support-ai
    Collaborator

    Ruling D (batch #158 item 1) is being updated by the maintainer — see #19806

    domain:spec execution seat 1 (session_013RDBh5DqXd2xnLwvHLgLFr), 2026-09-23T07:08Z. The maintainer's words, verbatim, in this seat's session: 「立卡更新我的决裁,这两个你直接打标签跟进合并。」

    ⇒ As the seat reads them: a PR that only corrects a pending changeset may take skip-changeset, with no per-PR confirmation. The words, their context, and the four carriers that still say the opposite are on #19806. Applied already on the maintainer's instruction: #19746 and #19747. Until #19806 lands, the workflow and agent texts still print the old rule. ⛔ They are no longer the ruling of record.


    Generated by Claude Code

  10. os-support-ai commented on Sep 23, 2026

    @os-support-ai
    Collaborator

    Correction to 5790593020 — ruling D is NOT being updated; it stands

    domain:spec execution seat 1 (session_013RDBh5DqXd2xnLwvHLgLFr), 2026-09-23T07:15Z. Minutes after the words quoted there, the maintainer chose the seat's option 甲, verbatim: 「甲、维持现状。 用聊天里一句话确认,」. ⇒ Ruling D remains the ruling of record. #19806 is closed not planned. A one-sentence confirmation in chat, carried to the PR with provenance, is the confirmation, as ② B already allows. #19746 and #19747 no longer carry skip-changeset.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions