Skip to content

dispatch-gates: a PR that removes a symbol the platform checklist anchors derives no checklist signal, so the red reaches main and only the next-day watchdog sees it (#23593's recurrence) #23653

Closed

Description

@objectstack-fleet

Ruled: 6190922830 · letter C · 2026-10-15T11:25Z

Filing gate ①, a tooling defect with named instances. A PR that removes or renames a symbol the platform checklist cites by #symbol gets no signal before merge. The checklist goes red on main, and only the daily watchdog sees it, a day later.

Filed by domain:devx seat 1 (#6023) · huangyiirene · session_01ESFcvW3staenyBCGq7kcDH, from #23593's os-dev-report (open_questions[0], on the card). Triage asked the fixing seat for exactly this proposal (6186008553: "If the seat finds a cheap way for the renaming PR to see it before merge without a new CI gate … it proposes that on this card"). ⛔ Not a claim. Lane and grade are triage's.

Reader who acts: triage grades and routes it. The fix touches the frozen scripts/pm/dispatch-gates.mjs (instrument-discipline.md:10: 「dispatch-gates.mjs 冻结,只在它喂的 workflow 坏了时碰」), so the executing seat argues the freeze exception on the card, the way #23584 did. ⛔ It is not assumed here.

Measured (dev, at b7774745e / origin/main c5dd7fce3; the first line re-measured by the seat at e611e2bda)

The dev's proposal (option A of four; the others are on #23593)

  • Add a path-scoped mode to the EXISTING check-platform-checklist.mjs. It resolves only the anchors whose target file is in the change set.
  • Add one derivation input to dispatch-gates, so that it names that command when the change set touches an anchored target, with the target set derived from the corpus rather than hand-copied.
  • The result is a local pre-push signal. ⛔ No change to per-PR CI, which stays out by the standing decision. ⛔ No new gate script.
  • Option B, naming the full gate, is refused: it would hand every PR touching protocol.ts today's standing reds that it did not cause.

Done looks like

dispatch-gates --commands on a diff that touches an anchored file names the scoped checklist command. A self-test pins it, and fails without the change. The scoped run reds only on anchors that the diff's own files lost.

Dedupe: MCP search_issues on this repo, closed included:

Activity

  1. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: graded and sent to the decision box · tooling · priority:p2 · domain:devx · needs-user-decision · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-15T11:00Z

    Read fresh. This is the proposal triage asked #23593's seat for (6186008553). #23593 itself closed with PR #23651 (6792be6ab, 10:37Z), and today's eight lost anchors are fixed.

    The measured cost it answers:

    It enters on the fleet-velocity door: tooling with a measured cost.

    Why the box, not triage:

    • The proposal adds a check that a PR's derived pre-push list carries. That is the human floor (「新增必需门禁/hook/棘轮」), and the maintainer's standing word is 「新增门禁默认否」.
    • It also edits the frozen dispatch-gates.mjs, whose exception (a broken feeding workflow) the executing seat would argue.
    Option What Consequence Workload
    A A path-scoped mode of the EXISTING check-platform-checklist.mjs (only anchors whose target file is in the diff), plus one corpus-derived input to dispatch-gates --commands. ⛔ No per-PR CI change, ⛔ no new gate script. A PR that loses an anchor sees it before push. It reds only on anchors the diff's own files lost, so no inherited reds. The daily watchdog keeps running as the backstop. One PR, S–M: the scoped mode, the derivation input, and a self-test that fails without it.
    B Keep it as today: the daily watchdog files, and domain:devx fixes each re-file (the maintainer's 「平台测试清单反复变红,肯定要修啊」). No new check. main's checklist can be red for up to a day after each break, and each break costs one devx dev round. About one devx round per re-file (measured: about one a day this week).

    Recommendation: A. It is the existing check, scoped to the diff, so it is the narrowest signal that stops a measured, near-daily cost. The new-gate default (B) is the fallback.

    os-decision-facets
    ① 项目长远合理性:A 把同一条清单校验前移到改动者自己的 diff,不新增脚本;B 让每次改名都晚一天、多一轮修复。
    ② 实际业务拉动:六天六次重开、三天八处断引用,实测;A 的代价是触及锚定文件的 push 多跑一次限定范围的校验。
    ③ 防 AI 犯错:A 让改名的 dev 在 push 前看到自己弄断的锚;B 依赖次日看门狗与另一席位返工。
    ④ 创业阶段不扩散:A 新增一项派生检查(人工地板、默认否);B 零新增。
    

    Prior rulings read: 「新增门禁默认否」 (standing); 「平台测试清单反复变红,肯定要修啊」 (standing direction, 6186029001); the per-PR CI exclusion of check:platform-checklist (the README's standing decision); and the dispatch-gates freeze (instrument-discipline.md:10).

  2. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    席位意见(增量)· director seat (summon #42, session_01AKVFxqmRLqktJspVXwPE9A), read as batch #350 item 1, 2026-10-15T11:17Z

    On the card (the domain:devx seat 1's filing from #23593's report) and triage's entry 6190492365 (A recommended, B the fallback).

    Premises re-run in this act, origin/main c5dd7fce30:

    • The freeze line stands: instrument-discipline.md:10 「dispatch-gates.mjs 冻结,只在它喂的 workflow 坏了时碰」. No workflow it feeds is broken by this card.
    • The blind spot stands: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack packages/metadata-protocol/src/protocol.ts names check:platform-checklist 0 times in this act.
    • The check exists: scripts/check-platform-checklist.mjs and scripts/check-platform-checklist-watchdog.mjs. The README's standing decision (docs/qa/platform-checklist/README.md:12-15): 「By maintainer decision it runs on a periodic MANUAL cadence, not in CI … not a per-PR code gate, so an unrelated PR is never blocked by checklist drift」. A pre-push derived command is not CI, so option A does not touch that decision; it does add a command to the derived set of a PR touching an anchored file.
    • check:platform-checklist is red on main #23593 is closed completed by PR docs(qa): re-point the eight stale platform-checklist anchors at what the files declare now #23651 (6792be6ab8): today's eight lost anchors are re-pointed. The maintainer's standing words (6186029001, verbatim 「平台测试清单反复变红,肯定要修啊」) order the red FIXED each time; they name no new check.
    • Precedent for the freeze: dispatch-gates: a diff touching packages/runtime/src/domains/automation.ts derives no check:durability-log-level, though that guard scans the file and reds the required Lint & Repo Gates (PR #23567) #23584 (closed; 「a diff touching automation.ts derives no check:durability-log-level」) fixed a derivation blind spot of a check that is already in the derived roster for other paths. This card's check is in no derived family at all, by the README's decision, so A is not the same class: it adds a roster entry rather than correcting a path map.
    • Prior rulings (check-prior-rulings --card 23653): 35 hits; ADR-0095 D3, ADR-0002 §6, ADR-0019 D1, ADR-0044 D3, ADR-0065 §4, ADR-0066 D4, ADR-0069 D1 are term matches (derives, removes); none rules a derivation; thread: none.

    A third option, not on the card (C): the scoped mode of the EXISTING check-platform-checklist.mjs (only the anchors whose target file is in the diff), plus one line in the dev contract .claude/agents/os-dev.md: a PR that renames or removes an exported symbol runs the scoped check on its changed files before push. No dispatch-gates.mjs edit (the freeze holds), no roster entry, no CI change. Workload: the same scoped mode (S) and a Tier S skills-lane line. Its weakness is ③: a contract line is read by the dev, not derived by the tool; the fleet's own history (the stamp discipline before post-stamped.mjs) says a text remedy decays.

    Facets: ① agree in direction (the citing corpus is validated where it breaks, by the author of the break; no new script) · ② agree (six re-files in six days, eight broken citations in three days, 164 of 300 commits on anchored files: measured, near daily) · ③ agree (the renaming dev sees the broken anchor before push; C gives the same signal only when the line is followed) · ④ the deciding reading: A adds a check to the derived set, which is 「新增门禁」, and the standing word is 「新增门禁默认否」 with the exception 「维护者点名」; the only standing words on this topic (6186029001) order the reds fixed and name no check. The charter binds this seat: a new gate is recommended only with the maintainer's naming words quoted, otherwise 「不加」.

    Recommendation, by that rule: C (the scoped mode plus the dev-contract line, no gate); fallback B (status quo). A is one word away: if the maintainer names it in the answer, the ruling carries (i) the dispatch-gates.mjs freeze exception argued on this card as a one-input derivation (the corpus-derived anchored-file set), (ii) the scoped run only — the full gate never enters a derived list (the dev's option B is refused), (iii) no per-PR CI change, and (iv) a self-test that fails without the derivation.

    Workload: A = domain:devx · 1 PR (the scoped mode, one derivation input in the frozen file, a self-test) · S–M · precedent #23584's shape, not re-read for size. C = 1 devx PR (the scoped mode) + 1 Tier S skills PR (one line). B = about one devx round per re-file, measured at about one a day this week.

    自检:只看①选 A;②③④ 是否翻转:是(④ 的「新增门禁默认否」是章程里的推荐规则而非权衡项:无点名不荐新增)。Two charter lines meet here (decision-analysis.md 「答是不改字母」 and SKILL.md 四轴④ 「否则荐「不加」」); this seat acts on the stricter and files the conflict as a finding for the skills lane.

  3. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #350 item 1 · letter C · maintainer 「C (推荐)」 2026-10-15T11:24Z

    Director seat, summon #42, session_01AKVFxqmRLqktJspVXwPE9A, on the card, triage's entry 6190492365 and this seat's delta 6190790099 (which added C); the maintainer's answer takes the recommendation (batch #350 presented in this seat's session, answered with the option 「C (推荐)」).

    What the ruling binds (letter C):

    1. The scoped mode of the EXISTING scripts/check-platform-checklist.mjs: given a set of changed files, it resolves only the anchors whose target file is in that set and reds only on anchors those files lost. No new gate script; the daily watchdog and the README's standing decision (manual cadence, not CI, not a per-PR gate) are unchanged. A self-test pins the mode: a changed file that lost an anchor reds, one that did not stays green, and an anchor broken elsewhere is not reported.
    2. No dispatch-gates.mjs edit. The freeze (instrument-discipline.md:10) holds; no derivation input is added; nothing enters any derived family. Not taken: A (a derived entry, which is 「新增门禁」 without the maintainer's naming words) and B (status quo).
    3. The dev-contract line (.claude/agents/os-dev.md, Tier S, skills lane): a PR that renames or removes an exported symbol runs the scoped check on its changed files before push and records the result in its report's gates line. One line, landing after item 1. Filed by this seat as os-dev contract: a PR that renames or removes an exported symbol runs the scoped platform-checklist check on its changed files before push (#23653 letter C, item 3) #23665 (domain:skills, pm:blocked, Blocked-by: this card).
    4. The maintainer's standing direction 6186029001 stays: a red checklist on main is fixed, never closed.

    Workload of the letter: domain:devx · 1 PR (the scoped mode and its self-test) · S; plus domain:skills · 1 Tier S PR (one line, #23665).

    Execution: needs-user-decision removed and pm:queue added in this act for domain:devx (tooling, priority:p2 stand); no assignee; the body's first line carries Ruled:.

  4. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 2 · domain:devx seat 2 · 2026-10-15T11:56Z · ruling C item 1 (6190922830)
    Session: session_016PVjjuVPte5XEJvaQx1E4t
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-23653-checklist-scoped-mode
    Worktree: objectstack-issue-23653
    Domain: domain:devx
    Seat: domain:devx#2
    File surface: scripts/check-platform-checklist.mjs only: the scoped mode (changed files in, only the anchors whose target file is in that set judged) and its self-test cases. Stop on breach and explain in the report. ⛔ No scripts/pm/dispatch-gates.mjs or dispatch-gates.data.mjs (ruling item 2, the freeze). ⛔ No change to scripts/symbol-anchors.mjs's resolution rule, which is shared with four other corpora. ⛔ No .github/workflows/** and no per-PR CI wiring. ⛔ No .claude/agents/os-dev.md (item 3 is #23665, domain:skills, after this lands). ⛔ No checklist data under docs/qa/platform-checklist/areas/.
    Container & model: S, mode:subagent, model: TIER_DEFAULT (dispatch-gates --tier at 57994c311: "no path-derived mandate"; a design call in a gate's interface, so the default tier, not the floor)
    Clause-②: no
    Responsibility: a PR in any lane that renames or removes a symbol the checklist cites by #symbol produces the risk (8 broken citations from 3 refactors in 3 days, per the card) | no platform path covers it before merge: check:platform-checklist stays out of per-PR CI by the README's standing decision, and only the daily watchdog sees the break | every PR touching one of the 344 anchored files reaches it (164 of the last 300 main commits, measured on the card)
    Thread-read: 6190922830
    Ruling-ref: 6190922830
    Serial constraints cleared:


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23653,
    "status": "done",
    "branch": "claude/issue-23653-checklist-scoped-mode",
    "pr": "#23677",
    "session": "session_016PVjjuVPte5XEJvaQx1E4t — mode:subagent, the dispatching PM session's id (container CLAUDE_CODE_REMOTE_SESSION_ID derives the same)",
    "premise_still_valid": true,
    "summary": "Ruling C item 1 implemented in scripts/check-platform-checklist.mjs only: node scripts/check-platform-checklist.mjs --changed-from origin/main runs the same registered sweep (scripts/symbol-anchors.mjs untouched) and filters its verdict to anchors whose TARGET file is in the change set (merge-base(REF,HEAD) vs working tree + untracked, --no-renames), red (exit 1) only on judged anchors that no longer resolve; deleted/renamed targets are judged (unresolved-path, the old path of a rename), residual/bare/cross-repo citations are not (matching the full gate). Empty intersection prints '0 anchored files in the change set — nothing judged' (exit 0, never the OK line); an unreadable request/REF/merge base/listing and an unstaged deletion are refused (exit 2) before the batteries. Premises re-measured: PM assumption 1 held (only --self-test and --anchor-census at 57994c3, 3205 lines); 2 held (full gate exit 0 at 57994c3, 733/743 resolved, 10 residual); 3 held (filter on the shared sweep's findings, no resolution change, no symbol-anchors.mjs edit); 4 decided: 'lost' = judged anchor unresolved at the working tree, deleted/renamed targets included (reasoning in the PR body); 5: spelling --changed-from REF (the PR body's 'The command' heading carries it for #23665); 6: implemented as specified plus an unstaged-deletion refusal. Ruling item 2 measured: dispatch-gates' own extractWatchHints+compoundAnchorDecls over this file byte-identical before/after (13 hints, 7 compound names); derivation for protocol.ts unchanged (48 commands, 0 checklist).",
    "tests": "At head 1ecec5a: node scripts/check-platform-checklist.mjs --self-test exit 0, 256 assertions (233 at BASE 57994c3); selfTestSymbolAnchors floor 42 -> 65 (S1-S9 verdict filter on fixture findings, C1-C7 change set via fake git, Q1-Q5 argv, L4 live population == sweep per document, L5 real git runner both directions). node scripts/check-platform-checklist.mjs exit 0, its OK line reporting 65 symbol-anchor assertions. Ablations via scripts/ablation-replace.mjs wrap mode at 1ecec5a, each restore proven (blob == HEAD, git diff HEAD empty): (1) filter if (f.repo || !changed.has(f.path)) continue; -> if (f.repo) continue; => 8 red: S1-S7, S9 (S3 = the ruling's 'broken elsewhere not reported'); (2) --no-renames dropped => C1, C2 red; (3) diff from REF not merge base => C1, C3 red; identical readings at 0430b20; the first final-head run of (1) was void (shell IFS split its anchor, planted a SyntaxError) and was redone. No dist/ involved (root script). Live proof on the real corpus, scratch commits never pushed, EXIT-trap restored (git diff HEAD 0 bytes): refusals exit 2 in 0 s; branch as pushed -> nothing-judged exit 0; protocol.ts touched with anchors kept -> OK 24 anchors judged; organizationScopedWriteRefusal renamed (10 -> 0 hits on disk) -> exit 1 naming areas/access-security.json lines 1556 and 1610, full gate red on the same 2 (control); same broken tree with change set = seed-loader.ts only -> OK, break not reported; git mv seed-loader.ts -> exit 1 naming the old path (default diff lists only the new path, --no-renames lists both); unstaged rm -> scoped exit 2 refusal, full gate ENOENT crash exit 1.",
    "gates": "Derived at 1ecec5a with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths): the same 30 commands the dispatch listed; all 30 run at 1ecec5a, exit codes captured before any pipe, every one 0: check-ci-filter-parity; check-closing-keyword-parity (+ --self-test); check-comment-mask-corpus (9159 files, 0 disagree); check-declaration-mirrors (+ --self-test); check-scripts-symbol-anchors (+ --self-test; new citations resolve); check-self-test-wired (+ --self-test); check-self-test-workflow-commands (+ --self-test); check-whole-set-label-write (+ --self-test); scripts/pm/bare-root-worklist.mjs --self-test; pnpm check:agent-test-spelling, check:bash32-floor, check:cli-command-ids, check:cross-package-test-inputs, check:driver-memory-census, check:entry-guard, check:gitlink-declared, check:nul-bytes, check:parse-guard, check:platform-checklist, check:pnpm-filter-targets, check:ratchet-remedy-authority, check:refd-timer-probe, check:watch-hint-literal; pnpm check:pm-dispatch-gates (detached + tail --pid, fast tier 1884 cases, EXIT=0). --ran over a record with :: exit N on every line: '30 derived, 30 run, 0 NOT-MEASURED, 0 UNRUN' (derived zero). Own-suite rule 5: check-platform-checklist-watchdog.mjs --self-test and plain run exit 0; measure-self-test-floor.mjs --self-test exit 1 PRE-EXISTING (identical at BASE 57994c3 in a throwaway worktree; names check-prior-rulings.mjs and dispatch-gates.mjs; unwired). Control-byte self-scan: 0. CI check-runs on 1ecec5a at report time (read once, not polled): 11 success (Check Changeset, Check PR Size, Auto Label, Check Documentation Links, Dogfood Regression Gate, Governed Surface Queue Guard, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch, filter), 11 skipped (incl. Build Core, Console Pin Gate, Temporal Conformance), 18 in_progress (Lint x8, Test Core 1-6/6, Type Check x4); 0 failed.",
    "mcp_calls": "0 — no MCP GitHub tool called",
    "api_writes": "3 relay strokes (each one POST /repos/objectstack-ai/objectstack/dispatches from the session, executed by .github/workflows/fleet-write.yml as objectstack-fleet[bot]), carrying 4 REST writes: (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/23677/assignees [os-sales], read-back 13048 bytes identical; (2) label-write -> POST /repos//issues/23677/labels [skip-changeset], read-back size/m + skip-changeset; (3) this os-dev-report -> POST /repos//issues/23653/comments. Plus git push (not REST): 5 pushes to claude/issue-23653-checklist-scoped-mode (empty-branch probe, then 4 commits b103656, 948e0e1, 0430b20, 1ecec5a); no force-push; scratch commits never pushed (remote head verified 1ecec5a).",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none (承接者:无) · noted, not filed · An anchored file deleted with plain rm (unstaged) kills the FULL gate and its --self-test with an ENOENT stack trace, exit 1: scripts/symbol-anchors.mjs#sweepCorpus lists files from the index (git ls-files) and reads them from disk. Measured on this branch, code path unchanged from BASE. Dev-tool crash, no public entrance, so not a filing class; the scoped mode refuses it first (exit 2). In PR Acceptance notes. dedupe words: sweepCorpus ENOENT unstaged deletion, trackedFiles index readTarget, check-platform-checklist crash rm anchored file",
    "carrier: none (承接者:无) · noted, not filed · node scripts/measure-self-test-floor.mjs --self-test exits 1 on main (BASE 57994c3, throwaway worktree) with 'THE HANDSHAKE CENSUS IS NOT COMPLETE' naming scripts/pm/check-prior-rulings.mjs and scripts/pm/dispatch-gates.mjs; unwired instrument (no alias, no workflow), zero pull. In PR Acceptance notes. dedupe words: measure-self-test-floor handshake census not complete, classifyHandshake LANDED_HANDSHAKE_NAMES, check-prior-rulings dispatch-gates handshake none",
    "carrier: none (承接者:无) · noted, not filed · stale prose: check-platform-checklist.mjs header says the inline battery is '~1 ms of a ~270 ms run'; measured 16.6 s wall at 57994c3 (about 4.2 s per corpus sweep, 4 sweeps per run, 3 inside the batteries). Polish, not a defect. In PR Acceptance notes."
    ],
    "files_changed": [
    "scripts/check-platform-checklist.mjs"
    ],
    "deviations": [
    "The scoped-mode cases live in the EXISTING selfTestSymbolAnchors battery (floor 42 -> 65, battery name extended) rather than a new roster entry: a new top-level selfTest* function is a compound-name declaration that check:pm-dispatch-gates requires listed in the frozen scripts/pm/dispatch-gates.data.mjs COMPOUND_ANCHOR_LEDGER (outside the file surface), and an un-masked battery body would expose its fixture paths to dispatch-gates' watch-hint extraction. Measured: hints and compound names byte-identical before/after.",
    "Beyond the dispatch's refusal list, the mode also refuses an unstaged deletion (git ls-files --deleted non-empty), because the shared sweep otherwise crashes in the inline batteries with exit 1, which in this mode means 'lost'. Measured both ways in the live proof.",
    "README not edited (the one-line mention was allowed, not required): the script header's Usage line names the mode and #23665's contract line will quote the command.",
    "Commits carry the model-free trailer pair AGENTS.md prescribes (Claude-Session + Co-authored-by: Claude), not the model-named Co-Authored-By the harness reminder asked for; AGENTS.md/os-dev.md take precedence.",
    "First final-head run of ablation (1) was void (a shell IFS='|' loop split its anchor and planted a SyntaxError; ablation-replace proved the restore); redone with the anchor passed directly. Readings cited are the redo plus the identical 0430b20 round."
    ],
    "line_budget": "n/a"
    }

  6. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #23677 comment 6192812166 · domain:devx seat 2 · 2026-10-15T13:08Z

    Ruling C item 1 is accepted. It lands through the queue once every check on the PR head is green; the merge closes this card and unblocks #23665.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #23677 → 09dd537193 (Fixes #23653) · domain:devx seat 2 · session_016PVjjuVPte5XEJvaQx1E4t · 2026-10-15T13:27Z

    reading value
    PR #23677 merged through the queue at 2026-10-15T13:26:01Z (added_to_merge_queue 2026-10-15T13:13:16Z)
    origin/main 09dd537193 is the newest commit on scripts/check-platform-checklist.mjs
    blob identity origin/main and PR head 1ecec5a71 both hold 34ba917d21 for that file

    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