fix(smokes): restore the required public smoke fleet on main - #4260
Conversation
Two required public smokes fail on current `main`. `cli-command-module-size-ownership-command-modularization-smoke.py` reports `project_lifecycle.py has 1007 lines, above budget 1000`. The file crossed the budget when loopx-project#4169 added the mutually exclusive external-sink delivery flags. Extract the three typed inline input codecs into `project_lifecycle_inputs.py`, which returns the owner to 925 lines without changing any public invocation. `cli-help-manpage-smoke.py` reports `unclassified: ['agent-context']`. The command shipped in loopx-project#4244 without a manpage class. Add it to the existing `MANPAGE_COMMAND_HELP_ONLY` set, which is where comparable read-only lifecycle helpers already live. Extraction keeps the existing ownership contract: the registration and dispatch markers asserted by `cli-project-lifecycle-command-modularization-smoke.py` stay in the module, and `PROJECT_LIFECYCLE_COMMANDS` still covers all four commands. Validation: - `python3 examples/cli-command-module-size-ownership-command-modularization-smoke.py` -> ok - `python3 examples/cli-help-manpage-smoke.py` -> ok - `python3 examples/cli-project-lifecycle-command-modularization-smoke.py` -> ok - `python3 regression/cli-command-module-contract.py` -> ok - `python -m pytest tests/cli_commands/ tests/control_plane/test_cli_output_budget.py` -> 94 passed Signed-off-by: song <liusongstep@gmail.com>
Four required public smokes assert contracts that have since moved. Each is reproducible on a clean `main@fa57253`, so this aligns the checks with the shipped behavior rather than changing any product path. - `blocker-push-runtime-smoke.py` asserted the retired per-shell phrasing `` `LOOPX_TURN=<current_time_iso>`; reuse. ``. loopx-project#4201 moved the bootstrap rule into the shared `HEARTBEAT_TURN_BOOTSTRAP_RULE`, whose current sentence ends with `reuse the value on retries`. Assert that sentence. - `install-local-smoke.py` required the accountable refresh and spend commands inside the `--brief` prompt, but brief mode renders exactly one bounded guard block by design; those commands belong to the full and compact modes. loopx-project#4201 already realigned the adjacent thin-mode assertions and missed this one. Assert the brief contract, including that the pair stays out. - `github-actions-runtime-smoke.py` rejected the `22.14` SQLite runtime and the Node 26 forward job, and required the pre-loopx-project#4241 `merge-gate` needs order. Record `SQLITE_NODE_VERSION` with its finalization rationale, extend the `python-tests.yml` expectation, and match the current needs list. - `repository-hygiene-smoke.py` fails because the `v1.0.3` tag exists without a timeline entry. Add the entry, following the existing format. Validation (each command exits 0): - `python3 examples/blocker-push-runtime-smoke.py` - `python3 examples/install-local-smoke.py` - `python3 examples/github-actions-runtime-smoke.py` - `python3 examples/repository-hygiene-smoke.py` - `python3 examples/release/release-readiness-doc-smoke.py` - `python -m pytest tests/control_plane/test_heartbeat_notification_rule.py tests/control_plane/test_heartbeat_prompt_support.py tests/control_plane/test_heartbeat_receipt.py tests/control_plane/test_heartbeat_recommendation_rules.py` -> 48 passed Signed-off-by: song <liusongstep@gmail.com>
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion
审阅绑定 exact head:48b029bafdad844e532d7ed33db5094e2cc260d0。结论:APPROVE。
动机
这个 PR 处理的是当前 main 上 Required Full Public Smokes 的成组失真:五个 shard 会被六项已经过时的断言或预算打红,导致真正的新回归与已知的测试漂移混在一起。最关键的一项不是简单放宽限制,而是 project_lifecycle.py 在 #4169 后从 1000 行预算长到 1007 行;其余问题分别来自已发布但未分类的 agent-context、#4201 后新的 Turn bootstrap/brief prompt 结构、#4241 后的 Node/merge-gate workflow,以及已经发布但未进入 timeline 的 v1.0.3。
我在 clean baseline fa57253a7888a33eb98b3b6d49d69726f54e5034 上独立复现了对应失败,再在当前 head 上验证修复。这里选择“不合入”的最强理由是:更新 smoke 很容易把真正的语义漂移误写成“新事实”。本轮逐项回到实际 owner 核对,因此没有把失败一概改成宽松匹配,也没有提高 hot-path 或模块预算。
改动思路
运行时部分把三个已有的纯输入适配器——inline vision、typed progress 与 strict usage JSON——原样抽到 project_lifecycle_inputs.py,而命令注册、dispatch、effect、错误输出与 refresh_state_run 仍由 project_lifecycle.py 负责。这样把主 owner 降到 925 行,同时避免新增第二套状态决策。
其余改动均落在既有 contract owner:agent-context 进入 MANPAGE_COMMAND_HELP_ONLY;blocker prompt smoke 对齐共享的 HEARTBEAT_TURN_BOOTSTRAP_RULE;install smoke 明确 brief 只包含 bounded guard,并由显式 compact continuation 提供完整 settlement;workflow smoke 记录两个 SQLite 22.14 job、Node 26 forward job 与当前 merge-gate dependency;release readiness timeline 补齐已发布的 v1.0.3。正向路径恢复 required smoke 信号,负向路径仍会对非法 vision/progress/JSON、未知 workflow 版本或未分类命令失败。
具体改动
完整 diff 为 7 个文件、+141/-103:3 个 production Python 文件、3 个 required smoke、1 个 release-readiness 文档。两个 commit 均带 DCO sign-off。
关键代码讲解
project_lifecycle_inputs.py:25的inline_agent_vision_packet保留原有约束:inline vision 必须有--agent-id且必须至少包含一个 patch 字段;Todo delta 或 state 不能单独绕过它。project_lifecycle_inputs.py:62的inline_progress_observation继续复用ProgressResultClass,并保持exploration_exhausted/no_followup的 coverage-scope 约束与 exhausted-complete 约束;project_lifecycle.py:522-539只有单一导入/调用路径。install-local-smoke.py:828不再要求 brief 内嵌 refresh/spend;它同时断言 quota guard、Turn bootstrap、compact continuation 和 settlement pair 的缺席,避免把 brief 误当完整执行契约。github-actions-runtime-smoke.py:21-81把 22.14 限定为python-tests.yml的两个 SQLite 运行时,其他 workflow 仍只允许 Node 24;这不是把任意 Node 版本加入白名单。help_surface.py:305将 read-onlyagent-context放入 command-specific help-only 集合,与它的实际 audience/authority 一致;release-readiness.md:565的 v1.0.3 tag、时间和 PR 内容与已发布 release 回读一致。
对主干的风险
没有发现 blocking finding。最强风险是 helper extraction 表面机械、实际却可能漏传一个 CLI 字段;为避免只依赖 source/line-count smoke,我在 base 与 head 都运行了真实 disposable registry/runtime 路径:goal-vision-refresh-state-budget-smoke 与 delivery-semantics CLI 测试两边均通过,invalid vision/progress 与 NaN/Infinity 仍在写入前拒绝。exact head 另外通过 8 个目标 smoke、完整 install-local-smoke、94 个 CLI/output-budget tests、48 个 heartbeat tests、Ruff 与 diff check;25 个 hosted checks 全部成功。
回归敏感性也成立:baseline 明确复现 module-size、help classification、blocker prompt、install brief、workflow Node/dependency 和 v1.0.3 timeline 六项失败,head 对应全部转绿。v1.0.3 tag 解析为 0496975e46e3c8ebd87c8b2bc1eed59b3fa4e3c7,远端 release 也包含 timeline 中列出的 PR。
非阻塞的合入前条件:GitHub 当前显示 BEHIND。它与最新 origin/main 的 git merge-tree --write-tree 无冲突,且 main 的两个后续 commit 没改本 PR 的七个路径;但 branch update 会产生新 head,仍需重新跑 exact-head review/readiness,不能沿用本批准直接 merge。
我的整体评价
APPROVE。这是一个范围合理的 CI 信号修复:唯一 production 重构通过窄模块抽取解决真实预算违例,其他修改都回到已有 owner 对齐已发布事实,没有放宽预算、删除 gate、改变默认行为或新增状态/权限语义。future-facing pass 已落实在将 argument translation 与 command/effect owner 分开;再抽象一层通用 codec framework 反而会增加维护成本。剩余风险只在 branch update 后的 exact-head freshness,应由重新资格检查关闭。
English verdict: APPROVE exact head 48b029bafdad844e532d7ed33db5094e2cc260d0. The PR restores six stale required-smoke contracts without loosening their budgets: the only runtime change is a behavior-preserving extraction of existing vision/progress/strict-JSON input adapters, while prompt, help, workflow, and v1.0.3 timeline assertions are reconciled with their authoritative shipped owners. Clean-base reproduction covered all six failures; exact-head validation passed the targeted and install smokes, 94 CLI/output-budget tests, 48 heartbeat tests, real-path vision/progress parity, Ruff, diff check, and all 25 hosted checks. The branch is currently behind but merge-tree-clean; any update creates a new head and must be requalified before merge.
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
Signed-off-by: song <liusongstep@gmail.com>
|
已接入 main 独立复核确认三个 CLI 输入 helper 与抽取前 AST 一致、生产调用点完整。144 项 CLI/control-plane、43 项 workflow 测试、10 项 focused smoke、mypy 与最终 head canary 18 项全部通过。未来向调整限制在既有 CLI 输入边界和单一版本解析规则;无新增设置、API 或权限,前端/Lark无需额外入口。 原有 heartbeat 输出预算问题仍明确保留在 PR 边界说明中,未提高预算或声称整个 smoke fleet 全绿。新 head 需重新完成托管 CI 与维护者复审,不沿用旧 head 的批准。 四个更新后的 PR 已在 main |
huangruiteng
left a comment
There was a problem hiding this comment.
审阅对象:PR #4260,exact head ad00e427495861c4cf98ead428bffbeeb90ed0fc(作者 songoow),base 84334f7,合并提交 ad00e42。本次审阅该 exact head 的完整增量(7 files, +148/-104),并在 base 上复现了它声称修复的红项。
动机
这个 PR 修的是"公开 smoke 已经和实际发货的契约脱节"。具体来说,本仓库里承载回归信号的公开 smoke 面(examples/run-smokes.py 跑的 public smokes,以及 canary 的 quality surface catalog)在 main 上变红了:
examples/github-actions-runtime-smoke.py断言 workflow 只声明 {24, 22.6, 26},但 #4259 之后 SQLite 资格校验 lane 明确钉了 22.14,于是 smoke 报AssertionError: ('python-tests.yml', ['24', '22.14', '22.14', '22.6', '26', '24', '24', '24', '22.14', '24'])。examples/blocker-push-runtime-smoke.py仍在断言已退役的 per-shell 措辞,而 bootstrap 规则早已由共享文本提供。examples/install-local-smoke.py仍在断言 brief 模式的 refresh/spend 配方,而 brief 模式已被有意移除该配方(docs/heartbeat-automation-prompt.md有明确说明),断言的刷新/结算顺序不再存在。
另外还有两处契约面遗漏:agent-context(#4244 新增命令)没有进入 MANPAGE_COMMAND_HELP_ONLY 这一"刻意只做 manpage 曝光"的决策集合;docs/product/release-readiness.md 缺少 v1.0.3 时间线条目,而 repository-hygiene-smoke.py 的 validate_release_timeline() 要求每个已存在 tag 都有条目。最后,loopx/cli_commands/project_lifecycle.py 本身就超出了模块体积预算。
因此这个 PR 的价值不是"把红变绿",而是让公开契约面重新描述真实发货行为——这在一周内已经被证明是必要的:本 lane 之前连续两轮把浏览器验收的 fixture 缺陷误读成别的 PR 引入,根源正是"已知红的信号面"。
改动思路
- 模块体积:把三个纯输入编解码 helper(
inline_agent_vision_packet、inline_progress_observation、reject_non_standard_json_constant)连同INLINE_VISION_FIELDS常量从命令 owner 里搬到一个同类职责的兄弟模块project_lifecycle_inputs.py,沿用该目录里已有的project_lifecycle_sinks.py模式;调用点把局部变量改名为inline_vision_packet以避免与新函数名遮蔽。搬迁是逐字的,连解释--usage-json严格 JSON 的注释也保留。 - workflow 断言:新增
SQLITE_NODE_VERSION = "22.14",把 node-version 正则提成命名 pattern,并改为 lane 感知断言——kernel-static-checks、dashboard-acceptance、windows-powershell三个 lane 必须各自声明 22.14;同时把 merge-gate 的依赖断言更新为包含checks(对应 #4259 的 job 拆分)。 - prompt/brief 断言:blocker smoke 改断言当前共享 bootstrap 句子;install-local smoke 改成断言 brief 契约本身——必须包含 guard 命令与共享 bootstrap 规则,且不包含 refresh/spend 配方(即从"顺序断言"改为"存在 + 有意缺席"断言,方向上更严格)。
- 契约面补齐:
agent-context进入 manpage 决策集合;release readiness 文档补 v1.0.3 条目(与 tag0496975e对齐)。
具体改动
loopx/cli_commands/project_lifecycle.py(-93,缩至 925 行)与新模块 loopx/cli_commands/project_lifecycle_inputs.py(+99);loopx/help_surface.py(+1);examples/github-actions-runtime-smoke.py(+13/-3);examples/blocker-push-runtime-smoke.py(+3/-1);examples/install-local-smoke.py(+8/-7);docs/product/release-readiness.md(+13)。
关键点讲解:
- 搬迁等价性:我用 AST 对 base 定义与新模块定义做了逐函数比对(
ast.unparsediff),三个函数的差异只有函数名的下划线这一处;INLINE_VISION_FIELDS一并搬走,原文件不再保留任何私有定义,仓库内也没有其它模块引用这三个私有名(因此无需兼容包装)。校验分支(缺--agent-id、缺 patch 字段、NaN/Infinity、progress result-class 与 coverage 约束)与错误文案全部不变。 - workflow smoke 的 lane 断言:把整文件版本集合断言升级为"逐 lane 必须声明 22.14",因此未来任何 lane 掉 pin 都会指名失败;同时断言 merge-gate 依赖列表包含
checks,让 #4259 的拆分不会悄悄失去 dashboard lane 的门禁。 - brief 断言:
render_brief_heartbeat_task_body的返回值只包含共享 bootstrap 规则与 guard 命令,文档亦记录"有意移除 brief 固定结算配方",所以新断言(存在 guard、缺席 refresh/spend、含LOOPX_TURN=<current_time_iso>与"not a command-prefix assignment")是描述契约而非迁就输出。 - 时间线条目:v1.0.3 的行与 tag
0496975e(提交时间 2026-09-11T11:53:42+08:00)一致,格式与相邻条目相同;repository-hygiene-smoke.py的规则要求"每个 tag 都有条目",本 PR 合并时缺失的正是 v1.0.3(v1.0.4/v1.0.5 的 tag 是 9/15、9/16 才出现的,之后由各自的发布 PR 补条目)。
对主干的风险
这个 PR 的风险集中在"改断言是不是在掩盖回归"。逐条给出反证:
- 红项复现:我在 base(84334f794)实际跑出两条红——workflow smoke 的 22.14 断言失败、blocker smoke 的退役措辞断言失败;在 head 上两者都打印
ok。也就是说修复方向是"把红修绿",而不是"把绿放松"。 - 断言来源是契约而非输出:22.14 是 smoke 自己注释说明的 SQLite 资格运行时、且与 workflow 三个 lane 的实际 pin 一致;blocker 断言改用的是共享 bootstrap 规则句子;brief 断言与渲染函数、prompt 指南一致;时间线条目与 tag 一致。四者都能指向 owner,而不是"照抄当前输出"。
- 搬迁无行为变化:AST 比对显示仅函数名变化,调用点唯一且已更新,无遗留私有定义、无其它引用者,因此不存在"搬走一半"或双份定义的漂移。
- 注意方向更严格:brief 与 workflow 断言的变化都让未来漂移更容易被抓住(要求 guard 存在 + 配方缺席;要求每个 lane 各自 pin)。
残余风险与未验证项如实标注:本轮未完成 examples/install-local-smoke.py 的本地实跑(它是真实本地安装 + canary 场景,启动后未产出输出即结束),因此该文件里 brief 断言的证据是渲染函数与 prompt 指南,而不是该场景的执行结果;help-surface smoke 也未在本地执行。另有一个与本 PR 无关、可选的非必需后续项:严格 JSON 常量守卫现在在两处存在(新模块与既有的 loopx/status_server.py,后者消息不同并额外递归校验有限数),本 PR 并未引入该重复。
本地验证:head 上 python examples/github-actions-runtime-smoke.py -> github-actions-runtime-smoke ok: Node 24 primary, 22.6 minimum, 26 forward;python examples/blocker-push-runtime-smoke.py -> blocker-push-runtime-smoke ok;base 上两者均失败(信息见上)。hosted:该 head 27/27 检查全绿。
我的整体评价
APPROVE。这是一次克制的契约面修复:把两个真实变红的公开 smoke 恢复到描述当前发货契约(并让断言更具体、更严格),把 brief 模式断言改为与渲染函数和文档一致的"存在 + 有意缺席"形式,补齐 manpage 分类与 v1.0.3 时间线条目,并把超预算的 CLI 模块按既有兄弟模块模式拆出三个逐字搬迁的输入编解码 helper。没有任何生产行为变化,没有任何断言被删除,base/head 的对照复现给出了"修复目标确实是红项"的直接证据。
无阻断发现。建议(非阻断):(1) 若后续要处理严格 JSON 常量的双份实现,可在不改消息契约的前提下收敛为一个共享 helper;(2) install-local-smoke.py 这类真实安装 smoke 建议在 review 时保留执行输出,便于审计留下"确实跑过"的凭证。作为合并后审计记录,本结论仅针对该 exact head,不授予任何合并、回滚或再次修改的权限。
English verdict: APPROVE - #4260 at exact head ad00e42 (community contribution by songoow) restores the drifted public smoke contracts without weakening any guard: it moves the three inline lifecycle input codecs token-identically into loopx/cli_commands/project_lifecycle_inputs.py (AST comparison shows only the function name changed; no leftover private definitions and no other importers), classifies agent-context in the existing manpage-only decision set, makes the workflow smoke lane-aware for the 22.14 SQLite pin and asserts the merge-gate checks dependency, realigns the compact-prompt and brief-mode assertions with the documented contracts (brief must contain the guard command and intentionally omit the refresh/spend pair), and adds the v1.0.3 release-timeline entry matching tag 0496975e. Verified locally: github-actions-runtime-smoke.py and blocker-push-runtime-smoke.py both fail at the base commit 84334f7 with the drift messages and both print ok at this head, hosted checks are 27/27 green, and the timeline rule in repository-hygiene-smoke.py is satisfied for the tags that existed at merge time. No blocking finding; limits are that the real-install install-local-smoke.py was launched but produced no output in this audit (its brief assertions were verified against the renderer and the prompt guide), the help-surface smoke was not run locally, and an optional non-required follow-up is the strict-JSON-constant guard that exists in two modules with different messages (pre-existing, not introduced here).
Behavior
Restore required public smoke contracts after CLI growth and workflow changes: extract three unchanged inline input codecs from the oversized project lifecycle CLI module, classify
agent-contextin the existing manpage help-only set, align bootstrap/brief-mode assertions, and add the missing v1.0.3 release-timeline entry.Workflow validation permits the qualified SQLite runtime and Node forward lane. It checks the required kernel, Dashboard and Windows runtime pins per job instead of counting jobs; comments cannot satisfy a runtime pin. The workflow itself is unchanged by this PR.
Validation
Scope
Includes main
84334f794and its split qualification jobs. Existing CLI arguments, validation errors and runtime behavior are preserved. No frontend/Lark/configuration companion is needed: the input adapter calls the unchanged underlying lifecycle owner and introduces no new setting or API.The future-facing pass narrows the existing CLI input boundary and reuses one anchored version parser. It does not raise the unrelated heartbeat output budget:
hot-path-interface-budget-smoke.pyremains an existing main issue (3825 versus 3600), outside this repair. No full-fleet success is claimed from the focused checks.Hosted CI and maintainer review must qualify the updated head; the earlier approval does not automatically qualify later commits.