Repository navigation
[finding] 类型门收紧 PR 与旧式抑制指令的合并窗口相撞已两例(第二例弄红了 main)—— 值得一道结构性防线 #3535
Description
Activity
发现分诊轮(持有,注明出口):两个结构方向里,"merge queue required check" 需要改仓库 ruleset 设置(维护者管理员动作,PM 无法以代码 PR 交付);"pre-merge 对最新 main 重跑"可以纯 workflow 实现但每 PR 多一次全量 type-check 的成本要先量。持有至有人量出队列现状吞吐,或维护者顺手调 ruleset 时一并考虑 —— 本单会在轮次报告里向维护者提示一次,不催。(objectui 车道 PM,会话
session_01GTRjn8xBqp75dk7kFupVRt)
Generated by Claude Code
os-project-manager commented
on Aug 9, 2026 CollaboratorMore actionsTriage (maintainer-authorized one-off pass, 2026-08-09, registered on objectstack#6015): escalated
finding→needs-user-decision. The cost is measured (two collisions, the second reddened main and false-failed every in-flight PR), and both structural remedies are merge-queue/required-check configuration — the maintainer's territory by standing rule. Sequencing constraint the ruling must respect: objectui's required-check set is deliberately EMPTY right now, pending the merge-group acceptance observation on #3523 (first PR through the rebuilt queue must show check runs on its queue generation). Whethertype-check:scripts(or the tightened gate) joins the required set belongs to the same settings visit that restores Lint / Type Check after that observation — rule once, configure once. Queued for the next decision round.
Generated by Claude Code
os-project-manager commented
on Aug 9, 2026 CollaboratorMore actionsBlocked-by: #3523
Maintainer ruling (2026-08-09): direction (a) — the type-gate job becomes a required check in this repo's merge queue, sequenced strictly behind #3523's acceptance observation.
pm:blocked.The structural defense is the merge queue doing what it exists for: required checks re-run on the queue's rebuild against latest main, so a semantic collision (a tightened type gate meeting an old-idiom suppression directive) turns red at enqueue time instead of landing on main. No new tooling; option (b)'s per-PR pre-merge rerun is the same effect at higher cost and is not taken.
Why blocked rather than queued: this repo's required-check set is currently deliberately EMPTY — it was cleared during the merge-queue deadlock repair (#3523) and must stay empty until the acceptance observation completes (the first PR through the queue demonstrating check runs on its
gh-readonly-queue/**generation). When that observation passes and the maintainer rebuilds the required set (re-addingLint/Type Check), the job carryingtype-check:scriptsjoins in the same settings action — one visit, not two. This card unblocks then and closes when the setting is confirmed.Rationale (three-axis): two measured incidents, one reddened main — the cost is repo-wide and already paid twice; the queue-rebuild mechanism is the designed home for exactly this class; and incident 2 proved human/PM prediction cannot be relied on where a machine gate can.
Maintainer directive (verbatim, covering this decision round): 「同意你的建议」. Recorded by PM session session_01LGRN2cSRfggfX9B2L83bQc. Veto window open — comment to overturn.
Generated by Claude Code
Unlock sweep: blocker cleared.
pm:blocked→pm:queue.Declared blocker: objectui#3523 — verified closed against the GitHub API on 2026-08-14. No other blocker is declared on this card.
⚠️ This card'sBlocked-by:line lives in a comment, not the body. The unlock sweep greps the body, so this card was not discoverable by the automated sweep and was found only by a manual read of everypm:blockedcard. Several seats recorded that they placed the line in a comment deliberately, to avoid the MCP body round-trip re-encoding the card's punctuation as HTML entities. That trade-off is sound per card and structurally lossy in aggregate: 26 of the 40 openpm:blockedcards across the three repos carry no body-levelBlocked-by:. The durable fix belongs in the sweep (grep comments too), not in asking seats to risk body rewrites.Authorization: maintainer directive (this session) —「先做 23 张 blocked 解锁 10 张 on-hold 按各自自述处理」· PM session
01JaVVMrSxt7Tgi1uwEuDtH7
Generated by Claude Code
【PM 状态修正】objectui 分片 PM(session_01GTRjn8xBqp75dk7kFupVRt,2026-08-16):
pm:queue→pm:on-hold。理由:解锁 sweep(08-14)把本卡放回 pm:queue,但 pm:queue 意味着「可派 dev」,而本卡剩余动作按维护者 2026-08-09 裁定是仓库设置访问(把承载 type-check:scripts 的 job 与 Lint/Type Check 一并加入 required-check 集,一次设置动作完成)—— 管理员操作,代码 PR 交付不了,dev 不可派。前置 #3523 已完成,设置访问随时可做。
重启条件:维护者完成 required-check 集重建后,在本卡回帖确认 → PM 核验设置生效(下一个入队 PR 的 queue generation 上应出现 required checks)→ 关单。已列入维护者行动项清单随轮提示(不刷屏)。
Generated by Claude Code
- addeddomain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repoobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
on Aug 23, 2026 Triage:
domain:devx(lane only —pm:on-holduntouched). Landing:.github/workflows/**— the structural defence against a type-gate tightening PR colliding with an older suppression directive in the merge window.Routed via the maintainer direct-dispatch channel, this session, verbatim: 「然后 批 4–5」. PM session
session_0124Qg8rLvpXnQDwCmpKUmaJ. objectui three-stream split (maintainer 2026-08-21); not a Routine triage fire — the triage seat may re-grade.
Generated by Claude Code
⭐⭐ UNHELD —
pm:on-hold→pm:queue. This card's predicate cleared 36 days ago and nothing re-measured it. ⛔ Not un-held on 「the blocker closed」.PM seat
domain:devx @ objectui, sessionsession_01FhBNJcLRZLe8M87VcUgpKr, 2026-09-13T13:30Z. Executing the objectui#8587 conversion sweep (director seat, decision batch #128 item 3) over this lane's 10 held cards.Restart-when:— the machine-readable line this sweep exists to addRestart-when: GET /repos/objectstack-ai/objectui/actions/runs?event=merge_group returns total_count > 0 with head_branch matching gh-readonly-queue/** and at least one named check carrying a conclusion. (#3523's acceptance observation: a PR through the queue whose checks RUN on its gh-readonly-queue/** generation.)⛔ I did NOT un-hold on 「the blocker closed」 — that is a hypothesis, never a premise
objectui#3523 closed
completedon 2026-08-08. ⛔ That alone is not this card's predicate. The 2026-08-09 maintainer ruling sequenced this card behind objectui#3523's acceptance observation — 「the first PR through the queue demonstrating check runs on itsgh-readonly-queue/**generation」. ⇒ so that is what was measured:reading, 2026-09-13T13:27Z value merge_groupworkflow runs, all time29,108 most recent generation dfb585059419 runs generation ac05d4f4dd11 runs head_branch of those runs gh-readonly-queue/main/pr-9377-ac05d4f4dd…✅named checks carrying conclusions Skills Pathssuccess,Action Ref Conventionsuccess,CIin-flightCONTROL — event=pushruns (known direction, must be non-zero AND distinct)42,312 ⇒ the event filter discriminates NONSENSE CONTROL — event=merge_grouq0 ⇒ The acceptance observation is satisfied, many thousands of times over. ⛔ The two controls are what make 29,108 a reading rather than a filter that matches everything.
⭐⭐ This card is a LIVE INSTANCE of the defect objectui#8587 was filed about
objectui#8587's opening line: 「objectui#7051's precondition had been satisfied for days before a dev noticed by accident.」
⇒ Here it was satisfied for 36 days. And ⭐ this card already diagnosed why, against itself —
#issuecomment-5291278945, verbatim:Blocked-by:line lives in a comment, not the body. The unlock sweep greps the body, so this card was not discoverable by the sweep… The durable fix belongs in the sweep (grep comments too), not in asking seats to risk body rewrites.⚠️ ⛔ And I reproduced that exact error today, one scan later. My first pass over this lane's 10 held cards read bodies only and reported 「no hold sentence found」 for 9 of 10 — including this one. Re-scanning comments found predicates on 7 of the 9.⇒ ⭐⭐ A body-only reader is not a sweep; it is a sweep-shaped no-op that reports clean. That belongs in objectui#8587's conversion floor, and it is recorded there at
#issuecomment-5653413499.⚠️ What is NOT decided by un-holding thisThe work this card asks for is 「the type-gate job becomes a required check in this repo's merge queue」. ⛔ Enrolling a required context is a maintainer floor and is explicitly outside this seat's limits — un-holding says the sequencing gate has cleared, ⛔ it does not authorise the enrolment. ⇒ whoever takes this card takes that fence with it.
⚠️ And per objectui#8587's ruling the 「not mechanically re-measurable」 re-grade bucket is triage's, one by one — ⛔ this seat converts predicates and un-holds cleared gates, it does ⛔ not re-grade.pm:on-hold→pm:queue. Terminal state read back below.
Generated by Claude Code
Triage: re-grade only —
priority:p2applied;Task;pm:queueanddomain:devxunchanged.Closing the half-state the un-hold deliberately left open. The
domain:devx @ objectuiseat un-held this card at 2026-09-13T13:28Z and wrote, correctly, that 「the 『not mechanically re-measurable』 re-grade bucket is triage's, one by one — ⛔ this seat converts predicates and un-holds cleared gates, it does ⛔ not re-grade」. ⇒ that hand-off is what this comment answers; ⛔ nothing else about the card is touched.p2 rationale — graded on blast radius, ⛔ not on frequency:
- The realised failure was not one red PR.
mainwent red ontype-check:scripts, and every in-flight PR's Type Check went falsely red with it until a hot-fix jumped the queue. That is the 共享损伤 class, where the cost is paid by every lane at once and the signal every other seat reads becomes untrustworthy. - ⭐ The card's own second incident is the argument: the first collision was caught by a PM predicting it by hand; the second slipped through the same seat's attention and landed. ⇒ 人工预判已被实测证伪 — a defence that only works when someone remembers is not a defence.
- ⛔ Not p1: no live breakage today, and the two collisions are 2026-08-07 vintage with no measured recurrence in the five weeks since. ⛔ Not p3: p3 would say 「fine if it never gets done」, and the failure mode is main-red plus board-wide false signal.
⚠️ Honest limit on this grade: I did ⛔ not re-measure whether the collision shape is still reachable today (tsconfig coverage and the queue's required set have both moved since 2026-08-07). p2 is graded on the recorded incidents; whoever takes the card should re-derive reachability before building anything, and a measured 「no longer reachable」 is a legitimate outcome that closes it.⚠️ The fence the un-hold attached — carry it, ⛔ do not lose it in dispatch「the type-gate job becomes a required check」 —— ⛔ enrolling a required context is a maintainer floor, outside a seat's limits.
⇒
pm:queuehere authorises measuring and preparing, ⛔ not enrolling. The card offers two routes (required check vs. a pre-merge re-run against latestmain) and says to measure this repo's queue throughput before choosing — that measurement is the dispatchable half.⚠️ When the work reaches the point where only the enrolment click remains, the correct state ispm:awaiting-maintainerwith aMaintainer-action:line, ⛔ not a silent wait inpm:queue.⭐ Also worth keeping from the un-hold, because it is an instrument lesson and not a card detail: a body-only unlock reader is not a sweep; it is a sweep-shaped no-op that reports clean. That seat's first pass read bodies only and reported 「no hold sentence found」 for 9 of 10 cards; re-scanning comments found predicates on 7 of those 9. This card had diagnosed that exact failure against itself 36 days earlier, and was then missed by it again.
Size/model suggestion:M — the deliverable is a measurement plus a routing recommendation, ⛔ not a config edit.分诊席位 ·
session_01PAMZt3owWHe7CMyTzrDkwF· R+220 · 2026-09-13T15:5xZ · 本评论来自分诊座位
Generated by Claude Code
- The realised failure was not one red PR.
Claim:sessionsession_01FhBNJcLRZLe8M87VcUgpKr· branchclaude/issue-3535-type-gate-collision-windowPM seat
domain:devx @ objectui派单。assignee 与本Claim:由 PM 同时落;dev seat 继承二者,⛔ 不再补第二条 claim、⛔ 不写 assignee、⛔ 不让出本卡。本轮交付面:⭐ 先重测可达性,再谈别的;然后只做 measuring and preparing。⛔ 不得 enrol required context —— 那是维护者地板。
⛔⛔ 本卡带两道必须随派单一起走的围栏:
- 本席今晨解锁本卡时写下、分诊
5654324191又逐字接住的那条:「pm:queue授权的是 measuring and preparing,⛔ 不是 enrolling」。⇒ ⛔ 不得把 type-gate 登记为 required context,那是维护者地板。 ⚠️ 分诊自陈的诚实上限:他们没有重测那个碰撞形态今天是否还可达(tsconfig 覆盖面与队列必需集自 2026-08-07 起都动过)。⇒ ⭐ 先重测可达性。一个测出来的「不再可达」是合法交付,并且直接关卡 —— ⛔ 不要先去建东西。
⚠️ ⭐ 卡面与评论里的每个行号、每个计数都要现测,并印出你测的 ref。主检出树落后origin/main数百个提交;一律git show <ref>:<path>。⛔ 一个数字都不许抄。
⚠️ 每个计数都要用文字点名它数的是什么人口。
⚠️ 任何「某串已消失」的腿用 code-only readergrep -vE '^\s*(\*|//|/\*)';⭐ 若交付面本身就是注释或字符串,那条腿要故意读注释并写明 —— 今天有一位 dev 的 ablation 就是因为 naive grep 把散文读成代码而作废重跑。
⚠️ 每条零读数都要一条方向已知、同一轮必须命中的对照。
⚠️ 两侧相等的腿必须印出命中行或带非空护栏(空串 sha256 =e3b0c44298fc1c14)。本席今天有七条腿栽在这上面,dev 零条。
⚠️ 用sed取一段声明时range 必须终止(例:const X = {…\n};)—— 未终止的 range 不是读错数字,是读了另一个人口。Commit 结尾恰好两行,⛔ 无其他 attribution:
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKrPR 正文结尾:
🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr⇒ 交付 DRAFT PR + 结构化 JSON 报告(probe block:每条腿一条可复制命令、pre/post 都 COUNTED 并注明 ref、人口用文字点名、至少一条会命中的已知方向对照)。⛔ 任何验收项做不到就明说,⛔ 不要静悄悄缩小交付面。
Generated by Claude Code
- 本席今晨解锁本卡时写下、分诊
os-dev-report
{ "issue": 3535, "status": "done", "branch": "claude/issue-3535-type-gate-collision-window", "pr": null, "premise_still_valid": false, "reachability_verdict": "NOT REACHABLE — the structural defence this card asks for is already fully in place. The collision is still CONSTRUCTIBLE (the raw material is abundant) but it can no longer REACH main.", "summary": "Triage's honest limit was the whole job, and re-deriving it closes the card. On today's origin/main (fb4ec65dafd522eaed64745ab1e29b1148a6291a) the remedy the card proposes — 'the type-gate job becomes a required check in this repo's merge queue' — is ALREADY DONE, and not by half: (1) ci.yml subscribes merge_group, the half that was missing on 2026-08-07, closed by objectui#3523; (2) 'Type Check' is a member of the main ruleset's required_status_checks; (3) on merge_group the job's docs-only skip path is unconditionally bypassed, so the gate really executes on the queue generation; (4) bypass_actors is null, so nothing can route around it. I built nothing and opened no PR, per dispatch. The branch is pushed but carries 0 commits.", "mechanism_that_closed_it": "The 2026-08-07 incident needed TWO independently-green PRs to be merged without anything ever type-checking their COMBINED tree. The merge queue now builds exactly that combined tree as a gh-readonly-queue/** generation, runs the full Type Check job on it with zero steps skipped, and will not merge a generation whose required context failed. Whichever of the two PRs is second through the queue is validated against a main that already contains the first — which is precisely where TS2578 appears.", "probe_block": { "ref_for_every_git_leg": "origin/main = fb4ec65dafd522eaed64745ab1e29b1148a6291a (fetched 2026-09-13; checkout NOT shallow, git rev-parse --is-shallow-repository = false)", "legs": [ { "leg": "1. Is the merge queue enforced on main, and can anyone bypass it?", "command": "curl -H 'Authorization: Bearer TOKEN' https://api.github.com/repos/objectstack-ai/objectui/rules/branches/main", "population": "the effective ruleset rules GitHub applies to branch main, all sources", "counted": "5 rules — deletion, non_fast_forward, merge_queue, pull_request, required_status_checks. All 5 carry ruleset_source objectstack-ai/objectui (ruleset 11776024, name 'main', enforcement=active, conditions.ref_name.include = ~DEFAULT_BRANCH). bypass_actors = null (read from /rulesets/11776024).", "verdict": "queue enforced; zero bypass actors" }, { "leg": "2. Is the type gate a REQUIRED context? (the card's remedy, verbatim)", "command": "same response, rule type=required_status_checks", "population": "contexts in required_status_checks for branch main", "counted": "9 contexts: Lint, Type Check, Build & E2E, Test (shard 1/4), Test (shard 2/4), Test (shard 3/4), Test (shard 4/4), Build Docs, Changeset Declaration. All integration_id 15368 (GitHub Actions). strict_required_status_checks_policy = true.", "verdict": "'Type Check' IS enrolled. ⇒ the maintainer floor this card fenced off is never reached, because the enrolment already exists." }, { "leg": "3. Does CI subscribe merge_group at all? (the half absent on 2026-08-07)", "command": "git show origin/main:.github/workflows/ci.yml | sed -n '1,60p'", "population": "the on: triggers of ci.yml at the named ref", "counted": "push, pull_request, and merge_group: types: [checks_requested]. The file's own header records the 2026-08-07 incident and says repo-wide event=merge_group runs stood at total_count 0 before that trigger landed.", "verdict": "subscribed" }, { "leg": "4. Can the Type Check job be skipped on a queue build?", "command": "git show origin/main:.github/workflows/ci.yml | sed -n '193,222p' (and sed -n '132,137p' for the job header)", "population": "the 'Decide whether this change needs a full run' step, plus the job header lines 132..137", "counted": "Job-level 'if:' inside lines 132..137 = 0 (verbatim window printed as the non-empty guard: 'type-check:', 'name: Type Check', 'runs-on: ubuntu-latest', 'timeout-minutes: 20', blank, 'steps:'). The step's FIRST branch is: if github.event_name != 'pull_request' then should_run=true — 'a merge_group build is the last validation before main. Running everything.' Every other step is gated on should_run == 'true'.", "control": "KNOWN DIRECTION, HITS: the same 4-space job-level '^ if:' pattern over all 37 workflow files enumerated FROM origin/main hits 4 files — changeset-release.yml (1), ci.yml (3), cross-repo-issue-closer.yml (1), dependabot-auto-merge.yml (1). ⇒ the reader can return non-zero, so the 0 is a reading.", "verdict": "the skip path is structurally unreachable on merge_group" }, { "leg": "5. EMPIRICAL: did the gate actually execute on a real queue generation?", "command": "curl .../actions/runs/34770552503/jobs?per_page=100", "population": "steps of the 'Type Check' job in CI run 34770552503, head_branch gh-readonly-queue/main/pr-9415-51fe04ec…, head_sha fb4ec65d… (which IS today's main)", "counted": "34 steps, conclusions {success: 34}, skipped: 0. Named steps that ran: 'Decide whether this change needs a full run', 'Verify type-check coverage', 'Type-check scripts/', 'Type-check the root Vitest config', 'Run type-check', 'Type-check repo-root vitest setup files'.", "control": "KNOWN DIRECTION, HITS: pull_request run 34763420817 (branch claude/issue-9297-teaching-surfaces-scope-names), SAME reader — 'Type Check' conclusion success with {success: 5, skipped: 28}, and the skipped set includes 'Type-check scripts/'. ⇒ the reader CAN return skipped, so '0 skipped' on the queue build is a reading and not a filter that matches everything.", "verdict": "the gate really runs on the generation" }, { "leg": "6. Do the required contexts actually attach to the generation commit?", "command": "curl .../commits/fb4ec65dafd522eaed64745ab1e29b1148a6291a/check-runs?per_page=100", "population": "check-runs attached to that gh-readonly-queue generation commit", "counted": "total_count 65. All 9 required contexts present with conclusion=success. Required contexts ABSENT = 0.", "control": "NONSENSE CONTROL: context 'Type Checq' present = False.", "verdict": "required set is evaluated on the generation, not only on the PR head" }, { "leg": "7. Is any type gate OUTSIDE the one required context?", "command": "for f in $(git ls-tree --name-only origin/main .github/workflows/); do git show \"origin/main:$f\" | grep -nE 'run: *pnpm type-check|tsc -p'; done", "population": "every type-gate invocation in all 37 workflow files, ENUMERATED FROM origin/main (same source as the reads, per AGENTS.md)", "counted": "exactly 4 hits, all in ci.yml: line 570 pnpm type-check:scripts, 599 type-check:vitest-config, 659 type-check, 686 type-check:vitest-setup. Job 'type-check' spans lines 132..732 (next job 'test' starts 733), so all 4 are inside the required 'Type Check' context. Separately: turbo.json task 'type-check' dependsOn ['^build', '//#type-check:e2e'] ⇒ pnpm type-check at line 659 also reaches the e2e gate.", "control": "KNOWN DIRECTION, HITS: same enumeration loop with pattern 'run: pnpm type-check:scripts' hits .github/workflows/ci.yml.", "note": "A first hypothesis that type-check:e2e was declared-but-unrun was DISPROVEN by this leg (turbo.json wires it) and is reported here rather than dropped.", "verdict": "no type gate sits outside the required context" }, { "leg": "8. Does queue rejection have teeth, or does it only ever run green?", "command": "curl .../actions/workflows/ci.yml/runs?event=merge_group&status=failure (and &status=success)", "population": "ci.yml workflow runs on event=merge_group, all time", "counted": "total event=merge_group = 1843; status=failure = 94; status=success = 1740.", "control": "NONSENSE CONTROL: status=faiure [sic] = 0.", "second_leg": "A rejected generation never lands: run 34630242284 (pr-9200) head_sha 60d7613e4e9ffa3b9cc7aa7b6118e53462e6e264 → git merge-base --is-ancestor SHA origin/main exits 1 (NOT an ancestor), on a checkout proven non-shallow. CONTROL, known direction, must be 0: the successful generation fb4ec65d… exits 0 (it IS main).", "verdict": "the queue demonstrably rejects and the rejected tree demonstrably never reaches main" }, { "leg": "9. INGREDIENT: is the raw material still present? (subject IS a comment)", "command": "git grep -l '@ts-expect-error' origin/main -- '*.ts' '*.tsx' '*.mts' | wc -l ; git grep -o '@ts-expect-error' origin/main -- '*.ts' '*.tsx' '*.mts' | wc -l", "population": "tracked .ts/.tsx/.mts files at origin/main carrying a TS suppression directive. COMMENTS READ DELIBERATELY — no code-only filter was applied, because the delivery surface IS a comment; a code-only reader would have filtered 100% of the subject and returned a false zero.", "counted": "238 files, 705 occurrences. Of those, 45 files live under scripts/**/*.ts — the exact tsconfig.scripts.json program that went red on 2026-08-07.", "tightening_lever": "still un-pulled: root tsconfig.json has NO extends and NO allowJs key ⇒ TS default false; include is ['packages','examples','apps']. tsconfig.scripts.json line 109 carries \"allowJs\": true (the landed 2026-08-07 tightening). vitest.config.mts carries exactly 1 @ts-expect-error — the one tsconfig.scripts.json's own header names as 'correct there and left alone'.", "control": "KNOWN DIRECTION, HITS: same grep for '\"allowJs\"' against origin/main:tsconfig.scripts.json returns line 109.", "verdict": "the collision is still CONSTRUCTIBLE — what changed is that it can no longer LAND" } ] }, "honest_limits": [ "Across ALL 94 queue rejections the failing job is always a Test shard (shard 4/4: 37, shard 2/4: 37, shard 1/4: 18, shard 3/4: 4) — NEVER 'Type Check'. So I did not observe the queue rejecting on a type failure specifically. The mechanism is proven to execute on the generation (leg 5) and proven to reject and to keep the rejected tree off main (leg 8), and Type Check sits in the same required_status_checks array as those Test shards — but the exact instance is absent from the record, and I am not inferring it.", "This verdict is a reading of a MUTABLE configuration taken 2026-09-13. Ruleset 11776024 was last updated 2026-08-24 — i.e. AFTER the incidents. Nothing in the repository mechanically notices if 'Type Check' were dropped from required_status_checks.", "I did NOT attempt a direct push to main to reproduce the 405 'Changes must be made through the merge queue' — pushing to main is forbidden to this seat. The enforcement claim rests on the ruleset reading (merge_queue rule active on ~DEFAULT_BRANCH, bypass_actors null), which is a stronger form of evidence than the anecdote anyway.", "I did NOT re-measure objectui#3523's acceptance observation; it is out of scope and was already measured satisfied on the un-hold." ], "fence_compliance": "⛔ Nothing was enrolled, requested for enrolment, or changed in any required-context set. No threshold lowered, no ratchet raised, no test skipped. The measurement is read-only. Notably the fence never had to be tested: the enrolment the card wanted ALREADY EXISTS, so no pm:awaiting-maintainer hand-off is needed either.", "routing_recommendation": { "on_this_card": "CLOSE as measured-not-reachable. Both routes the card proposed are moot: route 1 (type gate as required check) is DONE; route 2 (a pre-merge re-run against latest main) is precisely what a gh-readonly-queue generation already is, so it would be a second implementation of a defence that is already running 1843 times.", "residual_worth_routing": "The card's own sharpest lesson — 'a defence that only works when someone remembers is not a defence' — now applies one level up, to the required set itself. The defence works; nothing watches the defence. A cheap seat-authored guard is possible (a pin test asserting 'Type Check' is a member of required_status_checks at /repos/objectstack-ai/objectui/rules/branches/main, which this seat proved is readable with the ordinary session token — no admin scope needed). That is a NEW card needing its own dedup and dispatch; ⛔ I did NOT file it, and I am reporting it here rather than dropping it so it does not disappear. PM to route." }, "tests": "No code changed, so no build/test/changeset was owed and none is claimed. All legs are read-only measurements: git reads pinned to origin/main = fb4ec65dafd522eaed64745ab1e29b1148a6291a, and GitHub REST reads. Worktree created, verified clean (git status --porcelain empty, 0 commits ahead of origin/main), and removed; no other agent's worktree touched.", "mcp_calls": "1 — this comment. Every read was REST or git; the repo-scoped REST probe returned 200 at session start.", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
LANDED-equivalent — closing as measured-not-reachable. No PR; no code was owed.
本单问的是:收紧类型门的 PR 与 按旧口径写抑制指令的 PR 并行在飞时,语义相撞能不能落到
main。单子给了两条结构性方向。dev 席位量了之后的结论是:两条都已经是既成事实,所以这单不是「未做」,是「已被别的改动做掉了」。裁定
NOT REACHABLE. 相撞在今天仍然可以构造(原料充足:
@ts-expect-error在仓里到处都是),但它已经够不到main。判据 —— PM 席位独立复量,两半都打印了实测值
单子方向一是「把类型门设成 required check」。方向二是「合并前对最新
origin/main重跑一次」。两条各自的现状:其一 ——
Type Check是不是main的 required context?GET /repos/objectstack-ai/objectui/rules/branches/main→ HTTP 200,规则类型 5 种:deletion/merge_queue/non_fast_forward/pull_request/required_status_checks。merge_queue规则 PRESENT,来源objectstack-ai/objectui。required contexts 共 9 条,逐条打印:Lint Type Check <- 方向一问的就是这一条 Build & E2E Test (shard 1/4) Test (shard 2/4) Test (shard 3/4) Test (shard 4/4) Build Docs Changeset Declaration'Type Check' in contexts→ True- 胡话对照
'Type Checq' in contexts→ False(对照给出了已知方向,且与主腿同一次运行内命中)
⇒ 方向一 = DONE。
其二 —— 队列里跑的那次构建,到底订阅没订阅
merge_group?required check 写在 ruleset 里,但如果没有任何 workflow 订阅
merge_group,队列的必需集只能是空的 —— 这正是 #3523 记录过的历史空洞。所以第二半必须单独量。.github/workflows/ci.yml在origin/main上的on:块::44 merge_group: :45 types: [checks_requested]⚠️ 这一条我第一次量错过:sed -n '1,20p'的窗口只截到push:和pull_request:,读出来像是没有 —— 窗口太短不会读出错的数,它会读出另一个总体。加宽到 60 行才看见。记在这里,因为这正是本单在讲的那类错误的同构体。⇒ 方向二 = 已经是
gh-readonly-queue/**这套机制本身:队列为每个待并条目在当前main上重建一个 generation 并重跑上面 9 条 —— 单子提的「pre-merge 对最新 origin/main 重跑一次」,逐字就是它。所以两例实录在今天会怎么走
#3489 × #3498 那次(slipped 的一例):#3498 先并 →
main上type-check:scripts变红 → #3489 进队列时会在当前 main 上重建并重跑Type Check→ 红 → 被队列踢出,不落地。当年靠 PM 人工预判的第一例、靠热修 #3505 补救的第二例,今天都由机器接住。dev 席位交回来的诚实边界 —— 原样保留,不代为消化
- 队列从未被观测到因类型失败而拒绝。 全量 94 次队列拒绝里,失败 job 每一次都是某个 Test shard,没有一次是
Type Check。dev 明确拒绝由此外推「所以Type Check一定能拒绝」—— 这是配置读数支持的,不是观测支持的。我同意这个拒绝,并且不替他补这一步。 - 这是一份对可变配置的读数。 ruleset 11776024,最后更新 2026-08-24。它是今天下午的一次 API 读数,不是不变量。
残项 —— dev 明确没有立单,交回 PM 路由;我也不在本单里把它办掉
单子自己最锋利的那句话 ——「一道只在有人记得时才生效的防线不是防线」—— 现在原封不动地适用于上一层:
防线是在的;没有任何东西在看着这道防线。
Type Check从 required 集里被摘掉,不会红任何门、不会报警、不会留痕。候选修法是一条 pin 测试:断言Type Check是/repos/objectstack-ai/objectui/rules/branches/main的required_status_checks成员 —— 已验证普通 session token 即可读(上面那次 HTTP 200 就是),不需要 admin scope。这是一张新卡,需要它自己的查重和派发。⛔ 我不在本单顺手办掉,也不把它写成本单的验收项 —— 记在这里是为了它不消失。
本单处置
- 关单,state_reason
completed,理由 measured-not-reachable:两条提案路线均已 moot,无代码可写。 - 摘
pm:queue、摘 assignee。priority:p2/domain:devx不动(不是本席位能产的标签)。 - dev 的分支
claude/issue-3535-type-gate-collision-window已推,0 commits,无 PR —— 这是正确的结果形态,不是半成品。
Generated by Claude Code
残项已落成单据:objectui#9422 —— 「the merge queue's required-check set is watched by nothing」。
按本单 dev 的交回原样路由,filed bare(0 labels / 0 assignees),⛔ 本席位不定级。查重四问带命中对照,最近邻 objectui#6596 已通读并在单里写明为何不是重复(同族、不同主语:#6596 要新建一道门,#9422 要看住一道已经装好的门)。
本单保持 closed —— 残项是新卡,⛔ 不是重开本单的理由。
Generated by Claude Code
PM 立单(来源:#3504 热修 dev 的报告披露 + 本车道两次实际处置),observation-class,
finding不排队,留分诊轮定夺。查重:以全量 open 清单基线核对,无同源单。失败形态
一个收紧类型门的 PR(开
allowJs推断、扩 tsconfig 程序覆盖面等)与一个按旧口径写抑制指令(@ts-expect-error等)的 PR 并行在飞时,两者各自全绿、合并队列也拦不住 —— 因为撞的是语义(新门之下旧指令变 unused,而 unused 本身是 TS2578 报错),不是文本。两例实录
get),生产者侧修复 —— 零事故。pnpm type-check:scripts全线红(TS2578,main 的 Type Check job 全红:#3489 与 #3498 的语义冲突,check-doc-links.test.ts的@ts-expect-error在 allowJs 下变成 TS2578 #3504),每个在飞 PR 的 Type Check 都假红,直到热修 PR fix(scripts): 删除 check-doc-links 测试中已失效的 @ts-expect-error(修复净 main 上的 TS2578) #3505 插队落地。第一例靠 PM 人工预判,第二例证明人工预判不可靠。结构性方向(#3505 dev 的建议,未裁定)
type-check:scripts(或相应收紧后的门)设为 required check——队列在当前 main 上重建后跑,语义相撞会在入队时暴露而不是落地后;两条都有运行成本(队列吞吐 vs 每 PR 一次重跑),值得先量本仓队列现状再选。另注:
.github/workflows/duplicate-fix-guard.yml的覆盖边界是「同 issue 号的两个 PR」,对这类语义相撞零覆盖(objectstack 侧 Operational notes 第 8 条同理)。关联:#3504 / PR #3505(事故与热修)、PR #3496 / #3498 / #3489(两例)、objectui#3513 的 tsconfig 注释里也写明了 allowJs/TS2578 地雷。