fix(smokes): restore the three public smoke contracts broken on main - #4718
Conversation
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Reviewed exact head: 4718@237e2e19d006379190f446003e5260fe5347a0b4
Review basis: loopx pr-review --state all packet (execution policy_revision: 7), the full local diff, and a clean origin/main control worktree used to reproduce every failure string.
动机
main 的 Full Public Smokes 在 shard 0 / shard 2 上是红的(最近一次红 run 35379352831,head 42620170d;三条在 8299078f2 上仍可复现)。这些 smoke 是仓库对"公共契约"的护栏,红的护栏既掩盖新回归,也让后续所有 PR 的 CI 判定失去基线。它们红的原因不是同一条产品缺陷,而是三处"契约/守护"配对各自漂移:
loopx goal-acceptance是真实存在的 owner 命令(loopx/cli_commands/goal_acceptance.py,inspect|configure|verify|disable),却从未进入 CLI 帮助目录的分类集合;- blocked-priority 通知的真实出货文案以句首大写
No owner action is required:开头(loopx/control_plane/quota/blocked_transition_notice.py:147),而守护断言写成全小写; ark-turn.yml单独钉了补丁版本24.21.0,与examples/github-actions-runtime-smoke.py的PRIMARY_NODE_VERSION = "24"策略不一致。
本 PR 的动机就是把这三条护栏恢复到"能各自因为自己的原因失败",而不是因为主干自身的漂移一直红着。
改动思路
三条各自按最小、语义正确的方向修,且都不改变任何运行时行为:
- 命令分类缺失 → 修分类源(
loopx/help_surface.py的COMMAND_GROUPS),再由目录重新生成man/loopx.1,而不是改 smoke 的期望集合。守护继续守护"每个对外命令都被分类"这条不变量。 - 大小写噪声 → 修断言(
todo-first-open-summary-smoke.py),保留对策略措辞的断言强度,只去掉对句首大写的隐式依赖。策略文案本身不改。 - 版本钉死 → 修工作流(
ark-turn.yml)对齐既有策略"24",并在 PR 描述里显式标为判断项,给出反向修复路径(若24.21.0是有意为之,正确做法是像SQLITE_NODE_VERSION = "22.22.3"那样在 smoke 里登记为有文档的例外,而不是静默钉补丁)。
方向上是"让守护和出货事实重新对齐",不是"让 CI 变绿"——两处改的是守护的对齐方式,一处改的是出货配置本身,且这一处被明确标记为需要维护者确认的判断项。
具体改动
.github/workflows/ark-turn.yml(+1/−1):node-version: "24.21.0"→node-version: "24"。与desktop-updater.yml、frontstage-pages.yml、full-public-smokes.yml、python-tests.yml等主工作流一致;PRIMARY_NODE_VERSION未变。examples/control_plane/todo-first-open-summary-smoke.py(+5/−2):两处断言由in ...改为in ...lower(),并附注释说明出货文案以句首大写No开头。断言的策略短语、notify == "NOTIFY"、action_required is False以及 markdown 行断言全部保留。loopx/help_surface.py(+4):在既有的 "Maintainer and adapter commands" 组内、change-quality之后新增loopx goal-acceptance条目,purpose 为 "Configure, inspect or verify a versioned Goal acceptance basis."。沿用现有分组与就近排序,未新增分组、未改动分类算法。man/loopx.1(+3):由scripts/render-manpage.py从目录重新生成,与上一行同源,不是手写补丁。
关键内容讲解
cli-help-manpage-smoke.py 真正守护的是"CLI 帮助目录与手册覆盖完整":它会枚举 loopx 的子命令并与 COMMAND_GROUPS 求差集,未分类即 AssertionError: {'unclassified': [...]}。因此正确的修法是补齐分类表,再让生成物跟随源;若反过来只把 smoke 的期望集删掉,守护就失去意义。同理,todo-first-open-summary-smoke.py 的语义是"blocked 时用户可见的提示必须显式说明无需用户动作",策略短语是契约、句首大小写只是排版,所以降噪的正确边界是大小写不敏感匹配,而不是放宽短语本身。github-actions-runtime-smoke.py 的 expected 集合由 PRIMARY_NODE_VERSION 推导,是"所有主工作流共享同一主运行时"的策略表达;把 ark-turn.yml 对齐到 "24" 让新工作流回到策略内,且该文件只改版本声明,不触及 job、matrix 或权限。
对主干的风险
- 影响面限于帮助目录、手册生成物、一条 smoke 匹配方式与一条工作流版本声明;无
loopx/**运行时行为、无权限/持久化/协议/quota 语义变化。 - 唯一有产品含义的是
ark-turn.yml的 Node 版本:从24.21.0变为"24",会把该工作流交给 setup-node 解析到 24.x 最新补丁。若有未记录的原因必须钉24.21.0,则该 PR 应改回并在 smoke 中登记例外——这一点已在 PR 描述中作为判断项交给维护者,不隐藏。 - 反向复现证据完整:在干净
origin/main工作树上三条各自以 CI 的原始错误串失败(unclassified: ['goal-acceptance']、('ark-turn.yml', ['24.21.0'])、小写短语断言),在本 head 上三条全绿;相邻 smoke(full-public-smokes-workflow、frontstage-pages-workflow、desktop-release-workflow、docs-governance、todo-cli、todo-contract、work-lane-contract)一并通过;loopx canary premerge --from-git-diff为 0 failures / 0 advisories。 - 残留风险如实标注:完整 Full Public Smokes 套件的最终证据仍在 CI,本次本机只证明目标三条由红转绿与相邻覆盖;以及上面那条 Node pin 的意图确认。
我的整体评价
同一个"契约/守护漂移"家族的三个独立实例,修法分别落在正确的所有者上(分类源 / 断言噪声 / 出货配置),diff 仅 4 文件 +13/−3,属于小而完整、可独立回滚的一刀,无阻断性发现(findings: [])。建议合并;合并不改变任何运行时语义,ark-turn.yml 的版本判断项请在合并时确认一次。
非阻断 P2 建议(不阻挡合并):这一批修复来自"守护与出货事实脱钩"的同一类根因,值得在后续把 Node 运行时的策略表达收敛到一处(例如让 PRIMARY_NODE_VERSION 与工作流共享一个可读的单一来源),以免下一条新工作流再次静默钉补丁版本。
English verdict: APPROVE - head 237e2e1 restores the three public smoke contracts broken on main (unclassified goal-acceptance command, case-pinned blocked-notice assertion, ark-turn.yml node pin vs PRIMARY_NODE_VERSION); full diff is 4 files +13/-3 with no runtime, permission, state or protocol change, all three smokes fail on clean main with the exact CI strings and pass here, adjacent smokes and canary premerge are green, no blocking findings; only residual item is maintainer confirmation of the ark-turn.yml pin.
`main` (4262017, still failing on 8299078) fails Full Public Smokes in shards 0 and 2. Each failure is one side of a contract/guard pair drifting; all three reproduce on a clean `origin/main` worktree. - `examples/cli-help-manpage-smoke.py` reports `unclassified: ['goal-acceptance']`. `loopx goal-acceptance` is a real owner command (it is the entry point documented in the personal-workspace guide) but was never classified in the manual catalog. Classify it in the existing "Maintainer and adapter commands" group next to `change-quality`, and regenerate `man/loopx.1` from the catalog. - `examples/control_plane/todo-first-open-summary-smoke.py` asserted the lowercase phrase `no owner action is required`, but the shipped blocked priority notice starts that sentence with `No` (it follows a full stop). Keep the guard on policy wording but match it case-insensitively instead of pinning sentence-initial capitalization. - `examples/github-actions-runtime-smoke.py` reports `('ark-turn.yml', ['24.21.0'])`. Every other workflow declares the qualified primary runtime `24`; only this one pins a patch version, so it is aligned to the primary. If that pin was deliberate, the policy-conformant repair is an explicit documented exception in the smoke rather than a silent patch pin. Validation: the three smokes pass on this head (and failed on a clean main worktree with exactly the CI error strings); the adjacent workflow, todo, docs-governance and work-lane smokes pass; `loopx canary premerge --from-git-diff` reports 0 failures / 0 advisories. Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
237e2e1 to
bdd6178
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
审阅对象:PR #4718(开放中,未合并),exact head bdd617858dae62fa6ea7ef3ec9deb33c0899f692(作者 huangruiteng)。本文是该 exact head 的评审记录。
动机
main 上 Full Public Smokes 的 shard 0 与 shard 2 是红的,三处失败各自都是"契约与守门人配对后单侧漂移":ark-turn.yml 把 Node 钉成 24.21.0,而运行时守门人要求除 python-tests/postgresql 之外的工作流只声明主版本 24(其余 7 个 workflow 都是 24,它是唯一的离群值);todo-first-open-summary-smoke.py 断言的是 no owner action is required 这个小写开头的串,而实际投放的提示句里这句跟在句号后、首字母大写;策展 help 面(help_surface.py + 生成出来的 man/loopx.1)漏了已发布的 loopx goal-acceptance,被 help-manpage 守门人判为 unclassified。红着的主干会掩盖同 shard 的新回归,也让每个后续 PR 都要替历史失败买单。
改动思路
每处只修漂移的那一侧,不动守门人:工作流对齐仓库既有的主版本约定;冒烟把"策略短语存在"这件事按大小写无关匹配,而不是把投放文案的句首大小写写进断言;策展 help 面补上那条命令,并让生成物 man/loopx.1 与 render_manpage() 保持一致。三个文件都没有放宽阈值或删断言——这也是本轮评审要确认的重点:修的是漂移,不是把报警器关掉。
具体改动
4 个文件、+13/-3。.github/workflows/ark-turn.yml:36 "24.21.0" → "24";examples/control_plane/todo-first-open-summary-smoke.py:244 两处断言改为对 reason.lower() / markdown.lower() 做匹配(并留了注释说明为什么不再钉句首大小写),相邻断言(notify=NOTIFY、action_required is False、blocked_priority_* 标记)原样保留;loopx/help_surface.py:247 在 "Maintainer and adapter commands" 组内按字母序插入 loopx goal-acceptance 及其用途行;man/loopx.1:201 同步该条目。
我的验证:在 head 上 todo-first-open-summary-smoke ok、cli-help-manpage-smoke ok、github-actions-runtime-smoke ok: Node 24 primary, 22.18 minimum, 26 forward, 22.22.3 sqlite/authority;在干净的 base worktree(96364a3844c)上三处都复现且报错串与 CI 一致——AssertionError: (...) 'No owner action is required: ...'、{'unclassified': ['goal-acceptance'], 'stale_manual': [], 'stale_help_only': []}、('ark-turn.yml', ['24.21.0'])。另外我单独把 man/loopx.1 与 render_manpage() 做逐字节比对:完全一致(10318 字节),说明这条 help 面改动是按生成源改的,不是手改生成物;含隐藏文件的全树检索确认 24.21.0 只在 base 出现 1 次(ark-turn.yml:36)、head 为 0 次;loopx goal-acceptance 的子命令为 inspect/configure/verify/disable,新增用途行覆盖其中三项,与该列表其他条目的写法(如 change-quality、integration-branch)一致。
对主干的风险
改动面是"一条 CI 版本串 + 两行断言 + 两条文档行",无运行时、权限、配额或持久状态影响,回滚即退四文件。两条有界后果值得写下来:其一,短语断言改为大小写无关后,只看大小写的漂移不再触发失败(这是有意的——断言的目标是"策略短语仍被投放",不是文案排版);其二,ark-turn 现在与其他 7 个 workflow 一样在 Node 24 内浮动,将来 24.x 的行为变化会不经 diff 到达该任务(这与仓库既有约定一致)。另外新增 help 条目只写了 configure/inspect/verify,未列 disable,与该列表其余条目的颗粒度一致,不构成误导。证据边界:我跑的是三处组成检查本身,没有端到端重跑 Full Public Smokes 工作流;GitHub 报该 PR mergeStateStatus: BLOCKED,我没有把任何失败归因到本 diff(它修的正是 main 上原本就红的那批)。
我的整体评价
结论 APPROVE。这是一次干净、最小、方向正确的漂移修复:三处都修在"漂移的一侧",守门人文件一行未动,并且我在 base 上逐条复现了原始失败串、在 head 上逐条验证通过,man 页与生成源逐字节一致。把三处放在一个 PR 里是合理的——拆开会留下两个 shard 继续红着等第三个,而每处都只有一行,评审成本很低。合并仍归维护者;合并后建议顺手确认 Full Public Smokes 的 shard 0/2 在那次 run 上转绿。
语义与 CI 对齐
判定 reuse_existing(复用既有词表与既有契约,不新建):受影响契约是 loopx/help_surface.py 的 COMMAND_GROUPS(以及由它生成的 man/loopx.1)、.github/workflows/ark-turn.yml 的 Node 运行时声明、以及 examples/control_plane/todo-first-open-summary-smoke.py 的短语断言;三个守门人文件本身(examples/github-actions-runtime-smoke.py、examples/cli-help-manpage-smoke.py、help surface 的分类规则)均未改动,语义没有扩张。CI 侧对齐情况:该 head 上三处组成检查本地全绿,且改动让 ark-turn.yml 与仓库其它 7 处 24 声明一致;需要提醒的是 PR 的 merge state 被 GitHub 标为 BLOCKED,这是分支持有状态而非本 diff 引入的失败。
English verdict: APPROVE - Review of open PR #4718 at exact head bdd6178 (author-owned; recorded as a COMMENTED approval because GitHub blocks formal self-approval). The PR restores the three public smoke contracts that are red on main, each by fixing the drifting side rather than the guard: ark-turn.yml's node pin moves from "24.21.0" to the primary "24" that the other seven workflows already declare, the blocked-priority notice assertion matches the policy phrase case-insensitively instead of pinning the sentence-initial capital of shipped copy, and the curated help surface plus its generated man page gain the missing loopx goal-acceptance entry. I reproduced all three failures in a clean worktree at the PR base with the exact CI strings (('ark-turn.yml', ['24.21.0']), the assertion dump showing "No owner action is required:", and {'unclassified': ['goal-acceptance'], ...}) and confirmed all three pass at the reviewed head; I also verified that man/loopx.1 is byte-identical to render_manpage() (10318 bytes), that "24.21.0" exists only at the base, and that the added purpose text matches the command's inspect/configure/verify subcommands. No guard expectation was relaxed. Bounded consequences: capitalization-only drift of the notice no longer fails that smoke (intended), and ark-turn now floats within Node 24 like the rest of the fleet. Evidence limits: I ran the three constituent smokes rather than the Full Public Smokes workflow end to end, and GitHub reports mergeStateStatus BLOCKED without a failing check attributable to this diff. No blocking finding. Merges remain with the maintainer.
What this repairs
mainfails Full Public Smokes in shards 0 and 2 (latest red run35379352831at42620170d; all three still reproduce at8299078f2). Each failure is one side of a contract/guard pair drifting, reproduced on a cleanorigin/mainworktree with the exact CI error strings.mainexamples/cli-help-manpage-smoke.pyAssertionError: {'unclassified': ['goal-acceptance']}.loopx goal-acceptanceis a real owner command — it is the entry point documented indocs/guides/personal-workspace-user-guide.md— but it was never classified in the manual catalog.change-quality, and regenerateman/loopx.1from the catalog (scripts/render-manpage.py).examples/control_plane/todo-first-open-summary-smoke.pyno owner action is required, while the shipped blocked-priority notice starts that sentence withNobecause it follows a full stop (No owner action is required: the agent keeps the fallback ... moving).examples/github-actions-runtime-smoke.pyAssertionError: ('ark-turn.yml', ['24.21.0']).PRIMARY_NODE_VERSION = "24"and every other workflow declares that qualified primary runtime; only this one pins a patch version (introduced with the workflow inceb1d5f7f).ark-turn.ymltonode-version: "24", the policy every other workflow follows. Judgment call: if the24.21.0pin was deliberate, the policy-conformant repair is an explicit documented exception in the smoke (like the SQLite/authority22.22.3entry) rather than a silent patch pin — say the word and I will swap it.Validation
origin/mainworktree they fail with exactly the CI error strings above.full-public-smokes-workflow-smoke,frontstage-pages-workflow-smoke,desktop-release-workflow-smoke,docs-governance-smoke,todo-cli-smoke,todo-contract-smoke,work-lane-contract-smoke.loopx canary premerge --from-git-diff— 0 failures / 0 advisories (with the repository's TypeScript deps linked locally, whichsemantic-vocabulary-drift-smokeneeds).Boundary
loopx/help_surface.pyis the CLI help catalog (aloopx/**surface), so this is proposed as a PR with an exact-head review and left for the maintainer — never self-merged; the workflow pin and the two example smokes ride with it. No runtime, permission, state or protocol change.