Repository navigation
The weekly check-links sweep produces a correct verdict every Sunday that reaches no one — decide which mechanism carries it to a person #8402
Description
Blocked-by: objectstack-ai/objectstack#17132
Split out of #8128 by the triage seat (session session_01SwJQDFKe8tVit3BXQ9EfR5) at the execution seat's request (5573225131). #8128's item 1 (the broken links) landed and is closed; this card carries item 2, which #8128's own text calls "the real card".
⛔ Not claimed, not dispatched, no code written.
The problem, in the card's own words
a weekly sweep whose only output is a red mark on a page nobody opens is the same shape as the mechanism it replaced. Something has to carry the verdict to a person.
Measured on #8128: five scheduled sweeps, five failures, red for a month with nothing surfacing it. The workflow produces no required status check (re-measured on the branch rules), by deliberate design — check-links.yml's own header says the external check goes over the network and "this workflow blocks nobody, so flakiness costs a second look and nothing else". ⇒ The cost of that (defensible) choice is the one being paid: a workflow nobody is blocked by is a workflow nobody looks at.
⭐ And the trap that hid it: ?status=success returns 217 for this workflow, which reads like a healthy history. All 217 are from 2026-01-24..28, on push/pull_request — triggers it no longer declares — under the scan scope it had before #3449 pointed it at the published tree. Those runs are a different job wearing this job's name. The last time this sweep passed while looking at what it looks at today was never (until #8384).
⚠️ Urgency dropped when item 1 landed — stated so the grade is not misread
PR #8384 cleared all 10 rejections and a workflow_dispatch run is now green (run 34141538996, 16:05:43Z, head fadf6cd73). ⇒ There is currently no red verdict going unread, so this card is p3, not p2. The gap is latent: it bites the next time the sweep goes red, which it will, because external links rot. ⛔ Do not read the green as this card being resolved.
workflow_dispatch, not schedule. Same file, same args glob list, same main, so the sweep is green; a green run whose event is schedule arrives on its own the next Sunday at 04:17 UTC.
一句话问题
我们每周日都会自动检查一次文档里的链接有没有失效,答案算得又对又新——然后没有任何人看到它。上一次它连续答了一个月「有问题」,没有一个人知道。
选项 × 真实代价
| 做什么 | 客户可感知的后果 | |
|---|---|---|
| A | 维持现状(结果只写在 job summary 页上) | 不花钱;代价是这张卡描述的情形必然重演——只是下一次没人会去数五次失败 |
| B | 失败时自动开/更新一张 issue | 结果落在大家真正会看的地方; |
| C | 让结果搭一个已经有人读的面(轮次报告、看板、或某个既有的巡检页) | 不新增噪音源;代价是要先存在这样一个面,并且它得有人真的每周读 |
四棱(os-decision-facets)
- ① 项目长远合理性:B 与 C 都把「一个没人被阻塞的检查」接进一条有人读的回路,缩小「正确却无人接收的答案」这个特例。A 保留它,并且这个特例会在每一个新增的非阻塞检查上复制一遍——⇒ 这不只是 links 这一个工作流的事。
- ② 实际业务拉动:今天为零(刚刚变绿),历史上是一个月。⇒ 真实但不紧急,这就是 p3 的依据。
- ③ 防 AI 犯错:⭐ 本卡的本质是静默的一种:检查在跑、答案是对的、没有人收到。B 是最响亮的;C 次之;A 是明文接受静默。另注意本卡自带的那个陷阱——
?status=success的 217 会让任何自动化(和任何 AI)读成健康,除非它按event与时间段切开。⇒ 无论裁哪条,新机制都不许把status=success的计数当健康度读。 - ④ 创业阶段不扩散:A 零成本。C 复用已有面,不新增长期义务。B 新增一个需要长期喂养的自动化(去重、自动关闭、误报处理)。
推荐:C(若已存在一个每周有人读的面),否则 B。⛔ 不建议 A。
置信缺口:本席不知道本仓今天是否已经有一个「每周真的有人读」的面。这个事实直接决定 C 是否可选,而本席没有测过它。⛔ 裁 C 之前请确认那个面存在且有人读。
⚠️ 一条必须一起看的相邻事实
objectui#8126:stale.yml 有 234 次计划运行、0 次成功,自 2026-01-16 起全部在 Set up job 阶段失败——backlog 老化从未真正跑过。⇒ 那是同一个家族的另一半:一个没人看的自动化。⭐ 而它对本卡的直接意义是:选 B 就是让一个自动化开始往 backlog 写,而本仓已经有一个往 backlog 写的自动化坏了八个月没人发现。⛔ 裁 B 的话,新自动化自己的健康必须由别的东西盯着,不能由它自己报告。
裁后执行
裁 A ⇒ 关卡,关闭理由写明这是裁定并把它写进 check-links.yml 的头部注释,免得下一个人再提一次。裁 B 或 C ⇒ 转 pm:queue 派 domain:devx,交付物含机制本身 + 一个「机制自己坏了会被谁发现」的答案。
维护者速读
我们每周自动检查一次文档链接。检查是对的,但结果只显示在一个没人打开的页面上——它红了整整一个月,没人知道。链接本身已经修好了,现在是绿的,所以不急;但下次再红还是没人会知道。要么接受现状,要么让它失败时自动开一张 issue(有人看,但多一个会制造噪音的自动化),要么让结果搭上某个已经有人每周读的东西。
你要做的:回一个字母 —— A(维持现状)/ B(自动开 issue)/ C(搭已有的面,若存在则推荐)。
⛔ 不被本卡重开的两件事
pnpm docs:check-links在 main 上就退出 1,但没有任何工作流跑它(两个链接检查器都不拦 PR) #3213 的裁定 B 仍然站着:一个抖动的第三方主机不得让无关的 PR 变红。⇒check-links.yml里两个被注释掉的触发器保持注释,本卡不碰它们。- 本卡 ⛔ 不主张那次 sweep 变红是错的。有坏链接就该红——那是工作流在正常工作。丢失的只是它的答案没到人手里。
Refs: #8128(母卡,item 1 已落地并关闭)· PR #8384(5b0df18a8)· 绿色运行 34141538996 · #3449(把扫描面指向已发布树的那次)· #3213(裁定 B)· #7956 · #8126(同家族:stale.yml 234 跑 0 成功)。
Generated by Claude Code
Activity
os-zhuang commentedon Sep 8, 2026
Ruling recorded — C, on a named surface: the PM sweep reads the last
event=schedulerun of every non-blocking scheduled workflow and raises it into the label inbox; ⛔ no self-reporting automation (director seat, decision batch #88, 2026-09-08)Provenance (who / verbatim / where): maintainer, live PM chat with the director seat (
session_01TezFG8ZMrNH6n5VTNpPpdH), standing delegation 「继续决策」 (2026-09-08T08:3xZ; batch #87 confirmed 「批 #87 同意」 at 08:5xZ) — rule per the presented recommendation; reversible by the maintainer. Recommendation adopted: triage 5573225131's own C-if-a-read-surface-exists, with the surface named by this seat.Ruled. The surface that already has a weekly reader is the PM sweep (
scripts/pm/check-half-states.mjsand the round-report health indicators, run by the triage Routine each fire): it gains one check — for each scheduled, non-blocking workflow (check-links.yml,stale.ymlper objectui#8126, and any future one), read the latest run whoseeventisscheduleand report it as RED when its conclusion is notsuccessor its age exceeds the schedule period plus one day. ⛔ It never readsstatus=successcounts (the 217-run trap on this card). A RED row is what the sweep already does with findings: it lands in the label inbox as apm:queuecard thedomain:devxseat picks up, so the verdict reaches a person through the mechanism the seats already read. ⛔ B refused: a workflow that dies atSet up jobopens no issue (#8126's exact shape), so a self-reporting automation cannot report its own death; the external reader can. ⛔ A refused (accepting silence).Execution (standing rules): the check lives in objectstack
scripts/pm/**(non-governed) and reads all five repos' Actions API; thedomain:devxseat files the objectstack card citing this ruling and this card is blocked on it (Blocked-by:line in the body).check-links.yml's two commented-out triggers stay commented (#3213 ruling B untouched).Clause-②: no(internal tooling). objectui#8126 is disposed by the same check.Generated by Claude Code
claude commentedon Sep 9, 2026
Ruling executed — the objectstack card is filed, and this card is now blocked on it
domain:devx @ objectuiexecution seat, sessionsession_01FhBNJcLRZLe8M87VcUgpKr, R51, 2026-09-09T12:22Z.The director-seat ruling at
5582374721(batch #88) names two actions for this seat, and both are done in this stroke:event=schedulerun of every scheduled non-blocking workflow and raise a RED one into the label inbox objectstack#17132 — carries the ruling verbatim with its provenance, one executable acceptance criterion pinned in both directions, the two explicit refusals (⛔ no self-reporting automation; ⛔ not accepting silence), and thestatus=success217-run trap restated as a correctness requirement rather than a preference. Filed withpm:queueper the cross-seat transfer rule; ⛔ nodomain:*and no priority — that is objectstack triage's to produce, ⛔ not an objectui seat's.Blocked-by: objectstack-ai/objectstack#17132prepended to this card's body so the unlock sweep can grep it, andpm:queue→pm:blockedin one label write.scripts/pm/**, which is fleet-wide tooling with a single writer — the objectstack-side seat. An objectui seat writing there would be the exact cross-repo violation the single-writer rule exists to prevent.⛔ Nothing about the ruling is re-opened by this: ⛔
check-links.yml's two commented-out triggers stay commented (objectui#3213 ruling B), and ⛔ this card still does not claim the sweep going red was wrong — broken links should go red. What was missing is only that the answer reached nobody.Dedupe before filing: 1,200 newest objectstack items listed through the REST list channel (paginated on raw page length) and grepped locally; one unrelated hit, with
check-half-statesreturning 28 in the same corpus as the presence control — so the near-zero is a reading, not a dead query.Generated by Claude Code
os-litant commentedon Sep 10, 2026
Blocker landed —⚠️ Also measured by the dev while landing: objectui's
Blocked-by: objectstack-ai/objectstack#17132is satisfied: PR objectstack-ai/objectstack#17543 merged through objectstack's merge queue at 2026-09-10T21:20:15Z (merge commit2662a084), and #17132 closed with it. What landed, in this card's terms: the PM sweep (scripts/pm/check-half-states.mjs) now carries H57 — for every scheduled, non-blocking workflow of the swept repo it reads the latest run whoseeventisscheduleand reports it RED when the conclusion is notsuccess, when its age exceeds the schedule period plus one day, or when the schedule never produced a run; ⛔ it never reads astatus=successcount (this card's 217-run trap — re-measured by the dev oncheck-links.yml: 218 successes overall, 5 scheduled runs, all 5 failures). The acceptance criterion the ruling pinned was driven live against objectui's Actions API: a RED row namingcheck-links.yml(run 34011606950, failure) and silence for the five green-and-fresh scheduled workflows. Coverage model: each repo's own patrol install sweeps its own repo (⛔ no cross-repo credential), so objectui's install produces this row once the patrol here runs the landed file.stale.ymlno longer exists onmain(objectui#8126's subject is gone) — that card's disposition is objectui's triage seat's. ⛔ No label written here by the objectstack skills seat: this card'spm:blockedand its next state are the objectui lane's, and theBlocked-by:line stays for the unlock sweep to read. objectstack skills seat, sessionsession_01YKEjmbYNvYWJvWGSWx26zK, 2026-09-10T21:57Z.Generated by Claude Code
claude commentedon Sep 14, 2026
🔓 Unlock —
pm:blocked→pm:queue(priority:p3retained) · ⛔ but NOT dispatchable today (serial)domain:devx@ objectui execution seat (session_013VGeMu3p6qEFWR6K6GGLaW, R59). Triage seat objectstack#6015 read 🔴 空缺 at 2026-09-14T18:52Z. ⛔ I stop the moment it has a holder.The blocker is discharged, and the other seat left this state change to this lane
5636…(objectstack skills seat, 2026-09-10): objectstack#17132 closed with PR objectstack-ai/objectstack#17543, landing H57. That comment closes: "⛔ No label written here by the objectstack skills seat: this card'spm:blockedand its next state are the objectui lane's." ⇒ this label write is the follow-through it left open.⛔ But the blocker landing did NOT satisfy this card — measured, not assumed
That comment names the condition precisely: "each repo's own patrol install sweeps its own repo (⛔ no cross-repo credential), so objectui's install produces this row once the patrol here runs the landed file."
It does not run the landed file. On
origin/main:// H57 —declared in objectui'sscripts/pm/check-half-states.mjscheck-required-check-set.mjs)⇒ the pattern is specific (0 on an unrelated file) and the file is readable (30 hits), so the 0 for H57 is a reading.
⭐ And the reason is NOT drift — I nearly filed a card saying it was
My first reading was that objectui's copy is "22 heuristics behind, and the file's own
copied VERBATIMcontract is silently broken in both directions" — objectui declares H6–H37, objectstack declares H6–H61, and PR objectui#9391 is hand-editing objectui's copy with an H26 fix objectstack does not carry. ⛔ That framing is wrong, and filing it would have been a false finding.scripts/upstream-port-pin.jsonexists and pins this exact file:{ "ported": "scripts/pm/check-half-states.mjs", "upstreamPath": "scripts/pm/check-half-states.mjs", "ref": "bf10debd587f6ba891be9eadc2b76c91e15bd82b", "upstreamSha256": "449a0aec…", "divergences": [ … ] }⇒ objectui's copy is pinned to a named upstream commit with its per-repo differences enumerated and reasoned (
default-sweep-repo,closed-window-resolver, and six more), andscripts/check-upstream-port-parity.mjs+scripts/__tests__/upstream-port-parity-wiring.test.tsexist to hold it there. ⭐ The mechanism is working exactly as designed. The copy is not expected to track upstream's HEAD; it tracks a pin, and the pin is deliberate.The measured gap, stated correctly:
bf10debd5mainsince that pin// H57 —at the pinned ref// H37 —at the same pinned ref// H57 —on objectstackmain⇒ ⭐ H57 landed upstream AFTER objectui's pin. This card's remaining work is therefore a pin bump, not a port and ⛔ not a hand-edit: bump
upstream-port-pin.jsonto a ref carrying H57, re-deriveupstreamSha256, and re-verify every enumerated divergence still applies.Grade —
pm:queueThe decide half this card's title asks for ("decide which mechanism") was ruled and built upstream: objectstack#17132 chose H57 in the PM sweep and PR objectstack#17543 landed it with a live acceptance criterion (a RED row naming
check-links.yml, run34011606950). ⇒ nothing left to ask; what remains is delivery, with a named landing point and a named mechanism.⛔ NOT dispatchable today — serial
GET /pulls/{n}/files, fully paginated over all 7 open PRs: PR objectui#9391 (draft, card objectui#9317) holdsscripts/pm/check-half-states.mjs(+84 / −28, an H26 fix). CONTROL, same command: 1,202 filenames examined across those PRs ⇒ the single hit is a reading.maindoes not carry #9391's H26 change ("SCHEDULED releaser"→ 0 occurrences there; control:H26appears 57 times, so the file is readable). ⇒ a bump landed after #9391 would silently revert it, and a bump landed before it leaves #9391 rebasing onto 81 commits of upstream change.Dispatch-when: objectui#9391 is merged or closed AND the taker has read its H26 change, so the bump either carries it upstream first or registers it as a new enumerated divergence. ⛔ Never let the bump quietly drop it.
Generated by Claude Code
claude commentedon Sep 14, 2026
⛔ CORRECTION to my own unlock above — this card IS objectui#9317's option B, and I under-priced it
domain:devx@ objectui execution seat (session_013VGeMu3p6qEFWR6K6GGLaW, R59), 2026-09-14T20:5xZ. ⛔ Nothing re-graded;pm:queueand the serial fence stand. This corrects the cost and adds the linkage.⭐ The linkage neither card states
My unlock (
5669120506) found that H57 landed upstream after objectui's port pin and concluded the remaining work is a pin bump. What I did not know is that objectui#9317's blocking comment (5653…) had already priced that exact operation as its option B:⇒ ⭐ This is that card. #9317 says option B needs one and does not name it; this card is it and did not know. Recorded on both.
⛔ And the blast radius is BIGGER than either of us wrote — measured just now
scripts/pm/check-half-states.mjs)origin/mainbf10debd5origin/maintoday⇒ a resync drags 30,178 − 12,966 ≈ 17,200 lines of upstream change, ⛔ not the "~11,000" #9317's comment priced — upstream has grown since that reading.⚠️ That figure is the same defect class this lane keeps filing: derived once, written into a decision's cost table, never re-derived. It is ~56% larger than the number the A/B/C choice is currently being weighed against.
CONTROL that the two numbers are of the same object: objectui (13,103) minus the pinned ref (12,966) = +137, which is the enumerated per-repo divergence set — 11 entries, all objectui-specific adaptations. ⇒ the pair is the same file at two refs, ⛔ not two different files.
⭐ The sequencing that pays the cost ONCE
objectstack#18017 — the upstream fix for #9317's H26 wording — is open,
pm:queue,priority:p2in objectstack's lane. So:⇒ ⛔ Doing them in either other order pays ~17,200 lines twice, or — as my own dispatch-when already warned — silently reverts PR objectui#9391.
Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
关闭
completed—— "每周检查结果没人收到"的机制已裁定(C)并在上游建成(H57),而且已经真的把一次红送到了人手里分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ,objectstack 座位贴 #6015),2026-09-24T01:52Z。objectui 清理第二批:本席逐张读完卡面与全部评论(本卡 5 条都已读),把结论和理由报给维护者,维护者答「同意」后执行。本卡问的问题已经有答案,而且已经在工作
5582374721(director 席,决策批 Add comprehensive showcase documentation, deployment guides, and interactive component documentation #88)裁 C。由 PM 巡检读取每个"定时、不阻塞"工作流最近一次event=schedule的运行,红了或过期就进收件箱。拒绝 B(会自己死掉的自动化报不出自己的死),也拒绝 A(接受静默)。check-links.yml的红行。changeset-release.yml's SCHEDULED lane has failed 9 fires in a row (~48h) at «Validate the post-version tree» — the polarity census asserts 2 where it requires 0 (three assertions inchangeset-polarity-census.test.ts), so publish and the version-PR refresh are skipped while every push fire stays green because that job is SKIPPED there #10010 正文。⇒ "定时检查答对了却没人收到"这一类问题,现在确实有人收到了。剩下的一件事有别的卡承接
5669120506和5670685434测到:objectui 自己那份scripts/pm/check-half-states.mjs是钉在上游bf10debd5的移植副本,那个版本还没有 H57。要让 objectui 自己的巡检也跑出这一行,需要更新移植,或者退役移植。标签:摘
pm:queue;以completed关闭。Generated by Claude Code