docs(catalog): add IP-032 completed-work archive with durable decision retention - #4438
Conversation
huangruiteng
left a comment
There was a problem hiding this comment.
动机
GH-C37 要求补一个 public-safe 的 catalog case,而"done todo 被归档之后,它携带的 durable standing decision 归谁管"这件事确实没有任何 catalog 条目解释:claim/supersede/successor 归 IP-020、decision 怎么写归 IP-014,谁都不管"离开活跃窗口之后"。所以新增一个 pattern 的方向是对的,条目正文(retention / role scope / preview 三条,以及和 IP-020、IP-014 的边界说明)我也读不出重复。
但这个 PR 选用的 id 是已经被占用的 IP-030,而且 head 与当前 main 在同一文件冲突,这两件必须先收口。
改动思路
在 catalog 的 State And Boundary 族新增一行 IP-030 Completed Work Archive With Durable Decision Retention,正文补一个完整条目(Trigger / Expected behavior / Mermaid / Bad smell / Validation),Pattern-To-Canary 矩阵里把该 id 加进 State And Boundary 并把 completed-work archive 加进触发面,然后在 examples/interaction-pattern-catalog-smoke.py 的期望列表里补五条字面断言把新条目钉住。
具体改动
docs/concepts/interaction-pattern-catalog.md(+86/-1):第 91 行矩阵 State And Boundary 行、第 343 行新增 pattern 行、第 2155 行新增#### IP-030 …详情段。examples/interaction-pattern-catalog-smoke.py(+5):新增五条期望字符串(pattern 行文本、Archive is a storage move, not a decision loss.、retained_standing_decision_count、The role defaults to \agent``、owning smoke 路径)。
关键代码讲解
- 第 343 行
| P1 | IP-030 | Completed Work Archive With Durable Decision Retention |:这是新分配的 id,但 main 上第 323 行的IP-030已经是Machine Configuration Preview And Revision-Guarded Apply(详情段在第 1192 行),也就是说同一个 id 现在指向两个不同 pattern。 - 第 91 行矩阵:State And Boundary 行新增
IP-030,而第 90 行 Human Decision 行本来就含IP-030(Machine Configuration Preview 属于该族),于是矩阵里同一个 id 同时挂在两个族下——id 的"身份"语义被破坏。 interaction-pattern-catalog-smoke.py:104-110:新增断言全是"子串是否存在",所以它能抓文本漂移(我用把行 id 改成 IP-999 的变异验证过,确实会失败),但抓不到 id 冲突或族归属错误,这正是这次问题静默通过的原因。
对主干的风险
阻塞项 1(P1,pattern id 重复)。我在 head 上跑 git show HEAD:docs/concepts/interaction-pattern-catalog.md | rg -o '^\| P[0-9] \| (IP-0[0-9][0-9]) ' -r '$1' | sort | uniq -d → 输出 IP-030;对照三个版本:merge base 6bb413105 的 id 空间是 IP-001..IP-030(那时 IP-030 就已经被占用,下一个空闲是 IP-031),origin/main 19a69abd6 已经到 IP-001..IP-031(IP-031 = Manager Context Is Not Turn Authority,它的行与详情段也占用了第 343/2155 行附近的位置)。最小修复:把新 pattern 改成下一个空闲 id——按当前 main 是 IP-032——并同步改第 343 行表格、第 2155 行详情标题、第 91 行矩阵行、以及 smoke 里那条 IP-030 | Completed Work Archive … 期望串。
阻塞项 2(P1,head 与 main 冲突)。git merge-tree --write-tree HEAD origin/main 返回 1,冲突就在 docs/concepts/interaction-pattern-catalog.md(mergeable=CONFLICTING、mergeStateStatus=DIRTY,对照 origin/main 19a69abd6)。main 侧新增的 IP-031 行/矩阵项与本次插入位置重叠,所以请 rebase 到当前 main 后再确认一遍 id 分配(rebase 之后 IP-031 也被占,新条目应为 IP-032),并在新 head 上重跑 interaction-pattern-catalog-smoke.py、examples/canary/catalog-planner-smoke.py、examples/docs-governance-smoke.py 与 loopx check --scan-path docs/concepts/interaction-pattern-catalog.md。
P2(catalog 结构没有机器检查)。本 head 的四项验证我都跑过且全绿(catalog smoke ok、catalog-canary-planner-smoke ok、docs-governance-smoke ok、boundary scan clean,变异探测也如 PR 所述会失败),但它们无法表达"id 唯一、且只属于一个族"。建议保留新增的字面断言,另加一条数据驱动检查:遍历 | P<n> | IP-0nn | 行断言 id 唯一、矩阵里每个 id 只出现一次、且每个表格行都有对应详情标题。这条检查在本次 head 上就会失败,正好补上缺口。
我的整体评价
REQUEST_CHANGES。条目本身是这次改动里最有价值的部分:archive-retention 这个语义空白真实存在,正文把 retention / role scope(--role 默认 agent)/ preview 三条讲清楚,并主动与 IP-020、IP-014 划清边界,验证命令我逐条跑过都通过。问题集中在 id 分配这一处:IP-030 在本 PR 的 merge base 上就已经属于 Machine Configuration Preview And Revision-Guarded Apply,选择它会让表格、详情段与 canary 矩阵同时出现"一个 id 两个 pattern、一个 id 两个族";同时本 head 与当前 main 在同一文件冲突(merge-tree 返回 1),而 main 侧的 #4433 已把下一个空闲 id IP-031 也占掉。把新条目改成 rebase 后的下一个空闲 id(当前 main 为 IP-032)、同步四处引用、并给 catalog 结构补一条唯一性断言,然后我按新的 exact head 再做一次复审。
English verdict: REQUEST_CHANGES at 81561c0. The new pattern entry fills a real gap and its three rules (durable decision retention, role scope with the agent default, preview-then-execute) plus its explicit separation from IP-020/IP-014 read correctly; the catalog smoke, the canary planner smoke, the docs governance smoke and the public boundary scan all pass at this head, and the claimed mutation check reproduces. But the entry allocates the id IP-030, which already denotes Machine Configuration Preview And Revision-Guarded Apply at the PR's own merge base (table line 323, detail line 1192), so the catalog now has two IP-030 rows, two IP-030 detail headings, and the canary matrix lists IP-030 under both Human Decision and State And Boundary. That was already true at the base - the next free id there was IP-031 - and current main has since taken IP-031 as well, so after rebasing the new entry should become IP-032. The head is also CONFLICTING/DIRTY against origin/main 19a69ab, with the merge conflict in exactly this file and region. Please rebase, renumber the pattern in all four places (table row, detail heading, canary matrix row, smoke expectations) and add an id-uniqueness assertion so the catalog's own structure is machine-checked; I will re-review the new exact head.
81561c0 to
7d21fcd
Compare
…n retention `loopx todo archive-completed` encodes three interaction rules that no catalog entry explains to humans: 1. a done todo carrying a durable standing decision is not a move candidate, and the decision must still resolve as active standing authority after the move (`retained_standing_decision_count`, `standing_decision_authority_v0`); 2. the archive only touches the section for the requested role, so `--role user` must leave `Agent Todo` alone while the role defaults to `agent`; 3. without `--execute` the command is a preview that must not change the state file. Today `examples/control_plane/todo-archive-completed-smoke.py` and `examples/control_plane/todo-standing-decision-authority-smoke.py` encode this behavior but nothing in the catalog names it, which is exactly the "a smoke encodes a behavior that is not yet explained to humans" case in the catalog maintenance rules. The entry explicitly separates itself from IP-020 (claim / supersede / successor lifecycle) and IP-014 (how a decision is written): neither owns what happens to a durable decision once the todo carrying it leaves the active window. Pattern-To-Canary matrix: IP-032 joins State And Boundary, and "completed-work archive" joins that family's trigger surfaces. The catalog smoke also gains a data-driven structural check: pattern ids own exactly one table row, every row has its detail heading (and vice versa), and no id is listed under more than one family in the Pattern-To-Canary matrix, so an id collision like the one this entry initially shipped cannot pass silently again. Rebased onto main f4ed58d; the entry is numbered IP-032 because IP-030 (Machine Configuration Preview And Revision-Guarded Apply) and IP-031 (Manager Context Is Not Turn Authority) are already taken on main. Validation: - python3 examples/interaction-pattern-catalog-smoke.py -> ok (now also enforces id uniqueness / family membership / detail-heading pairing) - python3 examples/canary/catalog-planner-smoke.py -> ok - python3 examples/docs-governance-smoke.py -> ok - loopx check --scan-path docs/concepts/interaction-pattern-catalog.md -> ok, errors=0, public boundary scan clean - mutation check: duplicating an id or listing it under two families now fails the new structural assertions Role isolation is documented as a future CLI-level smoke because it is still helper-level only; the gap is stated rather than hidden. Docs-only; no runtime, validation, or benchmark behavior changes. Signed-off-by: YZJF <195568136+YZJF@users.noreply.github.com>
7d21fcd to
aa929ac
Compare
|
已按 review 意见完成修复,请按新的 exact head
新 head 上重跑全部验证:catalog smoke ok、catalog-canary-planner-smoke ok、docs-governance-smoke ok、 |
|
关于当前 CI 全红的情况,我(作者)逐项核实过,这些失败都不是本 PR 引入的,而是 main 自带的,说明如下:
另外 main 分支自己最近的 CI 也有 failure 记录( 本 PR 自身的验证在 head |
huangruiteng
left a comment
There was a problem hiding this comment.
动机
上一轮我在 81561c00 上给了 REQUEST_CHANGES,三个问题都在这一版收口了:id 重复、与 main 冲突、catalog 结构没有机器检查。
条目本身要解决的空白是真实的:loopx todo archive-completed 已经带着一套明确语义(durable standing decision 的保留、--role 的作用域、preview-then-execute),但 catalog 里没有条目认领它——IP-020 管 claim/supersede/successor,IP-014 管决策怎么写,谁都不管"承载决策的 todo 离开活跃窗口之后"。缺少这条,agent 整理活跃 lane 时很容易把已完成工作当成可丢弃历史,于是两个 turn 之后重新去问一个用户已经回答过、或者重新翻烧一条已批准的策略。
这一版把新条目放在 State And Boundary 族,并明确写出它与 IP-020、IP-014 的边界,方向和我上一轮的判断一致。
改动思路
三处修复分别是:条目改号为 IP-032(当前 main 的 catalog id 空间到 IP-031,IP-032 是下一个空闲号);head 重基于 75fcd5556,与当前 origin/main(36c6d8df0)合并干净;interaction-pattern-catalog-smoke.py 里新增数据驱动的结构断言,把"id 唯一、一个 id 只属于一个族、表格行与详情标题配对"从人工 review 变成会失败的检查。
条目正文保持上一版的四段结构(Trigger / Expected behavior / Visual Model / Bad smell / Validation),把 retention、role scope、preview 三条规则逐条绑定到实际命令与 payload 字段,并保留对下游 smoke 的归属。
具体改动
docs/concepts/interaction-pattern-catalog.md(+86/-1):第 91 行 State And Boundary 矩阵行加入IP-032,触发面补completed-work archive;第 344 行新增 pattern 行;第 2259 行起新增#### IP-032详情段(84 行,含 mermaid 流程与 Validation 归属)。examples/interaction-pattern-catalog-smoke.py(+41):新增require_catalog_structure(第 34 行),第 206 行调用;第 141 行起补五条字面断言(pattern 行文本、Archive is a storage move, not a decision loss.、retained_standing_decision_count、The role defaults to `agent`、owning smoke 路径)。
关键代码讲解
docs/concepts/interaction-pattern-catalog.md:344—| P1 | IP-032 | Completed Work Archive With Durable Decision Retention | Archive selector plus controller | no interruption; preview-then-execute readback | ... |:表格行的 id、owner、中断语义与一句话规则齐全,IP-032在当前 main 上确实是空闲号(我按rg -o '^\| P[0-9] \| (IP-[0-9]{3}) \|'在origin/main上取到的最大号是IP-031)。docs/concepts/interaction-pattern-catalog.md:91— State And Boundary 行现在同时把IP-032放进族覆盖和触发面(completed-work archive),因此该 pattern 会进入该族的 project/boundary canary 覆盖,而不再只存在于详情段里。docs/concepts/interaction-pattern-catalog.md:2259—#### IP-032 Completed Work Archive With Durable Decision Retention:详情段把三条规则写成可核对的行为承诺,并且逐条能对上实现:--role在 CLI 帮助里写明 archive-completed 默认agent(loopx/cli_commands/todo_registration.py:57)、retained_standing_decision_count由loopx/control_plane/todos/completed_archive.py产生并在examples/control_plane/todo-archive-completed-smoke.py:202被断言、<!-- loopx:todo role={role} -->标记在loopx/control_plane/todos/completed_archive.py:151。最后一段还写了 IP-020/IP-014 的边界。examples/interaction-pattern-catalog-smoke.py:34—require_catalog_structure:用四条断言把结构规则机器化——表格 id 不可重复、每个表格行都要有详情标题、详情标题不能是孤儿、矩阵里每个 id 只能挂在一个族下且必须存在于表格中。它不是格式检查的堆砌:loopx/canary/planner.py的_catalog_pattern_rows会把重复 id 直接喂进 coverage audit(同一 id 两行会被重复计数而不是报错),所以这条规则此前确实没有 owner。examples/interaction-pattern-catalog-smoke.py:206—require_catalog_structure(catalog, source=CATALOG):挂在这个已有的 catalog smoke 上,该 smoke 被examples/run-smokes.py收录进.github/workflows/full-public-smokes.yml的每日全量 public smoke 扫(含 main push),并由smoke-fleet-health汇总 receipt,因此这条断言会持续跑而不是一次性脚本。
对主干的风险
我上一轮提的两个阻塞项都已关闭,且我独立复验过。 id 唯一性:head 上 rg 表格行 id 无重复,id 空间为 IP-001..IP-032;合并冲突:git merge-tree --write-tree HEAD origin/main 退出码 0(对照 origin/main 36c6d8df0),head 的 merge base 是 75fcd5556,即一个可 fast-forward 语义的增补分支。
P2(非阻断,文档准确性):第 2339 行 - Future smoke: role isolation is currently proven at helper level; a CLI-level assertion that --role user leaves Agent Todo byte-identical is proposed and not yet landed. 与本 head 的事实相反。这条 CLI 级断言已经落地:examples/control_plane/todo-archive-completed-smoke.py:147 的 assert_user_role_archive_retains_standing_decision(在 :469 被调用)用 --role user 跑真实 CLI,断言普通 done gate 被归档、todo_user_standing_approve 保留,并在 :219-227 明确检查 "compacting the user section must not move Agent Todo work"(todo_agent_completed 仍在、done_count == 1、位置在 ## Completed Work Archive 之前)。该断言随 #4436 进入 main,而本 head 基于其之后的 75fcd5556,所以这句话在同一个 head 上是自相矛盾的。风险不是运行时,而是 catalog 作为权威指引会把读者引向重复劳动(再开一个 smoke PR),并错报当前覆盖。最小修复是一行:改成引用 examples/control_plane/todo-archive-completed-smoke.py 的该断言作为 role isolation 的 owner,或删掉这条 bullet。这是 P2,不阻断合并;若作者选择在同一 PR 内修掉,就会产生新 head 并触发一次完整复审,这个成本留给作者判断。
P3(非阻断,新断言的一个盲点):require_catalog_structure 的族矩阵 cell 解析用的是 ^\| [^|]+ \| ((?:IP-\d{3}, )*IP-\d{3}) \|,要求 cell 恰好是逗号分隔的 id 列表。只要有人在 id 后面加注释(IP-032 (archived-work retention)),整行的 id 会被静默跳过而不是报错。我复现了:把 IP-032 (archived-work retention) 加进 Human Decision 行,smoke 输出 interaction-pattern-catalog-smoke: ok;而不带注释的同一重复会按设计失败(pattern ids listed under more than one family: ['IP-032'])。也就是说这条新规则的保证是"针对当前格式"而非"针对该语义"。最小修复是把 cell 解析换成 re.findall(r'IP-\d{3}', cell) 并额外断言计数一致,这样带注释的 cell 会被报告出来。同一次验证里我还确认另外三种变异都会失败:表格 id 重复、详情标题改名(孤儿+缺标题)、矩阵里出现不存在的 id,说明这条断言不是空转。
验证面(全部在 aa929ac39 上跑):interaction-pattern-catalog-smoke ok、canary/catalog-planner-smoke ok、docs-governance-smoke ok、条目引用的 control_plane/todo-archive-completed-smoke ok 与 control_plane/todo-standing-decision-authority-smoke ok、loopx check --scan-path docs/concepts/interaction-pattern-catalog.md 报 public boundary scan clean: 1 files。按本 lane 的 review 配置我不会去拉取或等待 GitHub CI,因此上述结论只基于本地仓库验证;本地必跑项没有失败或跳过。作者在同一 head 之外声明的"CI 全红与本 PR 无关"我未复核,也不作为我的结论依据。
我的整体评价
APPROVE。这一版把上一轮的两个 P1 和一个 P2 都真正处理掉了:IP-032 在当前 main 上是空闲号且与 main 无冲突,新增的结构断言对四种结构破坏有实际杀伤力(我逐条做过变异验证),并且挂在每日全量 public smoke 上而不是一次性脚本。条目正文我没有只读一遍就放过,而是把三条规则逐条对回实现:--role 默认 agent 有 CLI 帮助背书,retained_standing_decision_count 有 payload 字段与 owning smoke 断言背书,role marker 与 Completed Work Archive 节名有代码常量背书;IP-020/IP-014 的边界划分与"archive 是存储迁移、不是决策丢失"的核心表述也都读得通。剩下的两个问题都不阻断:P2 是新增 bullet 与本 head 事实相反(一行即可修正),P3 是新断言对带注释 cell 静默跳过。二者都不改变这条 pattern 对读者的价值判断,所以我给 APPROVE,并建议经手时顺手收掉 P2。
English verdict: APPROVE — exact head aa929ac39f16bfcb31237e963c3c23cb7a362d63 of #4438. The rebased head renumbers the new pattern to IP-032 (current main tops out at IP-031), merges cleanly with origin/main 36c6d8d (git merge-tree exits 0), and adds a data-driven require_catalog_structure assertion to examples/interaction-pattern-catalog-smoke.py:34 that I verified has teeth: duplicate table id, cross-family duplicate, renamed detail heading and unknown matrix id each fail with the expected assertion, while the unmutated head passes. Independent validation at this head: catalog smoke ok, canary catalog-planner smoke ok, docs-governance-smoke ok, and the two smokes the entry cites as owners (todo-archive-completed-smoke, todo-standing-decision-authority-smoke) both ok, with loopx check --scan-path docs/concepts/interaction-pattern-catalog.md reporting a clean boundary scan. Two non-blocking findings: (P2) the new Validation bullet at docs/concepts/interaction-pattern-catalog.md:2339 still says the CLI-level --role user role-isolation assertion is "proposed and not yet landed", but that assertion already exists in the same head via #4436 (examples/control_plane/todo-archive-completed-smoke.py:147, incl. the Agent Todo must-not-move checks at :219-227); (P3) the matrix-cell regex silently skips annotated cells, so IP-032 (archived-work retention) listed under a second family passes the new check rather than failing it.
What this adds
GH-C37 asks for one new public-safe catalog case. This adds IP-032
Completed Work Archive With Durable Decision Retention, a
P1State AndBoundary pattern.
+127/-1across two files:docs/concepts/interaction-pattern-catalog.mdandexamples/interaction-pattern-catalog-smoke.py.Changes since last review (REQUEST_CHANGES at 81561c0)
IP-030, whichalready denotes Machine Configuration Preview And Revision-Guarded Apply
(and main has since taken
IP-031as Manager Context Is Not TurnAuthority). The entry is now
IP-032, the next free id on current main(
f4ed58de9), updated in all four places: table row, detail heading,Pattern-To-Canary matrix row, and the smoke expectation strings.
f4ed58de9. The previous head conflicted with main inexactly this file; the branch is now main + one commit,
mergeable=CLEAN.interaction-pattern-catalog-smoke.pynow also verifies, data-driven overthe catalog: (a) every pattern id owns exactly one
| P<n> | IP-0nn |tablerow; (b) every table row has its
#### IP-0nndetail heading and viceversa; (c) no id is listed under more than one family in the
Pattern-To-Canary matrix; (d) every matrix id has a pattern row. The
original IP-030 collision now fails three of these assertions.
Why this is a real gap and not a duplicate
loopx todo archive-completedencodes three interaction rules that nocatalog entry explains:
move candidate. The payload reports
retained_standing_decision_count, andthe decision must still resolve as active standing authority
(
standing_decision_authority_v0) after the move.--role usermust leaveAgent Todountouched. The role defaults toagent, so a caller who means the user lane has to say so.--executeit is a dry run that must not change thestate file.
Grepping the catalog for
archive,standing decision, orretentionreturnsonly paths under
docs/archive/incidents/. No IP owns this behavior.The entry explicitly separates itself from the nearest neighbors so it does not
become a second source of truth:
leaves the active window. That is the gap.
This is the catalog maintenance rule "a smoke encodes a behavior that is not
yet explained to humans":
todo-archive-completed-smoke.pyandtodo-standing-decision-authority-smoke.pyalready encode the behavior, butnothing names it.
Changes
P1).model, Bad smell, Validation.
list, and
completed-work archivejoins that family's trigger surfaces, sothe matrix does not drift the way docs(catalog): align pattern details order with the catalog table #4324 had to fix.
structural id-uniqueness / family-membership / detail-pairing assertions.
Validation
python3 examples/interaction-pattern-catalog-smoke.py-> ok (now alsoenforcing the structural assertions above)
python3 examples/canary/catalog-planner-smoke.py-> okpython3 examples/docs-governance-smoke.py-> okloopx check --scan-path docs/concepts/interaction-pattern-catalog.md(thevalidation GH-C37 names) ->
ok: True,errors=0, public boundary scan cleanpy_compileon the touched smoke;git diff --checkcleanRUF100), byte-identical on main's version — nothing new introduced
Mutation check: duplicating the IP-032 table row, listing IP-032 under a
second family, and renaming the IP-032 detail heading each make the catalog
smoke fail with the corresponding structural assertion message, so the new
checks bind rather than passing vacuously.
Honest gap
Role isolation is currently only proven at helper level. The entry marks the
CLI-level assertion (
--role userleavesAgent Todobyte-identical) asfuture smoke rather than claiming coverage it does not have — per the
maintenance rule "mark it as a future smoke rather than hiding the gap". A
separate open PR of mine (#4436) proposes that CLI-level assertion; if it lands
first, I will update the Validation section to point at it.
Scope and boundary
status, benchmark, or scoring behavior changes.
transcripts, private logs, credentials, or local paths.
docs/test-only and touch disjoint files. If the one-active-implementation-PR
convention should apply, say which one to hold and I will close the others.