docs(catalog): add IP-035 install ownership is not an update permission - #4657
Conversation
Add a State And Boundary entry for the case where the write target has a different owner than the caller: an install LoopX does not own. - name the classify-then-mutate rule over install_kind, owner, loopx_apply_supported, and owner_upgrade_command - pin the fail-closed unsupported_install_owner behavior: no pip guess, no git pull on a live checkout, no archive snapshot replacing a checkout - record the mirror-image smell of reporting success without writing - reference loopx/self_update.py::_install_lifecycle and the four existing mutation tests in tests/test_self_update_runtime_activation.py Refs GH-C37 Signed-off-by: Chumaniac <121109854+Chumaniac@users.noreply.github.com>
huangruiteng
left a comment
There was a problem hiding this comment.
Reviewed exact head: e179c61b90b3c1f4545f834297783ffeba60bb63 (docs/ip-035-catalog, docs only).
动机
Catalog 已经有「写作用域没被投影出来」(IP-006)、「按 revision 保护配置 apply」(IP-030)、「connector 运行时边界」(IP-028)、「manager context 不等于 turn 授权」(IP-031) 这几条,但缺一条最常见的边界:写入目标本身的 owner 不是调用方——也就是 LoopX 并不拥有的那份安装。缺少这条概念时,agent 看到 requires_upgrade=true 会直接挑一个自己最熟的安装器去改(pip install --upgrade loopx、pipx upgrade、git pull),结果是在另一个环境里装了第二份 LoopX、或者把 source checkout 静默换成了 archive snapshot。这个动机成立:填的是 catalog 里一个真实空缺,且与 #4656(GH-C37) 的既有验收描述一致。
改动思路
只加一条公开安全的 catalog 条目,复用已有的三段式结构,而不是新建文档或新 smoke:family 表新增一行、Pattern-To-Canary matrix 的 State And Boundary 行补上 IP-035、正文新增 #### IP-035 详情段(Trigger / Expected behavior 三条规则 / 与 IP-006、IP-030 的 sibling 边界说明 / mermaid 视觉模型 / Bad smell / Validation)。编号选择也说明清楚了:IP-034 属 #4641,尚未合并,所以本条取 IP-035,谁先落谁负责重编号。这个粒度是对的——issue 只要一个 good/bad case,PR 就只给一个 case,没有顺手改生产代码。
具体改动
1 个文件、+95/-1:
- matrix 行(第 91 行):State And Boundary 家族补
IP-035,并把 signal 列表加上install ownership。 - family 表行(第 346 行):
P1 | IP-035 | Install Ownership Is Not An Update Permission,user channel 写「不静默变更,报出所属安装器及其命令」,agent channel 写「先分类再变更;LoopX 不拥有时交还 owner 自己的命令,不切换安装通道」。 - 详情段(第 2423 行起):三条规则分别对应
install_lifecycle分类、loopx_apply_supported=false时的 fail-closed(ok=false、commands.apply=None、changes_applied=false、next_action.kind=use_installation_owner),以及交还owner_upgrade_command后停止;bad smell 里额外写了镜像错误——把apply=None渲染成「更新完成」。
我复核的关键点:
- 条目引用的生产标识确实存在:
loopx/self_update.py:706的next_action.kind=use_installation_owner、:967的execution.status=unsupported_install_owner,以及_install_lifecycle里的install_kind/owner/loopx_apply_supported/execution_driver分类。 python examples/interaction-pattern-catalog-smoke.py→ ok;python examples/docs-governance-smoke.py→ ok。pytest tests/test_self_update_runtime_activation.py -q→ 20 passed, 1 skipped,fail-closed 的负例(commands.apply is None、commands.owner_upgrade is None、next_action.command is None、无pip install猜测、无git pull、changes_applied is False)都在。- 结构检查确实覆盖了这条目:一个
IP-\d{3}一行、一个对应#### IP-035详情标题、无孤儿详情、无 id 跨家族重复。
遗留问题(非阻塞,P3)
条目正文点了两个机器可见标识符(next_action.kind=use_installation_owner、状态 unsupported_install_owner),但没有任何测试断言它们。我在这个 head 上全仓搜索这两个字符串,命中只有生产实现本身和这条 catalog 条目:tests/test_self_update_runtime_activation.py 断言的是 fail-closed 的 payload(apply、owner_upgrade、next_action.command 为 None,改动未生效),Windows 分支断言的是 execution.status == "unsupported_platform",都不是 unsupported_install_owner。也就是说,行为有护栏,但条目点名的两个字段名一旦被改名或写错,不会有测试失败。
最小修法(任选其一):在条目里直接写出生产行号(loopx/self_update.py:706 / :967),或给 tests/test_self_update_runtime_activation.py 加一条对 next_action.kind 与 unsupported_install_owner 状态的断言。这不阻塞合并:catalog 是散文,结构 smoke 本来就不校验每条断言的真假,而缺口已在 PR body 里如实披露。
对主干的风险
纯文档,无运行时行为、无权限边界、无持久化契约改动,回退成本就是一个 commit。catalog 是 agent 读的机器可读概念面,所以唯一实质风险是「条目说了不成立的话」,而我核对的两处标识符与失败关闭路径都成立。另两个已披露的残余:mermaid 只做了文本校验、未做视觉校验;「没有 owner 命令时应当……」(next_action.command=None)的 CLI 级断言被明确记为 gap 而非在本 PR 里补。编号依赖 #4641 的 IP-034,PR body 已写明谁先落谁负责对齐。
我的整体评价
结论 APPROVE。这是一个粒度正确、可独立评审、公开安全的文档增量:填了 State And Boundary 家族里一个真实空缺,完整接进了 family 表与 matrix,引用的生产事实我逐条复现(含两个标识符与 fail-closed payload),两个相关 smoke 在该 head 上通过,测试未改动。唯一发现是上面那条 P3——条目点名的两个标识符没有测试护栏——修法很小,且不构成合并阻塞。
English verdict: APPROVE - exact head e179c61; docs-only IP-035 catalog entry, docs-governance-smoke and interaction-pattern-catalog-smoke ok, and the entry's cited production facts reproduce (self_update.py:706 use_installation_owner, :967 unsupported_install_owner, plus the fail-closed payload pinned by 20 passing tests in tests/test_self_update_runtime_activation.py). One non-blocking P3 finding: the two machine-visible identifiers the entry names are asserted by no test, so the minimal repair is to cite the production lines in the entry or add one assertion.
Goal And Delivered Outcome
interaction pattern catalog. The catalog had entries for missing write-scope
projection (IP-006), revision-guarded configuration apply (IP-030), connector
runtime boundaries (IP-028), and manager-context retention (IP-031), but no
entry for the case where the write target itself has a different owner than
the caller: an install LoopX does not own.
loopx/self_update.pyalreadyclassifies
install_kind/owner/loopx_apply_supportedand fails closedwith
next_action.kind=use_installation_owner, andtests/test_self_update_runtime_activation.pyalready pins the mutationcases, but no concept told a controller or agent that
requires_upgrade=trueis not permission to pick an installer.
catalog has no IP whose agent channel says "classify the install, then hand
back the owner-owned command"; after,
IP-035 Install Ownership Is Not An Update Permissionstates the classify-then-mutate rule, the fail-closedunsupported_install_ownerbehavior, and the mirror-image smell of reportingsuccess without writing. The
staticandreal_entrypointrows below provethe entry is structurally complete (row, detail heading, single-family
listing) while leaving production behavior untouched.
Closes #4656(GH-C37), basemain.Scope And Continuation
entry with its family-table row, its Pattern-To-Canary matrix listing, a
mermaid visual model, and validation references. Docs only; no production or
test change, so no runtime behavior can regress.
IP-034is proposed in docs(catalog): add IP-034 unstaffable team lane is a typed gap #4641 and was stillopen when this branch was cut, so this entry takes
IP-035; if docs(catalog): add IP-034 unstaffable team lane is a typed gap #4641 landsfirst the numbering is already consistent, and if this lands first docs(catalog): add IP-034 unstaffable team lane is a typed gap #4641 owns
renumbering its own entry. The only identified successor is a possible
CLI-level assertion for the "no owner command exists" case, which is recorded
as a gap rather than built here.
Validation
e179c61b9(branchdocs/ip-035-catalog)staticpassedpython3 examples/interaction-pattern-catalog-smoke.py→interaction-pattern-catalog-smoke: ok. Structural check asserts one table row perIP-\d{3}, a matching#### IP-035detail heading, no orphan detail, and no id listed under two families.real_entrypointpassedloopx check --scan-path docs/concepts/interaction-pattern-catalog.md→ok: True,errors=0,checks=3,public boundary scan clean: 1 files.unitnot_runtests/test_self_update_runtime_activation.pyare cited as the independent oracle for the entry's claims and were not modified.structural smoke plus the repository contract check cover it end to end. The
two
warningsin theloopx checkoutput (registry file does not exist)are environmental, come from a checkout without
.loopx/registry.json, andare unrelated to this change. Untested path: the mermaid diagram is rendered
by the docs pipeline and was verified only as text, not visually. Follow-up
gap identified above: no CLI-level assertion yet exists for an install whose
owner command is
None.Frontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
incubator; GH-C37 interaction-model catalog curation.
Shared-authority RFC fixture impact
Boundary Checklist
.loopx/,.codex/goals/, and liveACTIVE_GOAL_STATE.md).none.Signed-off-bytrailer (git commit -s).