Add typed Goal lifecycle action catalog - #4132
Conversation
|
Post-review validation update:
Manual hold: repository review policy still requires a maintainer merge. The current author attempted the authorized admin merge but does not have |
huangruiteng
left a comment
There was a problem hiding this comment.
结论:请求修改(exact head dadb1f7ae6a0a46defaaa31314b8b957ffb9c8f9)
动机
这个 PR 为 cockpit/CLI 投影 Goal 的可执行 owner actions:active Goal 给 stop,stopped Goal 给 resume,controller 状态再附带 approve/reject/defer,并用 registry fingerprint 防止用户点击旧生命周期动作后覆盖新状态。把合法动作集合放到 TypeScript control plane、让 Python 只负责读取 registry 与执行现有 service,是符合当前 authority 迁移方向的;显式 freshness fence 也比 UI 自己猜合法 transition 更可靠。
改动思路
goal-actions 先调用 status 推断是否存在 controller gate,再由 Python adapter 解析 authoritative source registry、计算 SHA-256 fingerprint,并把 activation state 与 gate flag交给 projectGoalOperatorActions。TypeScript reducer生成有限 action catalog;生命周期 action 回到 goal-lifecycle --expected-state-fingerprint ... --execute,Python 在锁前和锁内都校验 registry bytes,最后沿既有 source→global route 写入并 read back。类型化状态、domain-neutral 文案和“requires_confirmation”义务表达总体清楚;默认未调用 goal-actions 时没有行为变化。
具体改动
关键代码讲解
projectGoalOperatorActions:拥有 active/stopped 到 stop/resume 的合法映射,并按 controller flag 生成有限 gate actions。build_goal_action_catalog:解析 source authority、计算 fingerprint,并校验 TS 返回的 schema/actions shape。set_goal_activation_state(expected_state_fingerprint=...):在写锁前后检查 source registry,拒绝 stale action。handle_goal_actions_command:把 status 的 waiting kind 适配成 operator-gate 是否可见的输入。test_projected_lifecycle_action_rejects_stale_registry_without_writing:验证 registry 变化后 lifecycle 写入被拒绝。
变更为 8 个文件、约 596 行;新增 CLI、typed reducer、adapter 和 focused regression 是一个完整 stage,而不是纯脚手架。
对主干的风险
[P1] catalog 中的 execution argv 没有绑定生成它的 registry,且 gate actions 根本没有 freshness fence
lifecycleAction 返回的 argv 从 loopx goal-lifecycle ... 开始,没有 --registry / --runtime-root,但 goal-actions 本身允许针对任意显式 registry 生成 catalog。真实 client 逐字执行返回 argv 时会落到 ambient/default registry,而不是生成 fingerprint 的 authority;当前测试在 _run_cli(registry_path, *argv[1:]) 中额外偷偷补回 registry,所以没有验证公开 execution contract。更严重的是 gateAction 虽然和 catalog 共用 state_fingerprint,执行 argv 既不携带 fingerprint,也没有 exact gate identity,只会调用默认 read_only_map_opt_in;状态或待决 gate 在投影后变化,旧 approve/reject/defer 仍可写入。于是 catalog 所称的“fresh typed owner actions”只对 lifecycle action 部分成立。
最小修复有两个可接受方向:要么 v1 只发布已绑定的 lifecycle actions并暂时移除 gate actions;要么把 authoritative registry/runtime locator、exact gate id 和 expected decision fingerprint 纳入 typed request/action/CLI,执行端在同一 authority 锁下拒绝 stale gate。回归应从一个非默认 registry 生成 catalog,然后不注入任何隐藏上下文地逐字执行完整 argv,并证明 registry 或 gate 状态改变后不会写入。
本地 6 个 focused pytest 与 Ruff 通过,git diff --check 通过;本地 worktree 缺少 Node type definitions,因此没有把本地 TS typecheck计为证据,远端 TypeScript/pytest/installed/e2e/mutants/merge-gate 均为成功。
我的整体评价
TypeScript 拥有 transition decision、Python 复用既有 activation service,以及锁内二次 fingerprint校验,都是值得保留的设计。future-facing pass 应收紧 action 的 authority/freshness envelope,而不是新增更泛的 command framework。当前公开 catalog 把“可执行 argv”作为 contract,却依赖调用方补一个未声明的 registry 上下文,并把未受 freshness 保护的 gate action 混在同一个 fresh catalog 中;这是会导致错 registry 或过期授权写入的语义缺陷,因此需修复后复审。
English verdict: REQUEST_CHANGES — make every executable action self-contained and authority-bound, and either freshness-bind exact operator-gate actions or omit them from this catalog.
22e4b4b to
97d331a
Compare
Signed-off-by: Huashuai Qu <256761+huashuai@users.noreply.github.com> Committed-By-Agent: codex Co-authored-by: codex <noreply@openai.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
97d331a to
a9fff9e
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
结论:批准(exact head a9fff9ec0c8160bd53fc8d1f581014232a057d9c)
动机
这个 PR 为 cockpit/CLI 提供一个由 TypeScript control plane 拥有的 Goal 生命周期动作目录,并让客户端能够逐字执行返回的 argv。原始实现的主要风险是:目录允许针对显式 registry 生成动作,但 lifecycle argv 没有携带该 registry/runtime 上下文,调用方只能暗中补参数;同时 approve/reject/defer 的 operator-gate argv 没有同等 freshness fence,旧 gate 可能继续写入。修复后,目录只发布已经具备完整 authority/freshness envelope 的 stop/resume 动作;gate 决策留在既有 operator-gate 命令路径,待未来补齐 exact gate identity 与决策点 freshness 后再纳入目录。这解决了本次可验证的错误 authority 和 stale lifecycle 写入问题,不扩大为任意命令执行或权限授权。
改动思路
入口仍是 registry_admin 注册的 goal-actions,Python adapter 读取请求 registry,经 activation_service._source_and_target 解析 source→global authority route,并对完整 source registry bytes 计算 SHA-256。projectGoalOperatorActions 在 TypeScript effect runtime 中校验 v2 request、Goal id、active/stopped 状态、registry locator 和 fingerprint,负责唯一的 active→stop / stopped→resume 合法映射。它生成包含 --registry、可选 --runtime-root、--format json、operation 和 --expected-state-fingerprint 的自包含 argv;不会写 registry。
执行 argv 回到既有 goal-lifecycle / set_goal_activation_state。该 service 在执行前检查 fingerprint,并在 source exclusive lock 内重新读取 bytes、再次检查 fingerprint 和 activation state,然后沿既有 sync/readback 路径写入。变化时返回 goal_action_stale、written=false,由 client 刷新目录重试。这个边界复用了现有 authority route、锁、atomic write、global sync 和 readback,没有新建第二个生命周期 writer。由于 operator-gate writer 还没有同等决策点 fence,本 head 明确不再从 status 推导并发布 gate 写动作。
具体改动
关键代码讲解
loopx/cli_commands/goal_actions.py:32新增只读 CLI adapter;它直接调用build_goal_action_catalog,失败时返回 boundedok=falsepayload,不触碰 registry。loopx/control_plane/goals/operator_actions.ts:85新增 v2 typed reducer。lifecycleAction将 registry/runtime locator、JSON 输出和 fingerprint 放入完整 argv;projectGoalOperatorActions只允许两种明确生命周期状态,gate action 不再混入同一 catalog。loopx/control_plane/goals/operator_actions.py:35将 requested registry 与解析出的 route 适配到 TypeScript effect,并验证返回 catalog/action shape;没有复刻 transition 规则。loopx/control_plane/goals/activation_service.py:206增加可选 expected fingerprint,在无锁预检和 source lock 内二次检查之间形成 stale/TOCTOU 防线;原有写入、同步和 readback owner 不变。loopx/cli_commands/goal_lifecycle.py:34接收并转发 fingerprint;registry_admin.py与effect_runtime_handlers.ts接入新 CLI/effect。tests/control_plane/test_goal_operator_actions.py覆盖 typed 正负路径、完整 argv、非默认 registry、stale 不写入和 stop→resume。
对主干的风险
没有遗留阻塞性 finding。新增 request schema 为 loopx_goal_action_projection_request_v2,因为 authority locator 现在是生成可执行 action 的必需输入;consumer-facing catalog schema 仍为 v1。直接调用既有 goal-lifecycle 时 fingerprint 仍是可选参数,因此默认路径保持兼容;新 goal-actions 本身是只读,只有明确带 --execute 的生命周期 argv 才会写入。
本地验证包括 59 个聚焦/相关 pytest、Ruff、Python 编译、TypeScript strip-types 语法检查、git diff --check,以及 loopx canary premerge --from-git-diff:12 个选中 checks 全部通过,0 failure、0 warning、0 manual hold。回归测试真正执行完整返回 argv,没有 helper 隐藏注入 registry,并验证 registry 变化后 goal_action_stale 且字节不变。远端 exact-head 的 DCO、依赖、构建、Python Tests(含 kernel/static、Node 兼容、dashboard、Windows、shards、e2e、mutants)需在合并前保持全绿;gate affordances 是有意的后续边界,不应通过恢复无 freshness 的 gate argv 来补齐。
我的整体评价
这是一个范围收敛后可合并的完整 stage:TypeScript 负责 typed lifecycle decision,Python 复用既有 authority/write owner,客户端拿到的 action 不再依赖未声明环境。删除 status-derived gate projection 比继续堆叠一个不完整 gate 协议更安全,也让目录的“fresh executable action”语义真实成立。exact head a9fff9ec0c8160bd53fc8d1f581014232a057d9c 的行为、负路径和主干对齐均已复核;剩余风险仅是 operator-gate 尚未进入该目录,后续应以独立的 exact gate identity/fingerprint 合同补齐。
English verdict: APPROVE — exact head a9fff9ec0c8160bd53fc8d1f581014232a057d9c binds lifecycle argv to the declared registry/runtime authority, rejects stale writes before and under lock, and removes unfenced operator-gate actions. Focused tests, local premerge canary, and the full remote required checks are green; no blocking finding remains.
huangruiteng
left a comment
There was a problem hiding this comment.
结论:批准(exact head a9fff9ec0c8160bd53fc8d1f581014232a057d9c)
动机
这个 PR 为 cockpit/CLI 提供一个由 TypeScript control plane 拥有的 Goal 生命周期动作目录,并让客户端能够逐字执行返回的 argv。原始实现的主要风险是:目录允许针对显式 registry 生成动作,但 lifecycle argv 没有携带该 registry/runtime 上下文,调用方只能暗中补参数;同时 approve/reject/defer 的 operator-gate argv 没有同等 freshness fence,旧 gate 可能继续写入。修复后,目录只发布已经具备完整 authority/freshness envelope 的 stop/resume 动作;gate 决策留在既有 operator-gate 命令路径,待未来补齐 exact gate identity 与决策点 freshness 后再纳入目录。这解决了本次可验证的错误 authority 和 stale lifecycle 写入问题,不扩大为任意命令执行或权限授权。
改动思路
入口仍是 registry_admin 注册的 goal-actions,Python adapter 读取请求 registry,经 activation_service._source_and_target 解析 source→global authority route,并对完整 source registry bytes 计算 SHA-256。projectGoalOperatorActions 在 TypeScript effect runtime 中校验 v2 request、Goal id、active/stopped 状态、registry locator 和 fingerprint,负责唯一的 active→stop / stopped→resume 合法映射。它生成包含 --registry、可选 --runtime-root、--format json、operation 和 --expected-state-fingerprint 的自包含 argv;不会写 registry。
执行 argv 回到既有 goal-lifecycle / set_goal_activation_state。该 service 在执行前检查 fingerprint,并在 source exclusive lock 内重新读取 bytes、再次检查 fingerprint 和 activation state,然后沿既有 sync/readback 路径写入。变化时返回 goal_action_stale、written=false,由 client 刷新目录重试。这个边界复用了现有 authority route、锁、atomic write、global sync 和 readback,没有新建第二个生命周期 writer。由于 operator-gate writer 还没有同等决策点 fence,本 head 明确不再从 status 推导并发布 gate 写动作。
具体改动
关键代码讲解
loopx/cli_commands/goal_actions.py:32新增只读 CLI adapter;它直接调用build_goal_action_catalog,失败时返回 boundedok=falsepayload,不触碰 registry。loopx/control_plane/goals/operator_actions.ts:85新增 v2 typed reducer。lifecycleAction将 registry/runtime locator、JSON 输出和 fingerprint 放入完整 argv;projectGoalOperatorActions只允许两种明确生命周期状态,gate action 不再混入同一 catalog。loopx/control_plane/goals/operator_actions.py:35将 requested registry 与解析出的 route 适配到 TypeScript effect,并验证返回 catalog/action shape;没有复刻 transition 规则。loopx/control_plane/goals/activation_service.py:206增加可选 expected fingerprint,在无锁预检和 source lock 内二次检查之间形成 stale/TOCTOU 防线;原有写入、同步和 readback owner 不变。loopx/cli_commands/goal_lifecycle.py:34接收并转发 fingerprint;registry_admin.py与effect_runtime_handlers.ts接入新 CLI/effect。tests/control_plane/test_goal_operator_actions.py覆盖 typed 正负路径、完整 argv、非默认 registry、stale 不写入和 stop→resume。
对主干的风险
没有遗留阻塞性 finding。新增 request schema 为 loopx_goal_action_projection_request_v2,因为 authority locator 现在是生成可执行 action 的必需输入;consumer-facing catalog schema 仍为 v1。直接调用既有 goal-lifecycle 时 fingerprint 仍是可选参数,因此默认路径保持兼容;新 goal-actions 本身是只读,只有明确带 --execute 的生命周期 argv 才会写入。
本地验证包括 59 个聚焦/相关 pytest、Ruff、Python 编译、TypeScript strip-types 语法检查、git diff --check,以及 loopx canary premerge --from-git-diff:12 个选中 checks 全部通过,0 failure、0 warning、0 manual hold。回归测试真正执行完整返回 argv,没有 helper 隐藏注入 registry,并验证 registry 变化后 goal_action_stale 且字节不变。远端 exact-head 的 DCO、依赖、构建、Python Tests(含 kernel/static、Node 兼容、dashboard、Windows、shards、e2e、mutants)需在合并前保持全绿;gate affordances 是有意的后续边界,不应通过恢复无 freshness 的 gate argv 来补齐。
我的整体评价
这是一个范围收敛后可合并的完整 stage:TypeScript 负责 typed lifecycle decision,Python 复用既有 authority/write owner,客户端拿到的 action 不再依赖未声明环境。删除 status-derived gate projection 比继续堆叠一个不完整 gate 协议更安全,也让目录的“fresh executable action”语义真实成立。exact head a9fff9ec0c8160bd53fc8d1f581014232a057d9c 的行为、负路径和主干对齐均已复核;剩余风险仅是 operator-gate 尚未进入该目录,后续应以独立的 exact gate identity/fingerprint 合同补齐。
English verdict: APPROVE — exact head a9fff9ec0c8160bd53fc8d1f581014232a057d9c binds lifecycle argv to the declared registry/runtime authority, rejects stale writes before and under lock, and removes unfenced operator-gate actions. Focused tests, local premerge canary, and the full remote required checks are green; no blocking finding remains.
What changed
goal-actionsCLI for cockpit clientsValidation
loopx canary premerge --from-git-diffpassed 12 selected checks with no failures or warningsBoundaries
Future-facing review
The nearest reusable boundary is the existing Goal activation service. This change adds a narrow typed projection and freshness fence there; a broader lifecycle refactor is unnecessary.