Compare commits
62
Commits
89a6054941
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c92443280e | ||
|
|
ce7b0d9c44 | ||
|
|
1651efa97b | ||
|
|
88581f3440 | ||
|
|
3d3474a731 | ||
|
|
9126b09b5d | ||
|
|
41391776e1 | ||
|
|
74f3278514 | ||
|
|
d138dd8b45 | ||
|
|
c88cb95654 | ||
|
|
a29562ef83 | ||
|
|
b63685d7b5 | ||
|
|
98ee59c477 | ||
|
|
60bd1bedb4 | ||
|
|
10b751f159 | ||
|
|
ceb6bb9f5a | ||
|
|
940409a85e | ||
|
|
955a32de2b | ||
|
|
2f37df890a | ||
|
|
9a80897cb7 | ||
|
|
68219746be | ||
|
|
058d5b3f9c | ||
|
|
67ffd7c89e | ||
|
|
1dbd15add0 | ||
|
|
88f03f500e | ||
|
|
36cc7f443b | ||
|
|
a2b4cf73a3 | ||
|
|
37e194871f | ||
|
|
a874ee600a | ||
|
|
b2f8c04b15 | ||
|
|
679c483d00 | ||
|
|
a1e0c85c04 | ||
|
|
17ba4eab35 | ||
|
|
d65195bd41 | ||
|
|
a23a212015 | ||
|
|
a39b4f795c | ||
|
|
d2dbd9dfdf | ||
|
|
ecfa244c0a | ||
|
|
f1866903c5 | ||
|
|
cb767f6035 | ||
|
|
5130babad4 | ||
|
|
f217a2bce6 | ||
|
|
1970dccb7c | ||
|
|
52093614fa | ||
|
|
80e5e4559c | ||
|
|
ba1d3b2b81 | ||
|
|
3080c0f4af | ||
|
|
b672792f77 | ||
|
|
5eee6fec51 | ||
|
|
6d35f911fe | ||
|
|
aa77f20184 | ||
|
|
1dee480dff | ||
|
|
ddc5bf9285 | ||
|
|
7c97dee7c2 | ||
|
|
36a1d4084e | ||
|
|
635459316d | ||
|
|
63ff64dd7a | ||
|
|
db37c59a32 | ||
|
|
5f1d6f8e15 | ||
|
|
4cb94bd8d7 | ||
|
|
49a22059c9 | ||
|
|
795444c4c0 |
@@ -0,0 +1,162 @@
|
||||
# feature/audit-followups-pin-gate-m5 任务卡
|
||||
|
||||
任务主记录:PR #695 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/695;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= b7636742fc9ea937e6030123afc608fd294861bf
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-audit-followups-pin-gate-m5(worktree);分支:feature/audit-followups-pin-gate-m5
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=d2608b7436d1c2d773c607a79568c57cfaa2c32cebf35d1a0cabda9562a543c1;triage_receipt=/tmp/triage-followups-1792647600.json;receipt_sha256=01837cb87f83d50c255ee460dfa8e28ade7ed600624bea7d0c083b7653c8a5dd
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:需求拆解;当前增量:I1(待定义)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
<预期结果、受影响入口/模块/契约;明确不做什么。>
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:待核对(无适用规划时说明原因)
|
||||
- 主记录路径/版本:待核对(路径按仓库根目录定位,归档后不变;只引用唯一正文)
|
||||
- 对应里程碑:待核对
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 待填写 | 待评估 | 待定 | 待定义 | 待确认 | 待评审 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:待填写(含对象版本)
|
||||
- 实施计划引用:待填写(含增量与需求 ID)
|
||||
- 测试计划引用:待填写(含需求 ID 与验收条件)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 待定 | 待执行 | — | — | — |
|
||||
| 需求评估 | 待定 | 待执行 | — | — | — |
|
||||
| 需求表确认 | 待定 | 待执行 | — | — | — |
|
||||
| 方案设计 | 待定 | 待执行 | — | — | — |
|
||||
| 实施计划 | 待定 | 待执行 | — | — | — |
|
||||
| 实施与自检 | 待定 | 待执行 | — | — | — |
|
||||
| 正式测试与验收 | 待定 | 待执行 | — | — | — |
|
||||
| 代码 Review | 待定 | 待执行 | — | — | — |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | b7636742fc9ea937e6030123afc608fd294861bf | b7636742fc9ea937e6030123afc608fd294861bf | 2026-10-22T05:41:15Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:未入队;调度记录:无
|
||||
- entry_id:无;attempt_id:无;窗口执行者:无
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:待核对(依据实际成果更新适用方案/规划)
|
||||
- 首选下一任务:待核对(无适用规划、已完成或受阻时如实说明,不虚构任务)
|
||||
- 推荐依据:待填写
|
||||
- 前置条件/待确认:待核对
|
||||
- 预期产物/验收条件:待定义
|
||||
- 重复任务核对:待核对(已有/在途任务、PR 与规划记录)
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- CI 记录:首轮 CI Checkout 失败——backend 合并 tip a75783f 未推送远端;已补推,本提交重触发 CI。
|
||||
- CI 记录:88f03f50/67ffd7c9 两轮 CI——首轮 Checkout 失败(backend tip a75783f 未推送,已补推);次轮 test-pg 的 TestMaterialAckAttachmentOnlyDoesNotCallParentModel 超时:产品代码从不设置 Workspace.Writable(M5 预检不可能触发),本地同提交复现 PASS(0.051s,真实 PG/Redis,verify-materialack.json),判定为并行功能新测试的 CI 环境超时 flake;后续提交重触发 CI。
|
||||
- Review 记录:独立 CLI 评审单元 audit-followups-independent-review(收据 /tmp/agent-stack-b2/reviewf-run.json);结论 APPROVE;报告 .task/reviews/2026-10-22-audit-followups-independent-review.md;被审对象合并基线 vs main
|
||||
- 合并基线:分支已合并 origin/main(含并行 backend 角色策略/scrarch-session 工作与 frontend/ui pin 前进);modules/backend 组合 pin = a75783f(含 7bcbbdb 与 M5 嵌套指针)。
|
||||
- 已知既有失败:make test-governance 中 test_svc.SvcDbRecoveryTests.test_addr_change_keeps_database_and_auth 在干净 main(b7636742)同样失败(环境依赖),与本分支无关;CI 环境为最终裁决。其余组合目标(boundaries/pins/libs/core/backend/frontend/ui/agentcmd)全部 PASS(verify-followups-rest.json exit=0)
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-22T05:40:13.195742+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "b7636742fc9ea937e6030123afc608fd294861bf",
|
||||
"after_head": "b7636742fc9ea937e6030123afc608fd294861bf",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "b7636742fc9ea937e6030123afc608fd294861bf",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||
"[repo] branch=main head=b7636742fc",
|
||||
"[remote] origin/main=b7636742fc ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] b7636742fc(完整:b7636742fc9ea937e6030123afc608fd294861bf)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||
],
|
||||
"output_sha256": "750b4668efa152b0a18152e950ab40051a737f940cf5e108e6adc79324dea82a",
|
||||
"receipt_sha256": "e2cd793c34846aafbd4c3dd5a77d8bc08fb51323ded1082b212c91e78074bad7",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/e2cd793c34846aafbd4c3dd5a77d8bc08fb51323ded1082b212c91e78074bad7.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #695(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/695)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,168 @@
|
||||
# feature/intent-multi-turn-a1 任务卡
|
||||
|
||||
任务主记录:PR #700 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/700;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 955a32de2bb49171348bad5f566cce935df19212
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-intent-multi-turn-a1(worktree);分支:feature/intent-multi-turn-a1
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=5dd60900f5e7d69e382c367bfde373c087528dd9dcae7e16199c956ef4f3d9bf;triage_receipt=/tmp/triage-a1-1792658810.json;receipt_sha256=35b32668d143e39f386f069ae4c0004cae9761125d95241bb66e6aff1530d24b
|
||||
|
||||
- 授权与交付终点:用户已批准启动 A1 多轮意图闭环(方案 Functional Follow-Ups 第一项)。范围内连续推进:设计、agent-per-unit 实施、验证、组合、Review、PR/CI/合入。未授权:A2-A4、A2A、部署/发布、其余业务动作族扩展。
|
||||
- 设计决定(2026-10-22,host):
|
||||
1. 第一类真实业务动作 = **运行取消**("取消刚才的任务"):cancel 命令已注册且为高后果动作,走既有 cmdbus 确认卡;后续动作族另行增量。
|
||||
2. 会话状态存 Backend 既有 store(新表 intent_session,键 owner+project+conversation),保存挂起预期(action ref/revision/缺失槽位/已累积槽位)与过期时间(30 分钟 TTL);revision 递增防过期回答污染新任务。
|
||||
3. 库层不动 Stage 1 语义:Backend 实现 ExpectationProvider 数据源并在 intentgate/route 消费 set_slot/change_slot/cancel/expectation 命令(route.go 现按"宿主不消费"转 turn——本任务改变这一点)。
|
||||
4. 高危动作仍必须经 cmdbus 确认;会话只是槽位与预期的载体,不授予执行权。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:需求拆解;当前增量:I1(待定义)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
**目标**:多轮对话中:缺槽动作 → 澄清 → 用户回答补槽 → 动作完成(第一类=运行取消);"取消刚才的任务"作用于明确对象;歧义先澄清;过期回答(TTL 外)不影响新任务;双用户隔离;revision 防串轮。拆两个单元:U-A1b 会话状态与命令消费(intentgate/route/server + store 表);U-A1c 取消意图路由到 cmdbus(确认卡 + 明确对象解析)。
|
||||
|
||||
**非目标**:A2-A4;A2A;更多业务动作族;前端界面改动;部署。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:待核对(无适用规划时说明原因)
|
||||
- 主记录路径/版本:待核对(路径按仓库根目录定位,归档后不变;只引用唯一正文)
|
||||
- 对应里程碑:待核对
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 待填写 | 待评估 | 待定 | 待定义 | 待确认 | 待评审 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:待填写(含对象版本)
|
||||
- 实施计划引用:待填写(含增量与需求 ID)
|
||||
- 测试计划引用:待填写(含需求 ID 与验收条件)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 待定 | 待执行 | — | — | — |
|
||||
| 需求评估 | 待定 | 待执行 | — | — | — |
|
||||
| 需求表确认 | 待定 | 待执行 | — | — | — |
|
||||
| 方案设计 | 待定 | 待执行 | — | — | — |
|
||||
| 实施计划 | 待定 | 待执行 | — | — | — |
|
||||
| 实施与自检 | 待定 | 待执行 | — | — | — |
|
||||
| 正式测试与验收 | 待定 | 待执行 | — | — | — |
|
||||
| 代码 Review | 待定 | 待执行 | — | — | — |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 955a32de2bb49171348bad5f566cce935df19212 | 955a32de2bb49171348bad5f566cce935df19212 | 2026-10-22T08:48:01Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:未入队;调度记录:无
|
||||
- entry_id:无;attempt_id:无;窗口执行者:无
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:待核对(依据实际成果更新适用方案/规划)
|
||||
- 首选下一任务:待核对(无适用规划、已完成或受阻时如实说明,不虚构任务)
|
||||
- 推荐依据:待填写
|
||||
- 前置条件/待确认:待核对
|
||||
- 预期产物/验收条件:待定义
|
||||
- 重复任务核对:待核对(已有/在途任务、PR 与规划记录)
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- 实施收据:U-A1b(pi attempt1,intent-session 会话/命令消费,backend 0fb64c4,verify-a1b exit=0);U-A1c(pi attempt1,cancel 经 cmdbus 路由,backend c2e3104→verify-a1c exit=0);架构基线边登记(repair,a1arch4-run.json)与 storage-migrate manifest 补齐(repair,backend 9ee752c,verify-a1sr exit=0)。
|
||||
- 组合验证:test-governance 存在 svc 环境不稳定失败(两次运行分别挂在 test_addr_change / test_U2_health;addr_change 已在干净 main 复现,见批二后验证),其余全部目标 PASS(verify-a1-rest.json exit=0);CI 环境为最终裁决(批二同代码 CI 全绿)。
|
||||
- Review 记录:待执行(独立 CLI 评审,随 PR 流程)
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-22T08:46:59.325275+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"after_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||
"[repo] branch=main head=955a32de2b",
|
||||
"[remote] origin/main=955a32de2b ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=4",
|
||||
"[recycle] merged un-recycled branches: feature/container-storage-plan",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 955a32de2b(完整:955a32de2bb49171348bad5f566cce935df19212)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||
],
|
||||
"output_sha256": "f06e6cc8ddb0c414c2ac92e5100dc58b0b926a2bf7196273056b01be80253217",
|
||||
"receipt_sha256": "32899ef2b4ebee5bf1725fb79e1bba0382177731155cebab04178821e6da3f1b",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/32899ef2b4ebee5bf1725fb79e1bba0382177731155cebab04178821e6da3f1b.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #700(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/700)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -1,108 +0,0 @@
|
||||
# feature/rdm-two-trees-followup-c-w8-w10 任务卡
|
||||
|
||||
任务主记录:PR #693 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/693;状态:已完成(合入前闭合);owner:weichen(W8a+W8b+W9+W10 全部交付;独立 Review 完成且 MAJOR 全修复;MINOR 留档评审报告;下一步阶段 6 评审包→用户人审);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= f23f5bb01a8b96ab90686d3a1b07791d7f473de2
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-rdm-two-trees-followup-c-w8-w10(worktree);分支:feature/rdm-two-trees-followup-c-w8-w10
|
||||
- 分支类型:child;initiative_id:feature-rdm-two-trees-followup;root_target:main;immediate_target:feature/rdm-two-trees-followup;accepting_requirements:false;parent_branch:feature/rdm-two-trees-followup;parent_head_sha:dcca5112923790506e0728a89ae3752349405653;parent_pr:无;parent_source:origin
|
||||
- 需求分流:verdict=JOIN;selected_initiative=feature/rdm-two-trees-followup;requirement_sha256=f4a1ab636c59ce3df302201310d359eac88b105e8ffecd86da8f7266bf3fa58e;triage_receipt=/tmp/triage-incC.json;receipt_sha256=3fb1f7cb6dd41fc250c764d08dd612749f150f3e619b54cbc3a9144b37a4d298
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
<预期结果、受影响入口/模块/契约;明确不做什么。>
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| w8a-operation-audit-core | W8 领域核:操作审计事实/同事务写入/失败尝试/幂等回执/延迟补全/重放一致 | implementation | cursor | 1 | cursor(auto) | completed | 6fd0478(refs/agent-exec/…,收据 ~/.cache/agent-system-dev/agent-exec-receipts/w8a-operation-audit-core.json) | never | ~/.cache/agent-system-dev/agent-exec-receipts/w8a-operation-audit-core.json |
|
||||
| w8a-operation-audit-core | W8 领域核(attempt 1 详见 ~/.cache/…/w8a-operation-audit-core.json;transport 6fd0478) | implementation | cursor | 1 | cursor(auto) | completed | 6fd0478 | never | ~/.cache/agent-system-dev/agent-exec-receipts/w8a-operation-audit-core.json |
|
||||
| w8b-audit-projection | W8 可读投影:DTO 词典/游标分页/可见性接线/rdm.audit.operations(attempt 1 infra 重试) | implementation | cursor | 2 | cursor(auto) | completed | c3d3111 | never | /home/weichen/.cache/agent-system-dev/agent-exec-receipts/w8b-audit-projection.json |
|
||||
| w9-rdm-audit-adapter | W9 RDM 消费端:audit.read 回调适配 Backend X2(受信裁剪/当时名称/owner 分离/拒绝事实)+ pin 前进 92a41183(worker 自纠 41 位笔误:按前缀 fetch 真 tip) | implementation | cursor | 1 | cursor(auto) | completed | c4c6df7 | never | /home/weichen/.cache/agent-system-dev/agent-exec-receipts/w9-rdm-audit-adapter.json |
|
||||
| w10-audit-workspace | W10 正式 Frontend:审计工作台/树懒加载/工作卡/依据条/四态/seq(FE 子分支 feature/rdm-two-trees-followup-w10 @fa08be4;知识多挂载显示留 TODO 待 RDM DTO 增补) | implementation | cursor | 2(attempt1 合理扩围重派) | cursor(auto) | completed | fa08be4 | never | /home/weichen/.cache/agent-system-dev/agent-exec-receipts/w10-audit-workspace.json |
|
||||
| rev-increment-c-rdm | RDM 增量 C 独立评审(0 BLOCKING/5 MAJOR/15 MINOR;报告 docs/reviews/2026-10-22-increment-c-rdm-review.md @b7b5227) | review | pi | 1 | pi(default) | completed | b7b5227 | never | ~/.cache/agent-system-dev/agent-exec-receipts/rev-c-rdm-receipt.json |
|
||||
| fix-increment-c-rdm-majors | RDM 5 MAJOR 修复(schema-2 目录/copyState/生产写接线等,含回归测试) | repair | cursor | 1 | cursor(auto) | completed | d835ae6 | never | ~/.cache/agent-system-dev/agent-exec-receipts/fix-increment-c-rdm.json |
|
||||
| rev-increment-c-fe | FE W10 独立评审(0 BLOCKING/3 MAJOR/MINOR;报告 docs/reviews/2026-10-22-increment-c-fe-review.md @a61eaac) | review | pi | 1 | pi(default) | completed | a61eaac | never | ~/.cache/agent-system-dev/agent-exec-receipts/rev-c-fe-receipt.json |
|
||||
| fix-increment-c-fe-majors | FE 3 MAJOR 修复(openInbox 定位/能力门禁/@ui Collapsible,+6 回归测试) | repair | cursor | 1 | cursor(auto) | completed | 4882e0a | never | ~/.cache/agent-system-dev/agent-exec-receipts/fix-increment-c-fe.json |
|
||||
| 待定义 | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | f23f5bb01a8b96ab90686d3a1b07791d7f473de2 | dcca5112923790506e0728a89ae3752349405653 | 2026-10-22T01:24:37Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:NOT_RUN(审阅智能体/平台身份、base/head、结论、问题闭合及重放依据)
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-22T01:23:50.112388+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "f23f5bb01a8b96ab90686d3a1b07791d7f473de2",
|
||||
"after_head": "f23f5bb01a8b96ab90686d3a1b07791d7f473de2",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "f23f5bb01a8b96ab90686d3a1b07791d7f473de2",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||
"[repo] branch=main head=f23f5bb01a",
|
||||
"[remote] origin/main=f23f5bb01a ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[recycle] merged un-recycled branches: fix/ci-rdm-pin-materialization fix/pal-component-delivery-pins",
|
||||
"[cards] 已合入未闭合任务卡:fix-pal-component-delivery-pins.md——make task-close 回填归档(主检出运行)",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] f23f5bb01a(完整:f23f5bb01a8b96ab90686d3a1b07791d7f473de2)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||
],
|
||||
"output_sha256": "013e1d4d93548576815e9e2fe8e735d049fea47507ef4492478b55e4cf3c7cd6",
|
||||
"receipt_sha256": "643c76ed799ad8ca4ea16ba47a3510694218cdc18e387586ed9e36d6f9c44502",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/643c76ed799ad8ca4ea16ba47a3510694218cdc18e387586ed9e36d6f9c44502.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #693(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/693)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -1,101 +0,0 @@
|
||||
# feature/rdm-two-trees-followup-w3-rest 任务卡
|
||||
|
||||
任务主记录:PR #641 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/641;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 2b7fdc39a1f44163f4766701429d61ffd6926f1a
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-rdm-two-trees-followup-w3-rest(worktree);分支:feature/rdm-two-trees-followup-w3-rest
|
||||
- 分支类型:child;initiative_id:feature-rdm-two-trees-followup;root_target:main;immediate_target:feature/rdm-two-trees-followup;accepting_requirements:false;parent_branch:feature/rdm-two-trees-followup;parent_head_sha:9e6d9690f64a1e970211b3a092084862a2174ad3;parent_pr:无;parent_source:origin
|
||||
- 需求分流:免(父 initiative 子任务登记已列出 feature/rdm-two-trees-followup-w3-rest;parent_head_sha=9e6d9690f64a1e970211b3a092084862a2174ad3)
|
||||
|
||||
- 授权与交付终点:用户 2026-10-19“授权启动 W3”;终点为 RDM 草稿 PR + 本子任务 PR CI 通过 + 阶段 6 评审包,等待用户阶段 6 人审后才合入父 initiative 分支。未授权:运行库迁移、部署、真实模型/付费请求。用户 2026-10-20 阶段 6 结论“通过”,同时授权 RDM #451 合入 RDM main(附条件:W4 开放结构写入口前必须先补节点种类放置检查)。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 目标:补齐 A1 差距核对中的 W3 余项——图形不透明块、重复表格精确覆盖、节点移动/改名保持引用、旧库备份迁移两次同值、已批准版与旧下载可回取;Dev 侧删除无效 `SVC_RCFWEB_ADDR`。
|
||||
- 受影响:RDM `internal/domain`(模板块校验、递归节点、两种新事件)及测试;Dev `tests/test_svc_realcombo.py`。
|
||||
- 非目标:不提供结构变更的 HTTP/插件入口(W4 负责结构提案门控);不改 DHF 渲染;不宣称正式 DHF 模板可用。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 2b7fdc39a1f44163f4766701429d61ffd6926f1a | 9e6d9690f64a1e970211b3a092084862a2174ad3 | 2026-10-19T16:15:03Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:W3 余项在 RDM 分支 `feature/rdm-two-trees-followup-w3-rest`(`013b8b1`、`96d53f6`、`f3fa0c0`,草稿 PR [#451](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/451))与本分支 `80ac3fa` 交付;阶段 6 评审包 docs/superpowers/specs/2026-10-19-rdm-two-trees-followup-w3-review.md。
|
||||
- 验证:RDM 目标测试 20 项与 `./internal/domain/... ./internal/rdmapp` 全量 PASS(SELF,@f3fa0c0);CI(GATE)见下方 CI 记录;Dev `py_compile` PASS(SELF)。
|
||||
- 影响与剩余项:仅 RDM 领域原语与测试(无新写入口)及 Dev 测试一行;剩余见评审包第 3 节与 Review 非阻塞项(W4 写入口前补节点放置检查)。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:全新会话 code-reviewer(只接收需求、base/head、diff),RDM `5a27d78..f3fa0c0`:PASS-with-fixes,无阻塞项;4 条非阻塞项记入评审包第 2 节(放置检查列为 W4 写入口前置条件)。
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录(RDM):用户 2026-10-20 阶段 6 通过并授权;RDM PR [#451](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/451) 核验 HEAD `f3fa0c0e5`、commit status success(2 项,GATE)后合入 RDM main,合入提交 `76b4c0572622c81316511de174469f5f67d31785`;合入后 tree `4cfacf16` 与合入前 `git merge-tree` 预期一致。Dev gitlink 改钉留待 initiative 收口统一组合验证。
|
||||
- 合入记录(本 PR):NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:RDM worktree `Agent-System-RDM-wt-feature-rdm-two-trees-followup-w3-rest` 已核验无写者、子模块干净(仅删除可再生的 `internal/rdmapp/data` 测试产物)后移除,本地分支已删;Dev 子 worktree 待本 PR 合入后回收。
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-19T16:13:50.819321+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "2b7fdc39a1f44163f4766701429d61ffd6926f1a",
|
||||
"after_head": "2b7fdc39a1f44163f4766701429d61ffd6926f1a",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "2b7fdc39a1f44163f4766701429d61ffd6926f1a",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||
"[repo] branch=main head=2b7fdc39a1",
|
||||
"[remote] origin/main=2b7fdc39a1 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=4",
|
||||
"[recycle] merged un-recycled branches: feature/layered-python-env",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 2b7fdc39a1(完整:2b7fdc39a1f44163f4766701429d61ffd6926f1a)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||
],
|
||||
"output_sha256": "ad2f421c16acf4d72ad5396eb19b97aa168900e85db78022bd77c6eac29b3040",
|
||||
"receipt_sha256": "abc4088cf722985bd2eeeffeae46cdd899a23e6b1c2711b8849f14f83c8e46bf",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/abc4088cf722985bd2eeeffeae46cdd899a23e6b1c2711b8849f14f83c8e46bf.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #641(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/641)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -1,124 +0,0 @@
|
||||
# feature/rdm-two-trees-followup-w5-w7 任务卡
|
||||
|
||||
任务主记录:PR #666 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/666;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= ed3847c85ce111a74f0cb100908abfb40f62db07
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-rdm-two-trees-followup-w5-w7(worktree);分支:feature/rdm-two-trees-followup-w5-w7
|
||||
- 分支类型:child;initiative_id:feature-rdm-two-trees-followup;root_target:main;immediate_target:feature/rdm-two-trees-followup;accepting_requirements:false;parent_branch:feature/rdm-two-trees-followup;parent_head_sha:13b9c9daddfb78717438777e955dd296ca56439d;parent_pr:无;parent_source:origin
|
||||
- 需求分流:verdict=JOIN;selected_initiative=feature/rdm-two-trees-followup;requirement_sha256=343b26bddcfc64e013a068fc4f294a28f94c28702c08bafa0f6d92262289e90b;triage_receipt=/tmp/triage-w5w7.json;receipt_sha256=c3948ecfa318defbdd96b5d465665abef97abfcc90472a0a1d216af4dc9ae0dc
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
<预期结果、受影响入口/模块/契约;明确不做什么。>
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| w5a-tracenet-domain-core | internal/domain 追溯网领域核心(revision锁定/候选确认/升版待复核/传递影响+水位/快照/重放) | implementation | cursor | 1 | cursor(auto) | completed | a7a6df0(refs/agent-exec/2533912c/0c0654db) | never | /home/weichen/.cache/agent-system-dev/agent-exec-receipts/w5a-tracenet-domain-core.json |
|
||||
| w5b-trace-impact-endpoints | rdmapp 影响分析/重算/快照读取/覆盖缺项/pending-recheck 命令面(impact.go + plugin 注册) | implementation | cursor | 1 | cursor(auto) | completed | 2e5fcb7(refs/agent-exec/d98a00bc/23b5e5d0) | never | /home/weichen/.cache/agent-system-dev/agent-exec-receipts/w5b-trace-impact-endpoints.json |
|
||||
| w7a-project-context-domain | W7 纯领域部分:ProjectContextBundle 组装/失效语义/渐进证据引用(零基座消费,tag 前约束达成) | implementation | cursor | 1 | cursor(auto) | completed | 8327603(refs/agent-exec/a3eba579/153246be) | never | /home/weichen/.cache/agent-system-dev/agent-exec-receipts/w7a-project-context-domain.json |
|
||||
| w6-x1-recheck-completion | W6 恢复:用户批准修订后,对照清单审计并补齐——消费点全覆盖/入模门禁/存储边界/负例矩阵(Backend 隔离分支,含候选合并 main b4399e3) | implementation | cursor | 1 | cursor(auto) | completed | 8c410b9(refs/agent-exec/412eff9b/0636a7e1) | never | /home/weichen/.cache/agent-system-dev/agent-exec-receipts/w6-x1-recheck-completion.json |
|
||||
| rev-increment-b-rdm | RDM 增量独立评审(0 BLOCKING/6 MAJOR/14 MINOR) | review | pi | 1 | pi(default) | completed | 56b23a0 | never | ~/.cache/agent-system-dev/agent-exec-receipts/rev-rdm-receipt.json |
|
||||
| w5-majors-repair | 评审 MAJOR M1–M6 修复(copyState/快照ID/端点校验/生产写入/空分析/别名) | repair | cursor | 1 | cursor(auto) | completed | 04991ed | never | ~/.cache/agent-system-dev/agent-exec-receipts/w5-fix-receipt.json |
|
||||
| rev-w6-x1-recheck | Backend W6 独立评审(7/7 PASS,0 BLOCKING 0 MAJOR/6 MINOR) | review | pi | 1 | pi(default) | completed | 65122b8 | never | ~/.cache/agent-system-dev/agent-exec-receipts/rev-be-receipt.json |
|
||||
| 待定义 | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | ed3847c85ce111a74f0cb100908abfb40f62db07 | 13b9c9daddfb78717438777e955dd296ca56439d | 2026-10-20T14:48:00Z |
|
||||
| W5a 后(RDM 侧) | ed3a0f9(RDM main) | a7a6df0(RDM 子分支) | 2026-10-20 | SELF:gofmt/vet ok,domain+rdmapp 全部 ok(81.9s) |
|
||||
| W5b 后(RDM 侧) | a7a6df0 | 2e5fcb7 | 2026-10-20 | SELF:vet ok;rdmapp(81.2s)+plugins/rdm(3.3s) 全部 ok;gofmt 仅既有 iam_integration_test.go 告警(未触碰) |
|
||||
| W7a 后(RDM 侧) | 2e5fcb7 | 8327603 | 2026-10-20 | SELF:vet ok;rdmapp 全部 ok(91.5s);grep 证实零 pkg/extension 消费 |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:NOT_RUN(审阅智能体/平台身份、base/head、结论、问题闭合及重放依据)
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T14:46:07.178211+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "ed3847c85ce111a74f0cb100908abfb40f62db07",
|
||||
"after_head": "ed3847c85ce111a74f0cb100908abfb40f62db07",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "ed3847c85ce111a74f0cb100908abfb40f62db07",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||
"[repo] branch=main head=ed3847c85c",
|
||||
"[remote] origin/main=ed3847c85c ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[recycle] merged un-recycled branches: feature/fast-slow-o2-a01-checkpoint-design",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] ed3847c85c(完整:ed3847c85ce111a74f0cb100908abfb40f62db07)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||
],
|
||||
"output_sha256": "f03caadb3c0597418604c953038e5658758c9546150ca3d711e853ebc0c24a8c",
|
||||
"receipt_sha256": "657414206727892ce0ed5ce0ee067e220e11e9da50b3b1fae1bd9db4bc530a6e",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/657414206727892ce0ed5ce0ee067e220e11e9da50b3b1fae1bd9db4bc530a6e.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 阶段 6 结论(2026-10-20)
|
||||
|
||||
用户结论「通过」。裁定明细:W5/W6/W7a 交付通过;X1 公开 tag 暂未发布(W7 provider 接线保持门禁,双钥匙另行动作);RDM 评审 MINOR 14 条按包建议列为增量 C 顺手项。合入执行:RDM PR #458 已核 CI(build-and-test+governance success @22b4fb0)后合入 RDM main(93badd7e);Backend PR #231 已核 CI(vet-and-test+governance success @5a4edf3,draft 转正式后)合入 backend main(7a41f22e)。
|
||||
|
||||
## 阶段 6 评审包(2026-10-20)
|
||||
|
||||
评审包:`docs/superpowers/specs/2026-10-20-rdm-two-trees-followup-w5-w7-review.md`。独立 Review 全部完成(RDM 0 BLOCKING、6 MAJOR 已修复;Backend 7/7 PASS、0 BLOCKING 0 MAJOR),增量 B 达到用户人审就绪点。
|
||||
|
||||
## W6 恢复记录(2026-10-20)
|
||||
|
||||
用户批准修订(「按已实际纳入来源逐项重新授权」X1 合同 + 去原文迁移边界)。恢复动作:候选 6ba53e4 建分支 feature/w6-x1-source-recheck,合并最新 main(自动合并无冲突,b4399e3),既有多数项已在候选实现(含此前 RED 的无正文测试已绿);派发 w6-x1-recheck-completion 单元补齐消费点/门禁/负例(7 文件,transport 8c410b9),SELF 全绿(build/vet/store/runmgr/workflow/extension)。X1 公开 tag 发布仍为用户双钥匙。
|
||||
|
||||
## W6 门禁记录(2026-10-20)
|
||||
|
||||
W6 按设计修订文档 `rdm-two-trees-stage6-w6-source-recheck-design-adjustment-r1.md`(状态:**待人工评审**)暂停实现:候选分支 HEAD 6ba53e4(未推送)已有部分实现但含隐私敏感的存储/迁移取舍,须经用户批准该修订后方可继续。本单元按计划 W7 但书仅交付「纯领域摘要与测试」,未消费基座接口,不宣布自动注入完成。
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #666(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/666)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,300 @@
|
||||
# feature/scratch-session-general-agent 任务卡
|
||||
|
||||
任务主记录:PR #691 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/691;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= a4311c6341c318d4bbf1563b3973358326086794
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-scratch-session-general-agent(worktree);分支:feature/scratch-session-general-agent
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=617539e611dec18b73e0f60c7581ac55246864a96411c38c56fb31dcf561d276;triage_receipt=/tmp/scratch-triage.json;receipt_sha256=1d6484b995d8c51764e91dfeb6ae605e2e3f5ccbf19e07c016f58abe5502d953
|
||||
|
||||
- 授权与交付终点:按已批准设计实现无项目对话(scratch);Backend/Frontend/Dev pin 经 PR 合入;未授权:改写历史、分支保护、发布版本、配额/升级成真项目
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:需求拆解;当前增量:I1(待定义)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 目标:所有启用账号可创建彼此隔离的 scratch 会话;侧栏「无项目对话」;固有能力不可配置;拒绝知识树/文档树等项目专属能力。
|
||||
- 非目标:升级成真项目、协作分享、配额/TTL、nullable project_id。
|
||||
- 规格:docs/superpowers/specs/2026-10-22-scratch-session-general-agent-design.md
|
||||
- 计划:docs/superpowers/plans/2026-10-22-scratch-session-general-agent-plan.md
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| backend-scratch-session-create-u1 | Backend internal/store/{scratch,projects}.go、internal/server/{server,project_lifecycle,scratch_session}.go + tests | Backend AGENT-EXECUTION.md digest 22fcb8ac | implementation | 按配置 | 1(fresh_session) | pi MiniMax-M3 | success | apply 16538f1..096e96e → 规范提交 3b4a7af(tree 不变) | never | /tmp/scratch-session-20261022/u1-run.json |
|
||||
| backend-scratch-session-gates-u2 | Backend store/server/runmgr/app 门禁 + tests | 同上 | implementation | 按配置 | 1(fresh_session) | pi MiniMax-M3 | success | apply 3b4a7af..7bd3e51 → 规范提交 613c068(tree 不变) | never | /tmp/scratch-session-20261022/u2-run.json |
|
||||
| frontend-scratch-session-sidebar-u3 | Frontend src/{contracts,api,store}.ts、TaskSidebar.tsx、tests | Frontend AGENT-EXECUTION.md | implementation | 按配置 | 1 | cursor | failed-unknown(CLI HTTP/2 keepalive 超时);对账:仅只读命令与 npm ci 下载、隔离区零跟踪改动 → 全新重派 | 无 | never | /tmp/scratch-session-20261022/u3-run.json |
|
||||
| 同上(重派 2) | 同上 | 同上 | implementation | 按配置 | 1 | cursor | failed-unknown(无有效终态记录;worker 报 src/store.test.ts 旧规则测试在范围外);对账:改动仅在隔离区;扩围 src/store.test.ts(已批准设计内的旧规则测试)→ 单元 u3b | 无 | never | /tmp/scratch-session-20261022/u3-run2.json |
|
||||
| frontend-scratch-session-sidebar-u3b | 同上 + src/store.test.ts | 同上 | implementation | 按配置 | 1 | cursor | failed-unknown(CLI Connection stalled,验证已通过、终态前断线);对账:只读/npm ci/vitest/tsc、/tmp 临时配置已删 → 全新重派 | 无 | never | /tmp/scratch-session-20261022/u3-run3.json |
|
||||
| backend-scratch-flag-u2b | Backend server 会话响应 scratch 标记 + store.ScratchProjectIDs + tests | Backend AGENT-EXECUTION.md | implementation | 按配置 | 1(fresh_session) | pi | success | apply → 规范提交 f3f70a7(tree 不变) | never | /tmp/scratch-session-20261022/u2b-run.json |
|
||||
| 同上 u3b(重派 4) | 同上 | 同上 | implementation | 按配置 | 1 | cursor | failed-unknown(最终消息含两份有效终态记录:后台 npm ci 完成通知后重复输出);对账:改动仅在隔离区,外部仅 npm ci 下载 → 单元 u3c 增加“前台执行、仅一份终态记录”约束后全新重派 | 无 | never | /tmp/scratch-session-20261022/u3-run4.json |
|
||||
| frontend-scratch-session-sidebar-u3c | 同 u3b | 同上 | implementation | 按配置 | 1(fresh_session) | cursor | success | apply 18fc958..62b7442 → 规范提交 1d7f2a3(tree 不变) | never | /tmp/scratch-session-20261022/u3-run5.json |
|
||||
| backend-scratch-context-and-bus-gates-u2c | Backend ProjectContext 门禁 + Bus.SetHostProjectGuard + scratch fail-closed + tests | Backend AGENT-EXECUTION.md | implementation | 按配置 | 1(fresh_session) | pi | success | apply → 规范提交 2fc6e7e(tree 不变) | never | /tmp/scratch-session-20261022/u2c-run.json |
|
||||
| backend-scratch-failclosed-both-ways-u2d | Backend confirmedScratchProject 双向 fail-closed + tests | 同上 | implementation | 按配置 | 1(fresh_session) | pi | success | apply → 规范提交 b704ba9(tree 不变) | never | /tmp/scratch-session-20261022/u2d-run.json |
|
||||
| frontend-scratch-hide-project-surfaces-u3d | Frontend Conversation/Workbench/ConversationWork 隐藏项目专属面 | Frontend AGENT-EXECUTION.md | implementation | 按配置 | 1 | cursor | scope-expansion(UI ActivityCardView 必须检查 callbacks.onArchive);人工授权扩围到 UI 仓 | 无 | never | /tmp/scratch-session-20261022/u3d-run.json |
|
||||
| frontend-scratch-hide-project-surfaces-u3e | 同 u3d(UI 侧另单元处理;本单元只断言回调) | 同上 | implementation | 按配置 | 1(fresh_session) | cursor | success | apply → 规范提交 b02ff83(tree 不变) | never | /tmp/scratch-session-20261022/u3e-run.json |
|
||||
| ui-activity-card-archive-optional-u5 | UI ActivityCardView 仅在有 onArchive 时渲染留档按钮 + tests | UI AGENT-EXECUTION.md | implementation | 按配置 | 1(fresh_session) | cursor | success | apply → 规范提交 db5129c(tree 不变) | never | /tmp/scratch-session-20261022/u5-run.json |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:待核对(无适用规划时说明原因)
|
||||
- 主记录路径/版本:待核对(路径按仓库根目录定位,归档后不变;只引用唯一正文)
|
||||
- 对应里程碑:待核对
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 待填写 | 待评估 | 待定 | 待定义 | 待确认 | 待评审 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:待填写(含对象版本)
|
||||
- 实施计划引用:待填写(含增量与需求 ID)
|
||||
- 测试计划引用:待填写(含需求 ID 与验收条件)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 待定 | 待执行 | — | — | — |
|
||||
| 需求评估 | 待定 | 待执行 | — | — | — |
|
||||
| 需求表确认 | 待定 | 待执行 | — | — | — |
|
||||
| 方案设计 | 待定 | 待执行 | — | — | — |
|
||||
| 实施计划 | 待定 | 待执行 | — | — | — |
|
||||
| 实施与自检 | 待定 | 待执行 | — | — | — |
|
||||
| 正式测试与验收 | 待定 | 待执行 | — | — | — |
|
||||
| 代码 Review | 待定 | 待执行 | — | — | — |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | a4311c6341c318d4bbf1563b3973358326086794 | a4311c6341c318d4bbf1563b3973358326086794 | 2026-10-22T01:43:32Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:未入队;调度记录:无
|
||||
- entry_id:无;attempt_id:无;窗口执行者:无
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:待核对(依据实际成果更新适用方案/规划)
|
||||
- 首选下一任务:待核对(无适用规划、已完成或受阻时如实说明,不虚构任务)
|
||||
- 推荐依据:待填写
|
||||
- 前置条件/待确认:待核对
|
||||
- 预期产物/验收条件:待定义
|
||||
- 重复任务核对:待核对(已有/在途任务、PR 与规划记录)
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:NOT_RUN(审阅智能体/平台身份、base/head、结论、问题闭合及重放依据)
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-22T01:41:26.489856+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "a4311c6341c318d4bbf1563b3973358326086794",
|
||||
"after_head": "a4311c6341c318d4bbf1563b3973358326086794",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "a4311c6341c318d4bbf1563b3973358326086794",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||
"[repo] branch=main head=a4311c6341",
|
||||
"[remote] origin/main=a4311c6341 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=6",
|
||||
"[recycle] merged un-recycled branches: feature/card-close-20261022-012859Z fix/ci-rdm-pin-materialization fix/pal-component-delivery-pins",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] a4311c6341(完整:a4311c6341c318d4bbf1563b3973358326086794)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||
],
|
||||
"output_sha256": "5dc418ef2935ca343c4d18b9a8b9408c31d0870061bf48a0cd20d1efeb574159",
|
||||
"receipt_sha256": "59b8a99e07c11121e009221f6700874c9650c4f057f494394d05d5e47eec3f95",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/59b8a99e07c11121e009221f6700874c9650c4f057f494394d05d5e47eec3f95.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 1de7c28c2122609dd9b6435881696492bd052a34 | 635459316dbd4e4a5134f4e409bed72a5dd02730 | 2026-10-22T05:18:42Z |
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-22T05:18:47.691228+00:00",
|
||||
"command": "make test",
|
||||
"exit_code": 2,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-scratch-session-general-agent",
|
||||
"before_head": "36a1d4084e8bb7aaea6f04e29fb4d3492f4f0db3",
|
||||
"after_head": "36a1d4084e8bb7aaea6f04e29fb4d3492f4f0db3",
|
||||
"branch": "feature/scratch-session-general-agent",
|
||||
"target_branch": "main",
|
||||
"target_head": "1de7c28c2122609dd9b6435881696492bd052a34",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"warn: deps/agent-native deps/development-manager is bd3a7de57a3a, Dev is 2c2e37255807",
|
||||
"ok deps/agent-native error-code-system",
|
||||
"ok deps/agent-native logging-system",
|
||||
"ok deps/agent-squad agent-intent",
|
||||
"ok deps/agent-squad agent-native",
|
||||
"warn: deps/agent-squad deps/development-guidelines is 2688fd2be6a5, Dev is a15db5085585",
|
||||
"warn: deps/agent-squad deps/development-manager is bd3a7de57a3a, Dev is 2c2e37255807",
|
||||
"ok deps/agent-squad error-code-system",
|
||||
"ok deps/agent-squad logging-system",
|
||||
"warn: deps/agent-intent deps/development-guidelines is 2688fd2be6a5, Dev is a15db5085585",
|
||||
"warn: deps/agent-intent deps/development-manager is bd3a7de57a3a, Dev is 2c2e37255807",
|
||||
"ok deps/agent-intent error-code-system",
|
||||
"ok deps/agent-intent logging-system",
|
||||
"make[1]: *** [Makefile:209: test-pins] Error 1",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-scratch-session-general-agent'"
|
||||
],
|
||||
"output_sha256": "2d377eb17fd54185095a73449eb657b742e645e180e1d52202cdd168114fe7e7",
|
||||
"receipt_sha256": "21c7a8c6a8ffc79adb81ed7e90b6c50d4f1aaaa941de12c2a4cd64e8b47e4aa8",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/21c7a8c6a8ffc79adb81ed7e90b6c50d4f1aaaa941de12c2a4cd64e8b47e4aa8.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | b7636742fc9ea937e6030123afc608fd294861bf | 1dee480dff7203aff3bd6b57661645fe0b890bce | 2026-10-22T05:49:12Z |
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-22T05:49:16.822650+00:00",
|
||||
"command": "make test",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-scratch-session-general-agent",
|
||||
"before_head": "aa77f20184ace3302480642aef5d412819792da8",
|
||||
"after_head": "aa77f20184ace3302480642aef5d412819792da8",
|
||||
"branch": "feature/scratch-session-general-agent",
|
||||
"target_branch": "main",
|
||||
"target_head": "b7636742fc9ea937e6030123afc608fd294861bf",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "7a447319f8c4fdedb68da2ef8c0fc740d86697df8f8af7650980c77a56befa7b",
|
||||
"output_tail": [
|
||||
"added 162 packages in 945ms",
|
||||
"",
|
||||
" RUN v4.1.11 /home/weichen/projects/Agent-System-Dev-wt-feature-scratch-session-general-agent/modules/ui",
|
||||
"",
|
||||
"",
|
||||
" Test Files 19 passed (19)",
|
||||
" Tests 188 passed (188)",
|
||||
" Start at 05:56:58",
|
||||
" Duration 1.68s (transform 1.31s, setup 0ms, import 3.38s, tests 3.68s, environment 6.91s)",
|
||||
"",
|
||||
"knip unused exports 61 <= 61",
|
||||
"cd deps/agent-command && go vet ./... && go test -count=1 ./...",
|
||||
"ok \tagent-command\t0.002s",
|
||||
"ok \tagent-command/conformance\t0.002s",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-scratch-session-general-agent'"
|
||||
],
|
||||
"output_sha256": "4fee33a7ba3812f667660d6c2b49348ae902983cdfebd6a5f3f13bedb4611f0b",
|
||||
"receipt_sha256": "1331f3cff5d6a225efa50af64c5e1301faee72c380b995ea08b4a1a7beff79da",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/1331f3cff5d6a225efa50af64c5e1301faee72c380b995ea08b4a1a7beff79da.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-22T06:14:22.167417+00:00",
|
||||
"command": "make test",
|
||||
"exit_code": 2,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-scratch-session-general-agent",
|
||||
"before_head": "b672792f77f612d563720bff99a55ff5aa721242",
|
||||
"after_head": "b672792f77f612d563720bff99a55ff5aa721242",
|
||||
"branch": "feature/scratch-session-general-agent",
|
||||
"target_branch": "main",
|
||||
"target_head": "33d6ef69212595c680a744549986c19bb6acd6c3",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
" File \"/home/weichen/projects/Agent-System-Dev-wt-feature-scratch-session-general-agent/tests/test_svc.py\", line 227, in svc",
|
||||
" self.assertEqual(proc.returncode, 0, proc.stdout)",
|
||||
" ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
||||
"AssertionError: 1 != 0 : \u001b[1;33m[svc]\u001b[0m [dev] 跳过 manifest 迁移:需 python3 与 scripts/svc_manifest.py",
|
||||
"\u001b[1;34m[svc]\u001b[0m [dev] 无 pidfile——没有由本脚本启动的实例",
|
||||
"\u001b[1;31m[svc]\u001b[0m [dev] 端口被占用(非本服务):LISTEN 0 4096 127.0.0.1:18794 0.0.0.0:* users:((\"python3\",pid=1444356,fd=3)) ",
|
||||
"\u001b[1;31m[svc]\u001b[0m 处理:停掉该进程,或换端口(--port / --addr)",
|
||||
"",
|
||||
"",
|
||||
"----------------------------------------------------------------------",
|
||||
"Ran 539 tests in 373.148s",
|
||||
"",
|
||||
"FAILED (failures=1, skipped=18)",
|
||||
"make[1]: *** [Makefile:232: test-governance] Error 1",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-scratch-session-general-agent'"
|
||||
],
|
||||
"output_sha256": "572e274146ea0cd7b0f87865cf16c8be406ca4eda51bca7ef8469be6812ddec9",
|
||||
"receipt_sha256": "d2b9cb21e0ab4dcf1d4b03bc939dd9f8bcd94faadff03b19acacad81b5465848",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/d2b9cb21e0ab4dcf1d4b03bc939dd9f8bcd94faadff03b19acacad81b5465848.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 33d6ef69212595c680a744549986c19bb6acd6c3 | 1970dccb7c8c11fb69ad739ae1338e97ca4ac0a6 | 2026-10-22T06:21:57Z |
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #691(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/691)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,99 @@
|
||||
# fix/svc-test-port-governance-final 任务卡
|
||||
|
||||
任务主记录:PR #698 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/698;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 955a32de2bb49171348bad5f566cce935df19212
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-svc-test-port-governance-final(worktree);分支:fix/svc-test-port-governance-final
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=04cbe9df900a03163aeadb42fd22e5a86ddeced179901b50ac0f4642ce7f6881;triage_receipt=/tmp/pg3-triage.json;receipt_sha256=aca390790027841efaefe2082123780b1ec8d775f9ca0be80262a80f7121bde9
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
<预期结果、受影响入口/模块/契约;明确不做什么。>
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 955a32de2bb49171348bad5f566cce935df19212 | 955a32de2bb49171348bad5f566cce935df19212 | 2026-10-22T08:55:33Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:NOT_RUN(审阅智能体/平台身份、base/head、结论、问题闭合及重放依据)
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-22T08:54:47.013779+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"after_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||
"[repo] branch=main head=955a32de2b",
|
||||
"[remote] origin/main=955a32de2b ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[recycle] merged un-recycled branches: feature/container-storage-plan",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 955a32de2b(完整:955a32de2bb49171348bad5f566cce935df19212)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||
],
|
||||
"output_sha256": "b63c0d5c11be0292024cc972eee78fbcef7349381ee3bdecb432815b903373e6",
|
||||
"receipt_sha256": "3ee914c56ff0e9cf2a4d11db1ec0a2e6c74cf745cceb7eff1cefd2f9d938ee89",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/3ee914c56ff0e9cf2a4d11db1ec0a2e6c74cf745cceb7eff1cefd2f9d938ee89.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #698(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/698)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | 98ee59c477785b2d5094fcbea3e67a4dc864c388 | a29562ef8315c6cbe71bf531720ce5ffa5ca2717 | 2026-10-22T09:35:11Z |
|
||||
@@ -0,0 +1,99 @@
|
||||
# fix/svc-test-port-governance 任务卡
|
||||
|
||||
任务主记录:PR #694 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/694(merge 37e194871f);状态:已完成(回填);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 33d6ef69212595c680a744549986c19bb6acd6c3
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-svc-test-port-governance(worktree);分支:fix/svc-test-port-governance
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=bb1fb490ff22f2cc7a7edfe11e0400569bf98c4e879d5527493186aa75429f31;triage_receipt=/tmp/port2-triage2.json;receipt_sha256=a8e49e2792cd069b95e4db5bc7f7d492d164ff84c21ef1001c3bbaf3de7ba364
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
<预期结果、受影响入口/模块/契约;明确不做什么。>
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 33d6ef69212595c680a744549986c19bb6acd6c3 | 33d6ef69212595c680a744549986c19bb6acd6c3 | 2026-10-22T06:18:16Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:NOT_RUN(审阅智能体/平台身份、base/head、结论、问题闭合及重放依据)
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-22T06:17:27.856549+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "33d6ef69212595c680a744549986c19bb6acd6c3",
|
||||
"after_head": "33d6ef69212595c680a744549986c19bb6acd6c3",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "33d6ef69212595c680a744549986c19bb6acd6c3",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||
"[repo] branch=main head=33d6ef6921",
|
||||
"[remote] origin/main=33d6ef6921 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=6",
|
||||
"[recycle] merged un-recycled branches: fix/rdm-pin-advance-latest",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 33d6ef6921(完整:33d6ef69212595c680a744549986c19bb6acd6c3)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||
],
|
||||
"output_sha256": "bae6a238774330d77e016166c60b79b796a22ec8cbdfed58574a18058fce3679",
|
||||
"receipt_sha256": "74d9784c1101c6a196fe3571ae41323a102a8d97a92fe15bc3270d00a7a71b7d",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/74d9784c1101c6a196fe3571ae41323a102a8d97a92fe15bc3270d00a7a71b7d.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | d65195bd41317e480c5a0cfa5133b0ad98a48d4b | a1e0c85c0400ce300a847da276e70d520f842844 | 2026-10-22T07:17:59Z |
|
||||
|
||||
## 闭合记录(card-autoclose 回填)
|
||||
|
||||
- 依据:merge 37e194871f4ce8f60c7678b64402bec3541fcbd1(PR #694)已包含于 origin/main,分支成果已落盘;本段为合入后回填。
|
||||
- 合并主题:Merge pull request 'Use dynamic ports in the two remaining hardcoded test bindings' (#694) from fix/svc-test-port-governance into main
|
||||
- 归档:随闭合移入 .task/archive/;任务执行期的过程证据见原任务记录与 PR。
|
||||
@@ -1,253 +0,0 @@
|
||||
# feature/rdm-two-trees-followup 任务卡
|
||||
|
||||
任务主记录:https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/635;状态:进行中;owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 46e331a7a81c94c6f5881c997ae0aca45664c830
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-rdm-two-trees-followup(worktree);分支:feature/rdm-two-trees-followup
|
||||
- 分支类型:initiative;initiative_id:feature-rdm-two-trees-followup;root_target:main;immediate_target:main;accepting_requirements:true
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=ec5195ceec26c05d94600716514c5f306bd3d2aba2a18e4e71f54a660e0b1b83;triage_receipt=/tmp/tt-followup-triage-20261019.json;receipt_sha256=3abe686c520606f4969e17668843666bfafc4cebd7f869995121ab82ed5dd345
|
||||
|
||||
- 授权与交付终点:用户 2026-10-19 在遗留项推进中选定“两棵树后续大任务”;本轮终点为建卡登记剩余项,并把第一个增量 A1(W0 清单守护 + svc 测试动态端口 + W3 余项差距核对)推进到阶段 6 评审包。manual 模式:每个增量须用户阶段 6 人审后才合入;未授权:Backend 发版/tag(X3 公开扩展接口,双钥匙由用户执行)、真实模型与付费请求(W11/W12 另提资源方案)、运行库迁移、部署。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:A1、W3 余项、增量 B(W5/W6/W7a)均通过阶段 6 并合入本分支;下一增量:C(W8/W10,待登记);W7 provider 接线等 X1 tag(用户双钥匙)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
### 章程(charter)
|
||||
|
||||
**用途与目标**:承接 `.task/archive/feature-rdm-two-trees-latest-templates.md`(部分收口,Dev #630)未交付部分,按已批准的阶段 4 方案与阶段 5 计划 r1(`modules/documents/docs/records/2026-10-14-rdm-two-trees-latest-templates/rdm-two-trees-stage5-implementation-test-plan-r1.md` 与 work-observation 增补计划 r1)完成两棵树一网的模板、递归实例、追溯、上下文、审计与正式界面。
|
||||
|
||||
**接纳范围**:W0(DHF 清单守护,原分支 9873a55 未合入部分)、svc 测试动态端口修复、W3 余项(旧数据迁移等,见 A1 差距核对)、W5 追溯网、W7 上下文提供方、W8 RDM 审计投影、W10 正式前端、W11 DHF 全量保真、W12 含真实模型全组合验收、W13 收口,以及 X3/R3/F3/V3 产品链。
|
||||
|
||||
**非目标**:IAM 项目协作与联动权限(feature-iam-role-permission-linked-capabilities 范围);重新设计已批准方案;未经单独授权的发版、迁移、部署与付费请求。
|
||||
|
||||
**分级测试策略**:每增量 SELF 定向测试 + 独立 Review → 用户阶段 6 人审 → PR CI(GATE);阶段 7 只在全目标候选固定后正式验收。
|
||||
|
||||
**生命周期**:增量 A1 → 用户阶段 6 → 后续增量按原计划 B(W5–W7)、C(W8–W10)、D(W11/W12)推进;X3 发版与 W11/W12 资源各自单独授权。
|
||||
|
||||
## 子任务登记
|
||||
|
||||
| 子任务分支 | 必需 | 状态 | PR |
|
||||
|---|---|---|---|
|
||||
| A1:W0 守护 + svc 动态端口 + W3 差距核对(本分支内增量) | 是 | 已合入(本分支 000a2d2;用户 2026-10-19 阶段 6 通过) | 本分支 PR #635(草稿,initiative 收口时合入 main) |
|
||||
| feature/rdm-two-trees-followup-w3-rest | 是 | 已合入(用户 2026-10-20 阶段 6 通过;W3 余项:迁移两次同值、图形不透明块、移动/改名引用、旧批准版回取、重复表格复核;评审包 docs/superpowers/specs/2026-10-19-rdm-two-trees-followup-w3-review.md)。前置条件:W4 开放结构写入口前先补节点种类放置检查;Dev 的 RDM gitlink 改钉在 initiative 收口时统一组合验证 | RDM PR [#451](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/451)(合入 RDM main 76b4c05);本子任务 PR [#641](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/641) |
|
||||
| feature/rdm-two-trees-followup-w5-w7(增量 B:W5/W6/W7a) | 是 | 已合入(用户 2026-10-20 阶段 6 通过:W5 追溯网领域核+命令面、W6 X1 恢复并按批准修订补齐、W7a 纯领域 bundle;独立 Review RDM 0 BLOCKING/6 MAJOR 已修复、Backend 7/7 PASS;评审包 docs/superpowers/specs/2026-10-20-rdm-two-trees-followup-w5-w7-review.md)。W7 provider 接线等 X1 tag(用户双钥匙);Backend MINOR 6 条随接线处理;RDM MINOR 14 条为增量 C 顺手项 | RDM PR [#458](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/458)(合入 RDM main 93badd7e);本子任务 PR [#666](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/666);Backend PR [#231](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Backend/pulls/231)(合入 backend main 7a41f22e) |
|
||||
| B:W5/W7 | 是 | 待登记 | — |
|
||||
| C:W8/W10 | 是 | 待登记 | — |
|
||||
| D:W11/W12/W13 | 是 | 待登记(需资源方案) | — |
|
||||
| X3/R3/F3/V3 | 是 | 待登记(X3 需 Backend 发版,双钥匙) | — |
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| two-trees-followup-a1-w0-guard-and-svc-ports | scripts/dhf_catalog_guard.py、tests/test_dhf_catalog_guard.py、tests/test_svc.py、tests/test_svc_recover.py、tests/test_svc_realcombo.py | config ccf5b7c03ce4;unit 6fe9be3e5516 | implementation | cursor → agy → pi | 1(9075eee055af,fresh_session) | cursor | success | apply ff 8cf4619..000a2d2 | 未触发 | /tmp/leftover-20261019/tt-a1-receipt.json(run b7a8e78a2b6d0d8f6803490c7fa8af2e) |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:适用
|
||||
- 主记录路径/版本:.task/archive/feature-rdm-two-trees-latest-templates.md;modules/documents/docs/records/2026-10-14-rdm-two-trees-latest-templates/(阶段 4 方案、阶段 5 计划 r1)
|
||||
- 对应里程碑:两棵树一网与 DHF 模板(阶段 6 增量 A–D)
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | W0 DHF 清单守护落地 main | 符合:阶段 5 计划 W0;原实现 38ef548 曾 SELF 通过与限域复审 | P1 | 守护单测通过;不宣称任何模板 published | DHF catalog | 纳入 A1 |
|
||||
| R2 | svc 测试动态端口 | 符合:并发 worktree 固定端口误入占用路径(cabac58 证据) | P2 | 原断言保留;test_svc/test_svc_recover 通过 | 无 | 纳入 A1 |
|
||||
| R3 | W3 余项差距核对 | 符合:W3 递归节点主体已在 RDM main(8dd8ebe) | P1 | 逐项列出已交付/缺失与证据 | RDM main | 纳入 A1(仅核对) |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:阶段 4 方案 rdm-two-trees-stage4-design-r1.md(已批准)
|
||||
- 实施计划引用:rdm-two-trees-stage5-implementation-test-plan-r1.md(已批准)W0/W3;A1 评审包见 docs/superpowers/specs/2026-10-19-rdm-two-trees-followup-a1-review.md
|
||||
- 测试计划引用:同上计划 W0/W3 验证节;A1 定向单测
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | R0 46e331a | 完成 | host | 2026-10-19 | 承接部分收口剩余项 |
|
||||
| 需求评估 | 待定 | 待执行 | — | — | — |
|
||||
| 需求表确认 | 待定 | 待执行 | — | — | — |
|
||||
| 方案设计 | 阶段 4 r1 | 沿用已批准方案 | 用户(原任务) | 2026-10-14 | 原卡阶段记录 |
|
||||
| 实施计划 | 阶段 5 r1 | 沿用已批准计划 | 用户(原任务) | 2026-10-14 | 原卡阶段记录 |
|
||||
| 实施与自检 | A1 000a2d2 | 完成(SELF) | cursor worker 实施;host 范围审阅与定向单测 | 2026-10-19 | 收据 tt-a1-receipt.json;67 测试 OK(skipped=16 为真实二进制 NOT_RUN) |
|
||||
| 阶段 6 人审 | A1 679da1a(CI GATE success,test-pg 偶发失败后重跑通过) | 通过 | 用户 | 2026-10-19 | docs/superpowers/specs/2026-10-19-rdm-two-trees-followup-a1-review.md |
|
||||
| 正式测试与验收 | 待定 | 待执行 | — | — | — |
|
||||
| 代码 Review | 待定 | 待执行 | — | — | — |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| A1 | 原 W0/端口修复在最新 main 上可最小适配重新落地 | Dev scripts/tests | 定向单测通过;原断言不删减;不适配则停止并报告 | 用户 2026-10-19 选定 | 守护脚本与单测按 9873a55 原样落地;端口修复按 cabac58 意图在最新 main 重新实现(原补丁不可直接应用),原断言一一改用分配端口未删减;W3 差距见评审包 | 仅测试与守护脚本 | 阶段 6 已通过 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 46e331a7a81c94c6f5881c997ae0aca45664c830 | 46e331a7a81c94c6f5881c997ae0aca45664c830 | 2026-10-19T14:38:46Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:未入队;调度记录:无
|
||||
- entry_id:无;attempt_id:无;窗口执行者:无
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证(完成后填写)
|
||||
|
||||
- 结果:A1 实施完成(000a2d2),评审包已提交,等待用户阶段 6 人审;未合入。
|
||||
- 验证:定向单测 PASS(SELF,67 测试,skipped=16 为真实二进制 NOT_RUN);PR CI(GATE)见合入与清理节;W3 差距为关键字级 SELF。
|
||||
- 影响与剩余项:仅 Dev 测试与守护脚本;剩余项见评审包第 3 节(W3 余项、B、C、D、X3 链)。
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:A1、W3 余项已验收;剩余 B(W5/W7)、C(W8/W10)、D(W11–W13)、X3 产品链
|
||||
- 首选下一任务:B(W5 追溯网 / W7 上下文提供方)子任务登记
|
||||
- 推荐依据:W3 余项已于 2026-10-20 通过阶段 6;B/C 依赖 W3/W4 稳定
|
||||
- 前置条件/待确认:W4 开放结构写入口前须先补节点种类放置检查(W3 评审非阻塞项 1,用户确认);RDM 文件与 IAM link(RDM iam-link-u2)可能重叠,启动前核对
|
||||
- 预期产物/验收条件:按阶段 5 计划 W5/W7 验证节
|
||||
- 重复任务核对:启动前核对
|
||||
- 启动授权:待用户确定
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:NOT_RUN(审阅智能体/平台身份、base/head、结论、问题闭合及重放依据)
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-19T14:37:49.134350+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "46e331a7a81c94c6f5881c997ae0aca45664c830",
|
||||
"after_head": "46e331a7a81c94c6f5881c997ae0aca45664c830",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "46e331a7a81c94c6f5881c997ae0aca45664c830",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||
"[repo] branch=main head=46e331a7a8",
|
||||
"[remote] origin/main=46e331a7a8 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=4",
|
||||
"[recycle] merged un-recycled branches: feature/layered-python-env",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 46e331a7a8(完整:46e331a7a81c94c6f5881c997ae0aca45664c830)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||
],
|
||||
"output_sha256": "6cba29f361cd5d6ad7c8712594c53dd0899e10d1d89a2b510a8e85522b461cfa",
|
||||
"receipt_sha256": "17f97ec95374ca86a9e49b4b7976cf318ae1f7ae5052d8a746ab219907e1be4c",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/17f97ec95374ca86a9e49b4b7976cf318ae1f7ae5052d8a746ab219907e1be4c.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-19T15:01:26.746182+00:00",
|
||||
"command": "python3 -m unittest tests.test_dhf_catalog_guard tests.test_svc tests.test_svc_recover tests.test_svc_realcombo",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-rdm-two-trees-followup",
|
||||
"before_head": "000a2d2ae0e29fecd42ff19bf2b42e430be22ef8",
|
||||
"after_head": "000a2d2ae0e29fecd42ff19bf2b42e430be22ef8",
|
||||
"branch": "feature/rdm-two-trees-followup",
|
||||
"target_branch": "main",
|
||||
"target_head": "2b7fdc39a1f44163f4766701429d61ffd6926f1a",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
".........................[main 579aaee] rdm v2",
|
||||
" 1 file changed, 1 insertion(+)",
|
||||
" create mode 100644 marker",
|
||||
"To /tmp/svc-db-regress-m9jw7ip3/rdmremote.git",
|
||||
" 526900c..579aaee main -> main",
|
||||
"..........................ssssssssssssssss",
|
||||
"----------------------------------------------------------------------",
|
||||
"Ran 67 tests in 44.079s",
|
||||
"",
|
||||
"OK (skipped=16)"
|
||||
],
|
||||
"output_sha256": "cdf6732fa953eedac3fcea3f5d042c64a3d16c93a94f76308a212256b05b75e2",
|
||||
"receipt_sha256": "4642350698dad77b3ab2397ef089219380651672d7f76113650d942b31b2df7a",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/4642350698dad77b3ab2397ef089219380651672d7f76113650d942b31b2df7a.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 2b7fdc39a1f44163f4766701429d61ffd6926f1a | 26e4ea63918b813557022c6bc12c54ff9213e48d | 2026-10-19T15:05:52Z |
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-19T15:06:00.721625+00:00",
|
||||
"command": "python3 -m unittest tests.test_dhf_catalog_guard tests.test_svc tests.test_svc_recover tests.test_svc_realcombo",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-rdm-two-trees-followup",
|
||||
"before_head": "dd2f8b1f240a74a0f467533512b88da541c19999",
|
||||
"after_head": "dd2f8b1f240a74a0f467533512b88da541c19999",
|
||||
"branch": "feature/rdm-two-trees-followup",
|
||||
"target_branch": "main",
|
||||
"target_head": "2b7fdc39a1f44163f4766701429d61ffd6926f1a",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
".........................[main e5428ad] rdm v2",
|
||||
" 1 file changed, 1 insertion(+)",
|
||||
" create mode 100644 marker",
|
||||
"To /tmp/svc-db-regress-e8y1ohso/rdmremote.git",
|
||||
" d78f4bf..e5428ad main -> main",
|
||||
"..........................ssssssssssssssss",
|
||||
"----------------------------------------------------------------------",
|
||||
"Ran 67 tests in 41.981s",
|
||||
"",
|
||||
"OK (skipped=16)"
|
||||
],
|
||||
"output_sha256": "ea93eda4d2d37fcbe97d826cdda982a1a9274f27284dd7a930d09117de251e04",
|
||||
"receipt_sha256": "0279d9b351beb2e54b662d4bc98b2b492c3898e931bd2a995ddb31c3e70f0fdb",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/0279d9b351beb2e54b662d4bc98b2b492c3898e931bd2a995ddb31c3e70f0fdb.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 7e6c544b3c25c510e3d928bfb4515937a1cd6696 | 85624242789c823b96f2adb080e11833f73352e3 | 2026-10-20T11:41:24Z |
|
||||
| 同步(记录前对象) | 33d6ef69212595c680a744549986c19bb6acd6c3 | 1b286ec3e68260d9d25cb3ab651ede31143d5b06 | 2026-10-22T06:35:19Z |
|
||||
@@ -0,0 +1,75 @@
|
||||
# 独立代码 Review:feature/intent-multi-turn-a1(A1 多轮意图闭环)
|
||||
|
||||
- Reviewer:独立 CLI 会话(pi,未参与本分支实施)
|
||||
- 日期:2026-10-22(UTC)
|
||||
- 对象:`git diff 955a32de...9126b09b`(Dev 超级仓);组件焦点 `modules/backend a75783fd..9ee752c3`
|
||||
- 任务卡:`.task/feature-intent-multi-turn-a1.md`(设计决定:run-cancel 为第一业务动作、30min TTL、revision、cmdbus 确认不变)
|
||||
- 性质:findings-only,未改任何代码;只新增本报告文件。
|
||||
|
||||
## 1. 范围与方法
|
||||
|
||||
- 超级仓 diff:`policy/architecture-baseline.json` +3 边、`modules/backend` gitlink a75783fd→9ee752c3、任务卡新增。
|
||||
- backend 组件 diff(3 commits,+3267/−21):新包 `internal/intentsession`、`internal/store/intent_session.go`(表 v52)、`internal/route`(Cancel 裁决)、`internal/server`(`intent_session.go`、`intent_cancel.go`、`server.go` chat 接线)、`cmd/storage-migrate`(copyOrder 补 intent_session)。
|
||||
- 方法:全量逐文件静态精读 + 调用链追踪(chat postMessage → fuseIntentSession → route.Decide → admitIntentCommand → Bus.Dispatch / handleIntentCancel → dispatchCancel;runmgr HITL 转轨判定的 route 契约),测试断言逐条对照审查镜头。
|
||||
|
||||
### 测试执行(诚实出口:NOT_RUN)
|
||||
|
||||
- 本会话**未能复跑**聚焦测试(`go test ./internal/intentsession/... ./internal/route/... ./internal/server/...`):当前工作区组件 checkout 只部分物化——`modules/backend/deps/agent-system-core、error-code-system、logging-system、agent-document-renderer` 为空目录,go.mod replace 闭包无法解析;无网络;本机模块缓存仅有 2026-10-13 伪版本(≠本次 pin 的提交),用它们编译出的结果不能作为本 PR 的证据,故弃用。
|
||||
- 尝试过程可复核:为诊断曾临时在 `modules/backend/deps/` 建指向超级仓 `deps/` 的符号链接并随即删除(超级仓 deps 亦缺上述四个组件,`go build` 报缺 go.mod 为证);现场已恢复,`git status --porcelain` 干净(见 §5)。
|
||||
- 任务卡记录的 verify-a1b/a1c exit=0、批二 CI 全绿:本会话无法重放,按 `[未经证实]` 处理;下述结论全部来自静态审查。按任务单要求未跑 `make test`。
|
||||
|
||||
## 2. 按镜头的结论(spec / security / regressions / tests)
|
||||
|
||||
**spec(多轮闭环)**:缺槽动作 → 澄清(`suspendIntentSession`,intent_session.go:273-310)→ 回答折叠(`fuseIntentSession` → `ApplyAnswer`,slot 齐则合成与完整识别同形的 MatchResult 走同一准入/派发路径,server.go:1252-1258 + intent_session.go:110-155)→ 完成;TTL 30min、写时续期、读侧惰性删除(intentsession.go:12-13、344-357);revision 单调、`ErrStale` 拒绝陈旧回答(intentsession.go:206-212、359-364);双用户/项目/会话键隔离(store 全查询带 owner 过滤)。主链路成立,测试覆盖到每一环。**例外见 B1/M1**。
|
||||
|
||||
**security**:会话不携带授权——补齐后仍走 `admitIntentCommand` + `Bus.Dispatch`,高危确认卡由 cmdbus 收口(TestIntentSessionStillRequiresHighRiskConfirmation 断言确认前处理器不执行);取消经 `run.cancel` 注册命令走同款 Dispatch,卡确认前不记「已取消」(intent_cancel.go:150-186,TestIntentCancelSurfacesHighDangerConfirmCard 覆盖确认→执行全链);跨用户运行 `GetRun(owner,…)` 不可见即澄清、不泄露存在性(intent_cancel.go:73-79,TestIntentCancelNeverResolvesAnotherOwnerRun);候选只列本人本会话未终态运行(intent_cancel.go:97-112)。**例外见 M2**。
|
||||
|
||||
**regressions**:无挂起预期时 fuse 段空转,`TestIntentSessionLegacyResponsesUnchanged` 做逐字节对照;route 契约——cancel 移出 unconsumedKinds 但 `StartFound` 刻意不置位(route.go:181-188),runmgr HITL 转轨判定(runmgr.go:2331-2334 只看 StartFound)行为与 main 一致;U6 准入——fused 结果同样过 `dispatchCommand`(res.Complete())与 `admitIntentCommand`(声明 schema 映射、必填不满足即 Blocked)两道闸,非法/缺失槽位不可能执行。**例外见 M1(累计槽位跨任务泄漏)。**
|
||||
|
||||
**tests**:覆盖面好——触发条件(有/无挂起、同意图/异意图、过期、隔离)、revision、高危确认、legacy 逐字节、显式/会话/歧义靶子、确认卡。缺口:无「同会话内换任务(异意图)再挂起」用例(正是 B1 场景);无真实并发写测试(revision 逻辑覆盖属逻辑等价,可接受);`answerFromRecognition` 显式落槽不看意图归属(intent_session.go:203)无专项用例。
|
||||
|
||||
## 3. Findings
|
||||
|
||||
### Blocker
|
||||
|
||||
**B1(spec/正确性)换任务再挂起时,上一个动作的累计槽位被继承,可把陈旧值派发给新动作。**
|
||||
`Service.Suspend` 无条件合并旧行槽位:`slots = mergeSlots(cur.Slots, slots)`(internal/intentsession/intentsession.go:193-194)。宿主侧 `suspendIntentSession` 对「route 层槽位未就绪」的新动作一律走 Suspend(internal/server/intent_session.go:273-310),不区分新旧预期是否同一动作。可达序列(同会话、TTL 内):
|
||||
1. T1 意图 A(如 `demo.run-cancel`)缺 reason → 挂起,累计 `{run_id: R-7}`;
|
||||
2. T2 用户改做意图 B(如 `demo.purge`,缺 scope)→ fuse 判「异意图、无落槽」不碰会话(intent_session.go:124-127)→ route 澄清 → `Suspend(B)` 继承 `{run_id: R-7}`;
|
||||
3. T3 用户补 scope → `ApplyAnswer` 合并全量槽位判齐(intentsession.go:227)→ `completedRecognition` 以累计槽位派发 B(intent_session.go:245-262);
|
||||
4. `mapSchemaParams` 只丢弃**未声明**键,B 声明过的 `run_id` 会带陈旧值 R-7 进 params(internal/server/intent_admission.go:69-84)——若 run_id 是 B 的必填,准入反而被陈旧值满足。
|
||||
这与包自己的不变量直接冲突(「绝不允许旧槽位漏进新任务」intentsession.go:25;「别的任务的输入绝不混进当前动作」intent_session.go:53、188)——该保护只做在了 fuse 的回答投影上(answerFromRecognition 异意图不并入,intent_session.go:180-208),Suspend 覆写预期时漏了。建议:Suspend 时若 `cur.Expectation` 存在且 `IntentID/Action` 与新预期不同,则丢弃旧累计槽位(或显式要求宿主先 Drop);并补「异意图换任务」回归用例。涉同一镜头的次生风险见 minor m4。
|
||||
|
||||
### Major
|
||||
|
||||
**M2(security/错误路径)`applyCancel` 吞掉存储错误后,可能把「放弃补槽」滑成「立即取消一个真实运行」。**
|
||||
同意图取消的设计语义是只清挂起预期、不派发(intent_session.go:20-24)。但 `applyCancel` 对非 ErrStale 的存储错误同样返回 nil 且错误被丢弃(internal/server/intent_session.go:165-170),`fuseIntentSession` 整体返回无会话语义 → postMessage 继续走 `route.KindCancel` 规划(server.go:1273-1279)。此时挂起行的 `run_id` 槽位经 `sessionRunTarget`→`Binding` 仍解出靶子(intent_session.go:313-327),`planIntentCancel` 对发起人可见即放行;若部署未注册 `run.cancel`,走 `dispatchCancel` 本地路径**立即 `CancelRun`、无任何确认**(intent_cancel.go:164-176)。即:一次瞬时存储故障把「算了不取消」变成「未确认取消 R-7」。建议:applyCancel 对 `err != nil`(非 ErrStale)向上传播错误(writeStoreErr),fail-closed。
|
||||
|
||||
### Minor
|
||||
|
||||
- **m1(文档失实)** `internal/store/intent_session.go:11-12` 声称该表「不进 storage-migrate 的 copyOrder(同 admission_leases)」,但 `cmd/storage-migrate/main.go:311-313` 已把 `intent_session` 加入 copyOrder(且是清单夹具测试 `main_test.go:218` 的强制要求,9ee752c 补得对)。代码正确、注释过期,需同步删改;旧源库缺表已有 `sqliteHasTable` 跳过(main.go:400-402),无行为问题。
|
||||
- **m2(回话如实性)** 总线路径派发被拒(StateRejected)时 `cancel.accepted=false` 但 `cancel.reason` 为空(internal/server/intent_cancel.go:142),与本地路径「失败带原因」不对称;receipt 虽随响应透出,建议把拒绝原因写入 `result.Reason`。
|
||||
- **m3(过期边界 fail-open)** `expires_at` 解析失败得零值时 `live()` 视为「永不过期」(intentsession.go:366-373 + store `parseTime` 容错返回零值)。当前写入方恒写 TTL,风险仅来自自身写坏的数据,但与全文件 fail-closed 纪律不一致:解析失败宜按已过期处理。
|
||||
- **m4(与 B1 联动)** 显式 `set_slot/change_slot` 命令「不看意图归属」直接写入当前挂起预期(intent_session.go:196-203,注释称有意)。B1 修复(换任务即弃旧槽位)落地后,此路径仍可把异动作的落槽命令并入当前预期;建议改为「命令名 ∈ 当前预期 Missing/声明键才采纳」,或补用例锁定现状语义。
|
||||
|
||||
### Notes
|
||||
|
||||
- **n1** 「放弃补槽」轮仍按 KindTurn 起普通回合(server.go:1273-1279 不改 routeKind;1340-1345 仅 KindCancel 拦截),响应同时带 `intent_session` note 与 run——与 main 上 cancel 落 turn 的旧行为一致,测试未固定该组合,建议在任务卡确认一次。
|
||||
- **n2** `completedRecognition` 不搬运 `Risk/RequiresConfirm`(intent_session.go:245-262):全仓 grep 确认 `Decision.RequiresConfirm` 目前无消费者(route.go:114-116 仅透传),确认权在 cmdbus danger,今日无行为影响;将来若有人消费该字段,fused 路径会静默不同——建议留注释或补字段。
|
||||
- **n3** `PutIntentSession` upsert 后读回非原子(store/intent_session.go:74-93),并发双写可能都读到同一新 revision;当前会话请求路径单写者,无实害,建议加注释固化「每会话单写者」前提。
|
||||
- **n4** 未注册 `run.cancel` 的本地回落取消无确认卡——与既有 `POST /api/runs/{id}/cancel` 逐字同款、属设计决定(任务卡决定 1/4 与 intent_cancel.go:15-17 注释一致),非本 PR 引入;注册了命令的部署确认链有测试护栏。记录在案以回应「确认是否被绕过」镜头:**会话路径在命令注册时不绕过确认**。
|
||||
- **n5** 架构基线 3 条新边与实际 import 逐一核对相符(intentsession→intentgate/store;server→intentsession);基线治理测试本会话未复跑(同 §1 NOT_RUN 原因)。
|
||||
- **n6** 任务卡记载的 svc governance 偶发失败为已知预存问题、按任务单不属本次范围,未复跑 `make test`。
|
||||
- **n7** 测试缺口清单:异意图换任务再挂起(B1 回归用例)、applyCancel 存储错误注入、显式落槽跨意图(m4)。
|
||||
|
||||
## 4. 结论
|
||||
|
||||
主链路(挂起→澄清→补齐→同路派发)、过期隔离、revision、双用户隔离、取消三态(显式/会话绑定/歧义→如实澄清)与 cmdbus 确认收口均实现且有测试护栏,route/runmgr 契约保持。但以下问题应先修再合:
|
||||
|
||||
- Blocker B1:跨动作挂起继承旧累计槽位,陈旧值可进新动作派发参数(intentsession.go:193-194;admission 只滤未声明键)。
|
||||
- Major M2:applyCancel 吞存储错误 fail-open,可滑向无确认的本地取消(intent_session.go:165-170 → server.go:1273 → intent_cancel.go:164-176)。
|
||||
- Minor m1–m4 建议随修复一并处理(m1 文档、m2 回话、m3 过期 fail-closed、m4 落槽归属)。
|
||||
|
||||
最终判定(理由如上三条):
|
||||
|
||||
REVIEW VERDICT: BLOCK
|
||||
@@ -0,0 +1,72 @@
|
||||
# Independent Code Review — feature/audit-followups-pin-gate-m5
|
||||
|
||||
- Reviewer: independent CLI review session (audit-followups-independent-review unit; fresh session, not the implementer)
|
||||
- Date: 2026-10-22
|
||||
- Review object: this checkout @ `a2b4cf73` (branch tip `feature/audit-followups-pin-gate-m5`, detached HEAD) versus `origin/main`. No `origin/*` refs exist in this sandbox, so the diff was taken against the merged authoritative main tip `d65195bd` (PR #691 merge, second parent of `a874ee60` "Merge authoritative main into the task branch"); `merge-base(d65195bd, HEAD) = d65195bd`, so `git diff d65195bd...HEAD` ≡ `git diff origin/main...HEAD`.
|
||||
- Scope claims under review:
|
||||
1. Dev: new `scripts/check_pin_regression.sh` wired into `scripts/check-pins.sh` and `scripts/task_pr_check.py`; tests `tests/test_check_pins.py` (+1 case) and `tests/test_check_pin_regression.py` (new, 13 cases). Motivating incident: PR #682 (stale branch silently resetting component pins over main).
|
||||
2. `deps/agent-native` `cff69355` → `f2fb9ffa` (review-M5 fix: Writable preflight rejects subagent delegation).
|
||||
3. Gitlink-only pin advances in `deps/agent-squad` `9edb6ba8` → `b6d87638` (nested native `f2fb9ffa`) and `modules/backend` `7bcbbdb3` → `a75783fd` (merged upstream `7bcbbdb` line + parallel work `443e7af`; nested native `f2fb9ffa`).
|
||||
- Method: read-only review of `git diff d65195bd...HEAD` (9 files, +479/−4) plus per-submodule commit/tree diffs; focused checks run where this worktree permits (see Verification runs). No code was changed; the only write is this report.
|
||||
|
||||
## Findings
|
||||
|
||||
### Blockers
|
||||
|
||||
None.
|
||||
|
||||
### Major
|
||||
|
||||
- **MA1 — The CI leg of the pin-regression gate is a structural no-op; all enforcement rests on local full-clone runs.** All three CI checkout jobs fetch a raw object id shallowly and detach (`.gitea/workflows/ci.yaml:152,336,661` — `git fetch --depth=1 origin <sha>` then `checkout -q --detach FETCH_HEAD`), which writes only `FETCH_HEAD` and never creates `refs/remotes/origin/main` (empirically confirmed in a scratch repo: after a raw-SHA shallow fetch, `git rev-parse --verify origin/main^{commit}` fails). `check_pin_regression.sh:24-27` treats an unresolvable `origin/main` as "warn + exit 0" (documented shallow-checkout escape). Consequently `task_pr_check.py ci` (ci.yaml:162) and the CI `make test` → `test-pins` leg can never fail a PR on pin regression — the exact incident shape #682 (a stale branch merged through CI) would again pass CI today. Mitigation: the delivery policy mandates a local PR check before acceptance/merge, and in a full clone (`make task-pr-check local`) the gate does enforce (verified below). Not blocking — the change is a strict improvement and the local leg is the mandated enforcement point — but the gate's stated purpose ("gitlink 相对 origin/main 不得倒退" as a PR gate) is not achieved in CI. Recommendation: in the reviewed checkout step, additionally fetch the base ref (e.g. `git fetch --depth=1 origin "refs/heads/${GITHUB_BASE_REF}:refs/remotes/origin/main"`) so `pin_regression_problems()` verifies real ancestry at gate time; the submodule materialization already produces full child clones, so the per-component `merge-base --is-ancestor` checks would then work offline.
|
||||
|
||||
### Minor
|
||||
|
||||
- **MI1 — Gitlink removals relative to `origin/main` pass silently.** The gate iterates only HEAD's gitlinks (`check_pin_regression.sh:71-72,95`); a component dropped from the Dev tree (present on base, absent in HEAD) is never compared, and `check-pins.sh` likewise only checks the containers it iterates (a container dropping one of its `deps/*` links is equally invisible). Not the #682 shape (reset, not removal), and "new pin not on base" is an intentional pass (line 74-76), but silent composition shrinkage is adjacent to the incident class this gate exists for. Suggest a follow-up that diffs the base/HEAD gitlink path sets in both directions.
|
||||
- **MI2 — `pin_regression_problems()` runs the gate with no subprocess timeout** (`scripts/task_pr_check.py:222-224`). `ensure_commit`'s `git fetch` (`check_pin_regression.sh:48-54`) can hang on a black-holed connection and would stall the PR check until the runner/job timeout. Low practical likelihood (unchanged pins short-circuit at line 73 before any fetch; CI submodules are full clones), but a `timeout=` on the `subprocess.run` would bound it.
|
||||
- **MI3 — Unreachable-but-confusing fail mode if a CI submodule lacks the old pin.** If `ensure_commit` ever needed to fetch in CI, the component remotes persist only the public URL (credentials live solely inside the checkout step's per-command env), so the fetch fails and the gate fails closed ("missing locally and could not be fetched", lines 80-86) — correct direction, but the PR author sees an unfetchable-pin error whose real cause is credentials/absence. With full submodule clones this should never trigger; noting for diagnosis only.
|
||||
|
||||
### Notes
|
||||
|
||||
- **N1 — Preflight placement is consistent with the sibling F02 checks; "zero side effects" holds for the meaningful claims.** The new check (`deps/agent-native/adapters/native/backend.go:228-230`) sits inside the existing `len(writable) > 0 && profile == exec.ToolProfileWorkspace` block (line 216) before any registry/session/model work. Workdir preparation above it (`MkdirAll`, `INSTRUCTIONS.md`/`TASK.md` writes) predates rejection — identical to the pre-existing exec/MCP/host rejections, and `TestContractWritableRejectsSubagentInPreflight` asserts the substantive zero-side-effect properties: no stream call, no factory call, no deliverable file, no `.agentx` session state.
|
||||
- **N2 — `pin regression ok` (line 100) prints even when `PIN_REGRESSION_SKIP=1` downgraded failures to warns.** Exit status is the contract (0 with warns), and the warns are loud on stderr; cosmetic only.
|
||||
- **N3 — Pre-existing gofmt deviation in `adapters/native/backend.go`** (struct-literal alignment around `runtime.NewAgentSession`, ~line 447). The file at the base pin `cff69355` fails `gofmt -l` identically; the changed hunk (212-230) is clean. Out of scope for this fix.
|
||||
- **N4 — Pre-existing stderr noise `fatal: not a tree object` when a container directory exists but is uninitialized.** Reproduced as `git -C deps/agent-intent ls-tree <sha>` from an empty dir discovering the parent Dev repo (process substitution in `check-pins.sh:68`); pre-dates this branch and does not change its exit status. In this sandbox only `modules/backend`, `deps/agent-native`, `deps/agent-squad` are materialized.
|
||||
- **N5 — Go tests NOT_RUN in this workspace (environment, not code).** `deps/agent-native/go.mod:10,12` replace `error-code-system`/`logging-system` with nested checkouts that are not materialized here, and the pinned commit objects (`a3ee67bb`, `49b5d8be`) are absent from every local object store (Dev, native, squad, backend); network access is forbidden for this unit, so the modules cannot be completed. `GOWORK=off go test ./adapters/native/...` therefore fails at module setup, not in code. Fallbacks applied: both changed files parse (`gofmt -e`) and were line-reviewed; the author-recorded evidence (`verify-m5.json` / `verify-m5-host.json` exit=0, quoted in commit `f2fb9ffa`) is uncorroborated in this session and treated as author-claimed.
|
||||
- **N6 — Composition coherence verified (lens 3).** Nested pins agree at every level: native `f2fb9ffa` (Dev `deps/agent-native` = squad `deps/agent-native` = backend `deps/agent-native`), squad `b6d87638` (Dev = backend), intent `765a7e71` (Dev `deps/agent-intent` = native = squad = backend), core `0cf03446` (Dev `modules/core` = backend `deps/agent-system-core`), command `fe7987dc`, document-renderer `3f259990`, error-code-system `a3ee67bb`, logging-system `49b5d8be`; guidelines/manager deltas (`2688fd2`/`67784f43`/`bd3a7de5` vs Dev `a15db508`/`2c2e3725`) are warn-only by design (`check-pins.sh:57-62`). `bash scripts/check-pins.sh` exits 0 ("pins ok") in this worktree.
|
||||
|
||||
## Spec lens (1) — gate catches the incident shape; M5 preflight is exact
|
||||
|
||||
- Gate trigger: HEAD pin not a descendant of the base pin fails — stale reset (`test_pr682_stale_branch_carrying_old_pin_fails`) and diverged/rebased pins (`test_diverged_pin_fails`) both exit 1 with `moves BACKWARD` + `PR #682` guidance; forward move, same pin, base-pin-absent (`new, not on origin/main`), unresolvable `origin/main`, and `PIN_REGRESSION_SKIP=1` downgrade all exit 0. `task_pr_check.py ci` wiring hard-fails (`PR_CHECK_FAILED`) on regression and passes with the skip env (`test_task_pr_check_ci_hard_fails_on_regression`, `test_task_pr_check_ci_skip_env_passes`). All 19 Python tests pass in this workspace (Verification runs).
|
||||
- M5 preflight: the rejection condition is exactly non-empty Writable ∧ workspace profile ∧ delegation enabled (`backend.go:216,228-229`). Delegation exists iff `b.opts.Subagent != nil` (`runtime/subagent/subagent.go:62-71` no-ops on nil factory; `delegate_task` is registered only in the workspace branch, `backend.go:265`), and Writable reaches `Execute` only via `withWritable(ctx, req.Workspace.Writable)` (`adapters/native/contract.go:66`) — the reasoning profile has no registry at all. Empty-Writable delegation is covered by `TestContractSubagentWithoutWritableStillDelegates` (delegate_task declared, factory invoked with the request fields). Side-effect escape confirmed closed: `tools.ApplyWriteAllowlist` runs before `subagent.Register`, so `delegate_task` never inherited the write-set — rejecting the combination is the right call, and `run_tool_script` cannot reach it either (it only invokes registered tools and bans re-dispatch).
|
||||
|
||||
## Regressions lens (2)
|
||||
|
||||
- `check-pins.sh` behavior when `origin/main` is absent: unchanged — live run exits 0 with "pins ok", the gate only warns (`check_pin_regression.sh:24-27`). The appended call is `|| fail=1` (check-pins.sh:67), so any gate crash also fails closed.
|
||||
- `task_pr_check` callers: no signature/behavior changes to existing functions (`PIN_REGRESSION` const and `pin_regression_problems()` are additive; `cmd_local`/`cmd_ci` append/inject problems only). `scripts/test_affected.py:61-62` and `scripts/task.sh:699` import unaffected names. Remote-failure `Stop` paths still precede the gate (fail-closed elsewhere). `make task-pr-check` runs from the repo root, matching the gate's cwd→toplevel resolution.
|
||||
- Delegation flows without Writable: backend `a75783fd` and squad `b6d87638` contain zero `Writable`/`ContractRequest` usage in non-test Go code (grepped) — no live composition flow can trip the new rejection; the preflight only fires for contract requests that declare both.
|
||||
|
||||
## Scope lens (3)
|
||||
|
||||
Dev diff touches exactly the declared files plus the task card (`.task/**`, host domain): `scripts/check-pins.sh` (+2), `scripts/check_pin_regression.sh` (new, 100 lines), `scripts/task_pr_check.py` (+17/−1 docstring+gate), `tests/test_check_pins.py` (+17), `tests/test_check_pin_regression.py` (new, 187 lines). Submodule ranges are integration-only as declared: native = 1 commit (`f2fb9ff` "Reject subagent delegation under a Writable write-set", parent `cff6935`, no other content); squad = 1 gitlink-only commit (`b6d8763`, `diff-tree` = `deps/agent-native` only); backend = 3 commits (`3ab7cd7` and `92c2b4e` gitlink-only pins; `a75783f` = merge of `7bcbbdb` into the pin line; tree delta vs `7bcbbdb3` is exactly the two nested gitlinks; contains both `7bcbbdb` and `443e7af`). Nothing rides along outside the declared fixes.
|
||||
|
||||
## Tests lens (4)
|
||||
|
||||
New tests assert the trigger conditions, not just exit codes: exact rejection reason string, no stream/factory invocations, no deliverable/session files (native); `moves BACKWARD` / `PR #682` / `PIN_REGRESSION_SKIP=1` message substrings, `ok <path>` / `pin regression ok` / warn lines, and `[verdict] PR_CHECK_FAILED` / `PR_CHECK_OK` plumbing (Dev). `test_check_pins.py` additionally proves the gate is actually wired inside `check-pins.sh` (copies both scripts into the fixture). Offline discipline is good: temp-dir repos, hermetic env, `GIT_CONFIG_NOSYSTEM=1`, env vars scrubbed in setUp, local bare remotes for the fetch paths.
|
||||
|
||||
## Verification runs (this session, foreground)
|
||||
|
||||
| Check | Result |
|
||||
|---|---|
|
||||
| `git diff --stat d65195bd...HEAD` | 9 files, +479/−4 — matches declared scope |
|
||||
| `bash scripts/check-pins.sh` | exit 0, "pins ok"; gate warn-only (no origin/main); guidelines warns only |
|
||||
| `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_check_pin_regression tests.test_check_pins -v` | 19/19 OK (0.796s) |
|
||||
| `cd deps/agent-native && GOWORK=off go test -count=1 ./adapters/native/...` | NOT_RUN — module setup fails on unmaterialized replace targets (`go.mod:10,12`); objects absent everywhere; network forbidden (see N5) |
|
||||
| `gofmt -e` on both changed native files | parse OK; `gofmt -l` deviation pre-existing (N3) |
|
||||
| Raw-SHA shallow fetch → `origin/main` resolvability experiment | does NOT resolve (basis of MA1) |
|
||||
| `make test` | intentionally NOT run per unit packet (known pre-existing `test_svc.SvcDbRecoveryTests.test_addr_change_keeps_database_and_auth` governance failure on clean main `b7636742`, recorded on the task card; CI is the final arbiter) |
|
||||
|
||||
## Verdict
|
||||
|
||||
The pin-regression gate correctly detects the #682 backward-pin shape (and diverged pins) while passing forward/same/new-pin moves and degrading safely without `origin/main` or via the owner-reviewed skip; the M5 preflight rejects exactly the unenforceable Writable+delegation combination with zero behavioral fallout for empty-Writable or non-contract flows; composition pins advance cleanly with no source riding along; the new tests assert real trigger conditions. The one substantive weakness (MA1 — CI leg cannot enforce due to the shallow raw-SHA checkout) is an enforcement-depth gap on a strictly-improving change whose mandated local enforcement point works; MI1–MI3 are bounded follow-ups.
|
||||
|
||||
REVIEW VERDICT: APPROVE
|
||||
Vendored
+1
-1
Submodule deps/agent-native updated: cff69355f7...f2fb9ffa6d
Vendored
+1
-1
Submodule deps/agent-squad updated: 9edb6ba865...b6d87638cc
@@ -0,0 +1,61 @@
|
||||
# 无项目对话(Scratch Session)实施计划
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 任意启用账号可创建彼此隔离的通用智能体会话;无 `project.create` 也能用;不得接入知识树/文档树等项目专属功能。
|
||||
|
||||
**Architecture:** 每个无项目对话 = 带 `settings.host.kind = "scratch"` 的私有空壳 `Project` + 恰好一条 `Conversation`。新建走专用 API(不要求 `project.create`)。列表/侧栏把 scratch 从真项目切换器剔除,归入虚拟分类「无项目对话」。服务端对 scratch 统一 fail-closed 拒绝 RDM/知识发布/成员等。
|
||||
|
||||
**Tech Stack:** Go Backend(store/server/authz)、TypeScript Frontend(store/TaskSidebar)、现有 SQLite projects 表 `settings` JSON。
|
||||
|
||||
**Spec:** [docs/superpowers/specs/2026-10-22-scratch-session-general-agent-design.md](../specs/2026-10-22-scratch-session-general-agent-design.md)
|
||||
|
||||
**实施执行:** agent-per-unit(配置:AGENT-EXECUTION.md);`fallback_to_host: never`。
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- 固有能力不可做成 `role_permissions` 行。
|
||||
- ListProjects 前后端一致过滤 scratch。
|
||||
- RDM 未部署时也须宿主侧拒绝,不能只藏 UI。
|
||||
- 最小充分:不做升级、协作、配额、nullable project_id。
|
||||
|
||||
## Review Focus
|
||||
|
||||
- 无 `project.create` 的创建路径与真项目门禁是否拆开
|
||||
- scratch 上项目专属能力是否服务端 fail-closed
|
||||
- 侧栏是否把 scratch 与真项目分开
|
||||
|
||||
## 文件映射
|
||||
|
||||
| 区域 | 路径 |
|
||||
|------|------|
|
||||
| 标记 helper | Backend store 或 core 小 helper |
|
||||
| 创建 API | `modules/backend/internal/server/server.go` |
|
||||
| 列表过滤 / 1:1 | `modules/backend/internal/store/projects.go`、server createConversation |
|
||||
| 门禁 | `project_lifecycle.go`、cmdbus/RDM、knowledge、跨读 |
|
||||
| 前端 API/store/侧栏 | `api.ts`、`store.ts`、`TaskSidebar.tsx` |
|
||||
|
||||
## Tasks
|
||||
|
||||
### U1 — Backend 创建与固有授权
|
||||
|
||||
- [ ] 实现 `IsScratchProject`;`POST /api/scratch-sessions`
|
||||
- [ ] ListProjects 排除 scratch;禁止 POST /api/projects 伪造 scratch;scratch 下禁止二开会话
|
||||
- [ ] 角色 catalog 不暴露可配置 scratch 动作
|
||||
- [ ] 单测覆盖无/有 project.create、禁用账号、伪造、1:1
|
||||
|
||||
### U2 — Backend 项目专属 fail-closed
|
||||
|
||||
- [ ] `rejectIfScratch` 挂到 projectWork/成员/RDM/knowledge/X1/跨读
|
||||
- [ ] 允许消息/附件/改名置顶归档/级联删除
|
||||
- [ ] 单测正负例
|
||||
|
||||
### U3 — Frontend
|
||||
|
||||
- [ ] `createScratchSession`;sidebarMode;去掉无项目时自动「默认项目」
|
||||
- [ ] TaskSidebar「无项目对话」分类与隐藏项目专属入口
|
||||
- [ ] 前端测试
|
||||
|
||||
### U4 — 验证收口
|
||||
|
||||
- [ ] 最小相关测试通过;task-sync / PR / CI / 独立 Review
|
||||
@@ -1,64 +0,0 @@
|
||||
# 两棵树后续 A1 增量:阶段 6 评审包
|
||||
|
||||
- 任务卡:`.task/feature-rdm-two-trees-followup.md`(initiative,manual:每个增量用户阶段 6 人审后才合入)
|
||||
- 对象:Dev `feature/rdm-two-trees-followup`,base `8cf4619`(R0 `46e331a` + 建卡),A1 实施提交 `000a2d2`
|
||||
- 依据:阶段 5 计划 r1 W0 / W3 节;原分支提交 `9873a55`(W0 守护)、`cabac58`(端口修复)
|
||||
- 证据来源:本文所有通过类结论均为 SELF(作者本地运行与关键字核对),GATE 以 PR HEAD 的 CI 为准
|
||||
|
||||
## 1. A1 交付内容
|
||||
|
||||
| 项 | 变更 | 说明 |
|
||||
|---|---|---|
|
||||
| R1 W0 DHF 清单守护 | 新增 `scripts/dhf_catalog_guard.py`(100755)与 `tests/test_dhf_catalog_guard.py` | 与 `9873a55` 内容逐字节一致(`git diff 9873a55 HEAD` 仅显示脚本 mode 为可执行);守护只校验清单状态,不宣称任何模板 published |
|
||||
| R2 svc 测试动态端口 | `tests/test_svc.py`、`tests/test_svc_recover.py`、`tests/test_svc_realcombo.py` | `cabac58` 补丁已不能在最新 main 直接应用,worker 按其意图重新实现:每个用例经 `available_port()` 分配互不重复的端口,替换 18791–18800、9471–9474 等固定端口与 `free_fixture_port()`;被删除的 7 处断言均以同一断言改用分配端口的形式保留,无断言删减 |
|
||||
|
||||
实施:`agent-exec` cursor worker 第 1 次尝试成功(run `b7a8e78a2b6d0d8f6803490c7fa8af2e`,attempt `9075eee055af`),经 `agent-exec apply` 快进集成,变更路径与单元 `allowed_paths` 一致。
|
||||
|
||||
定向验证(SELF,被测 HEAD `000a2d2`):
|
||||
|
||||
```text
|
||||
python3 -m unittest tests.test_dhf_catalog_guard tests.test_svc tests.test_svc_recover tests.test_svc_realcombo
|
||||
Ran 67 tests ... OK (skipped=16)
|
||||
```
|
||||
|
||||
16 个 skip 为需要 `SVC_REAL_*_BIN` 真实二进制的 realcombo 用例(NOT_RUN),与改动前一致。
|
||||
|
||||
## 2. W3 余项差距核对(RDM `origin/main` = `5a27d78`)
|
||||
|
||||
计划目标测试名:`TestRecursiveBlock|TestLegacyDocMigration|TestTemplateBinding`。
|
||||
|
||||
| 计划验证点 | main 现状 | 依据(关键字级 SELF,需在后续增量精确复核) |
|
||||
|---|---|---|
|
||||
| 递归块、稳定 ID、深于五级 | 已覆盖 | `internal/domain/document_blocks_test.go`:`TestRecursiveBlockInstanceHasIndependentIdentityAndReplays`(depth ≥ 6)、`TestRecursiveBlockReplayRejectsForgedOrFutureNodes`(伪造/未知未来节点拒绝) |
|
||||
| 模板绑定 | 已覆盖 | `internal/rdmapp/read_isolation_boundary_test.go`:`TestTemplateBindingCannotAliasPublishedProjectOrRetainedTransaction` |
|
||||
| 合并单元格 | 结构层已覆盖 | `document_blocks_test.go` 断言 `ColSpan == 2`;`template_asset_test.go` 拒绝非法 `RowSpan` |
|
||||
| 旧 Section.ID 维持为章节身份 | 已覆盖 | `TestLegacySectionBecomesDeterministicRecursiveReadWithoutWrites`、`internal/rdmapp/document_blocks_test.go` `TestLegacyDocSectionNodeIDsSurviveStoreRestart` |
|
||||
| 旧数据库备份副本迁移两次同值(`TestLegacyDocMigration`) | 缺失 | 无该测试名,未检到双次迁移同值用例 |
|
||||
| 图形不透明块 | 缺失 | 测试中未检到 figure/opaque 块 |
|
||||
| 移动/改名保持引用 | 缺失 | 测试中未检到 move/rename 用例 |
|
||||
| 已批准版与旧下载仍可回取 | 缺失 | 未检到对应用例 |
|
||||
| 重复组/重复表格 | 部分 | 关键字出现在上述两个测试文件,是否覆盖“重复表格”语义待精确复核 |
|
||||
| 完成条件:真实项目实例表达新项目建议书及用户需求 12 组要求 | 未验证 | 依赖 W11 DHF 资产 published 状态,当前只能验证实例机制 |
|
||||
|
||||
结论:W3 主体(递归实例、模板绑定、旧 Section 身份)已在 RDM main;余项集中在旧数据迁移幂等、图形块、移动/改名引用与旧批准版回取。建议作为子任务“W3 余项”在 RDM 仓单独登记实施。
|
||||
|
||||
## 3. 剩余项与门禁
|
||||
|
||||
| 剩余项 | 门禁 |
|
||||
|---|---|
|
||||
| W3 余项(上表缺失 4 项 + 1 项待复核) | 下一增量,RDM 仓 worker 实施 |
|
||||
| B:W5 追溯网、W7 上下文提供方 | 依赖 W3/W4 稳定 |
|
||||
| C:W8 RDM 审计投影、W10 正式前端 | W8 接入宿主需 W9;W10 需 W3–W9 合同稳定 |
|
||||
| D:W11 DHF 全量保真、W12 真实模型全组合验收、W13 收口 | 需资源方案与付费/真实模型授权 |
|
||||
| X3/R3/F3/V3 产品链 | X3 需 Backend 公开扩展发版,属双钥匙操作,由用户执行 |
|
||||
|
||||
## 4. Review 发现(非阻塞)
|
||||
|
||||
- `tests/test_svc_realcombo.py` 新增的 `SVC_RCFWEB_ADDR` 环境变量不被 `svc.sh` 读取(伴生 `rcfweb` 由 `deploy --kind static` 自行取端口),属无效但无害配置,仅位于默认 NOT_RUN 的真实 RDM 用例;可在后续增量顺带删除。
|
||||
- `available_port()` 绑定后即关闭 socket,与 svc.sh 实际绑定之间存在小窗口竞争(docstring 已说明);相较固定端口已消除并行 worktree 间的确定性冲突。
|
||||
|
||||
## 5. 请用户阶段 6 判定
|
||||
|
||||
- 通过:A1 进入 PR CI(GATE)与合入窗口,按 #518 排队合入。
|
||||
- 修改:指出需调整项,按新单元重新实施。
|
||||
- W3 余项是否按第 2 节登记为下一增量。
|
||||
@@ -1,42 +0,0 @@
|
||||
# 两棵树后续 W3 余项:阶段 6 评审包
|
||||
|
||||
- 状态:**用户阶段 6 通过(2026-10-20)**。确认第 3 节切分:W4 开放结构写入口前必须先补节点种类放置检查。RDM PR [#451](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/451) 经用户授权已合入 RDM main(`76b4c05`);本子任务 PR 合入 initiative 分支。
|
||||
- 日期:2026-10-19;作者:host;子任务分支:Dev `feature/rdm-two-trees-followup-w3-rest`(父 `feature/rdm-two-trees-followup`)、RDM `feature/rdm-two-trees-followup-w3-rest`(base RDM main `5a27d78`)。
|
||||
- 依据:[阶段 5 计划 W3](../../../modules/documents/docs/records/2026-10-14-rdm-two-trees-latest-templates/rdm-two-trees-stage5-implementation-test-plan-r1.md)(已批准);[A1 评审包第 2 节差距核对](2026-10-19-rdm-two-trees-followup-a1-review.md)。
|
||||
|
||||
## 1. 本增量交付
|
||||
|
||||
| 差距项(A1 第 2 节) | 交付 | RDM 提交 | 目标测试 |
|
||||
|---|---|---|---|
|
||||
| 图形不透明块 | `ContentBlock.Kind` 新增 `figure`:不透明叶子,禁子块/重复/跨格,必须引用带 SHA-256 的附件或 Word 槽位;实例化、重放、`NodesOf` 逐字节保持 | `013b8b1` | `TestRecursiveBlockFigure*` |
|
||||
| 重复表格(待复核) | 补精确用例:重复行与 v2 重复章节通过校验、实例节点 ID 各异、保留 `Repeat` 与 `RowSpan/ColSpan`、可重放;非法位置拒绝 | `013b8b1` | `TestRecursiveBlockRepeat*` |
|
||||
| 移动/改名保持引用 | 领域原语 `MoveNode`/`RenameNode`,新事件 `doc.node.move`/`doc.node.rename`:节点 ID、模板块、内容不变;兄弟序连续重排;文档结构修订 +1,乐观并发;拒绝环、陈旧修订、旧章节节点、空标题、只读日志;live 与 replay 共用同一算法;前缀重放可取回更早结构 | `96d53f6` | `TestRecursiveBlockMove*`、`TestRecursiveBlockRename*`、`TestRecursiveBlockMoveRenameNodeRefsSurviveStoreRestart` |
|
||||
| 旧库备份迁移两次同值;已批准版与旧下载可回取 | 新增 `TestLegacyDocMigration*`:旧形态库(文档/章节/版本/批准)复制为备份副本后两次独立打开与重放,事件行、序号、迁移版本与 `NodesOf` 一致,第二次不写入;未知未来事件类型在启动时写前拒绝;迁移后已批准版与审批提交下载字节/哈希不变(只增测试,未改生产代码) | `f3fa0c0` | `TestLegacyDocMigration*` |
|
||||
| Dev:删除无效 `SVC_RCFWEB_ADDR` | `tests/test_svc_realcombo.py` 删除 `svc.sh` 从不读取的 `SVC_RCFWEB_ADDR` | Dev `80ac3fa` | `py_compile` |
|
||||
|
||||
## 2. 验证
|
||||
|
||||
- host 本地验证(SELF)@RDM `f3fa0c0`:`gofmt`、`go vet ./internal/domain/... ./internal/rdmapp`、`git diff --check` 通过;目标选择器 `GOWORK=off go test ./internal/domain ./internal/rdmapp -run 'TestRecursiveBlock|TestLegacyDocMigration|TestTemplateBinding' -count=1` 20 项 PASS;`./internal/domain/... ./internal/rdmapp` 全量 PASS。
|
||||
- CI(GATE):RDM 草稿 PR [#451](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/451) 与本子任务 PR 当前 HEAD 检查(见 PR)。
|
||||
- 独立 Review(全新会话 code-reviewer,只接收需求、base/head 与 diff;评审者自跑为 SELF):`5a27d78..f3fa0c0` **PASS-with-fixes,无阻塞项**。确认:克隆与摘除无共享切片、校验全过才写入;live 与 replay 共用算法;伪造节点 ID/旧章节/未知 op 被拒;`validateDocNodes` 对既有调用方等价;无新增写入口;测试断言非空。非阻塞项(建议 W4 开放写入口前修):
|
||||
1. 移动不检查节点种类放置关系(row 可移出 table、cell 可移出 row),`rendererNodeKind` 会照常渲染为表格行;应在 `movedDocNodes` 加放置检查并把相应测试改为预期拒绝——**列为 W4 写入口的前置条件**。
|
||||
2. `TestLegacyDocMigration` 在 WAL 模式下计算哈希可能漏掉 `-wal` 中的写入。
|
||||
3. Postgres 用例的“第二次不写库”写入次数硬编码为 0。
|
||||
4. `figure` 在渲染时退化为空段落(超出本增量需求,导出前须映射或显式报错)。
|
||||
另:移动/改名事件 `Target` 与载荷 `DocID` 未做一致性校验;重放查找不过滤已删除文档(当前无删除路径)。
|
||||
|
||||
## 3. 风险与需人审确认
|
||||
|
||||
1. **与 W4 的边界**:移动/改名只提供领域原语,没有 HTTP 路由、插件命令或 rdmapp 写入口;按阶段 5 计划,结构变更须经 W4 结构提案与人工确认后才能调用。W4 开放写入口前须先补节点种类放置检查(独立 Review 非阻塞项 1)。请确认这一切分。
|
||||
2. **合入冲突**:`internal/domain/template_asset.go`(figure 校验)同时被在途的 project-access-lifecycle 任务修改;本增量只新增一个 case 和一个函数,后合入的一方需要按最新 main 解决冲突。
|
||||
3. **事件契约新增**:两种新事件对旧二进制属于未知未来事件,旧版本读到时会拒绝启动写入(符合计划的“未知未来事件拒绝”);部署顺序须先升级再产生新事件。
|
||||
4. **完成条件中的业务项**:“真实项目实例表达新项目建议书及用户需求 12 组要求”依赖 W11 DHF 资产 published,本增量只验证实例机制,不宣称正式标准模板可用。
|
||||
|
||||
## 4. 执行记录
|
||||
|
||||
| 单元 | CLI/候选 | 终态 | 收据(源码外) |
|
||||
|---|---|---|---|
|
||||
| w3-rest-u1-figure-opaque-repeat-table | Cursor attempt 1 | success | /tmp/leftover-20261019b/w3-u1-receipt.json |
|
||||
| w3-rest-u2-node-move-rename-refs | Cursor attempt 1 | success | /tmp/leftover-20261019b/w3-u2-receipt.json |
|
||||
| w3-rest-u3-legacy-migration-approved-retrieval | Cursor run 1 `failed-unknown`(host 在其运行期间集成 u1,HEAD 变化);用户授权全新 run 2 | run 2 `blocked`(隔离区缺 `pins/` 无法编译 rdmapp);修正单元包后经用户授权 run 3 success | /tmp/leftover-20261019b/w3-u3-receipt*.json |
|
||||
| w3-rest-dev-drop-svc-rcfweb-addr | Cursor run 1 `invalid-config`(单元包未就绪)、run 2 `failed-unknown`(Cursor 连接反复失败);用户授权全新 run 3 | success | /tmp/leftover-20261019b/w3-dev-receipt*.json |
|
||||
@@ -1,33 +0,0 @@
|
||||
# 两树一网增量 B(W5/W6/W7a)阶段 6 评审包 r1
|
||||
|
||||
日期:2026-10-20。状态:独立 Review 通过,**待用户阶段 6 人审**。人审通过前不合入任何分支。
|
||||
|
||||
## 1. 范围与结论一览
|
||||
|
||||
| 工作项 | 内容 | 交付分支/HEAD | 独立评审 | 修复 |
|
||||
|---|---|---|---|---|
|
||||
| W5a 领域核 | 受控边 revision 锁定、候选/确认语义、升版 pending-recheck、AnalyzeImpact/RecomputeImpact 传递闭包(去环/截断/水位/不可变快照)、重放一致 | RDM `feature/rdm-two-trees-followup-w5-w7` @ a7a6df0 | 0 BLOCKING | — |
|
||||
| W5b 命令面 | rdm.compliance.impact.analyze/get/recompute、rdm.trace.coverage、rdm.trace.pending-recheck | 同上 @ 2e5fcb7 | 0 BLOCKING | — |
|
||||
| W7a 纯领域 | ProjectContextBundle 组装/失效/渐进证据引用;零基座消费(tag 前约束达成) | 同上 @ 8327603 | 0 BLOCKING | — |
|
||||
| RDM 评审修复 | 评审 MAJOR M1–M6(copyState/快照 ID 碰撞/端点校验/生产写入路径/空分析/切片别名) | 同上 @ 04991edb | 修复后 domain+rdmapp+plugins 全绿 | ✅ |
|
||||
| W6 X1 恢复 | 候选 6ba53e4 合并 main 后按批准修订审计补齐(消费点全覆盖/入模门禁/存储去原文边界/负例矩阵) | Backend `feature/w6-x1-source-recheck` @ 8c410b9(draft PR #231) | **7/7 硬需求 PASS,0 BLOCKING 0 MAJOR**(仅 6 MINOR 备忘) | — |
|
||||
|
||||
## 2. 证据
|
||||
|
||||
- 全部实施经 agent-exec 有界单元(cursor,各 1 次尝试),收据:`~/.cache/agent-system-dev/agent-exec-receipts/{w5a,w5b,w7a,w6-x1-recheck-completion,w5-majors-repair}-*.json`;评审收据同目录。
|
||||
- SELF:RDM gofmt/vet + domain(0.07s)/rdmapp(79s)/plugins(3.1s) 全绿 @04991edb;Backend build/vet + store/runmgr/workflow/extension 全绿 @8c410b9;backend CI(PR #231)vet-and-test+governance success @8c410b9。
|
||||
- 评审报告:RDM `docs/reviews/2026-10-20-increment-b-rdm-review.md`(209 行,逐项 PASS/FAIL);Backend `docs/reviews/2026-10-20-w6-x1-recheck-review.md`(235 行,7 硬需求全 PASS)。
|
||||
- 权威 GATE:合入前各 PR HEAD 的 CI(Backend #231 已绿;Dev/RDM PR 在人审后创建)。
|
||||
|
||||
## 3. 已知限制与门禁
|
||||
|
||||
- **W7 provider 接线未做**:按计划但书等待 X1 公开 tag(用户双钥匙);当前仅纯领域摘要与测试,不宣布自动注入完成。
|
||||
- Backend MINOR 6 条(文档/诊断细分/补测建议)已记录于评审报告,不阻塞;建议随 W7 接线一并处理。
|
||||
- RDM MINOR 14 条见评审报告;不阻塞合入,随增量 C 顺手项处理。
|
||||
- W6 迁移与 W9 的 v31/v32 顺序:候选分支已在合并 main 后重排,串行合入时按 store 测试为准。
|
||||
|
||||
## 4. 请用户阶段 6 裁定
|
||||
|
||||
1. W5+W6+W7a 交付是否通过(对应 RDM/Backend 分支保留,人审通过后走各 PR 合入门禁)。
|
||||
2. X1 公开 tag 是否发布(双钥匙)——通过后 W7 provider 接线另行派发。
|
||||
3. RDM 评审 MINOR 14 条作为增量 C 顺手项的处理安排。
|
||||
@@ -0,0 +1,108 @@
|
||||
# 无项目对话(Scratch Session)设计
|
||||
|
||||
- 状态:**已批准**(对话设计第 1–4 节,2026-10-22)。
|
||||
- 日期:2026-10-22;作者:host(设计整理);来源等级:SELF。
|
||||
- 配套计划:[实施计划](../plans/2026-10-22-scratch-session-general-agent-plan.md)。
|
||||
|
||||
## 1. 目标与非目标
|
||||
|
||||
**目标**
|
||||
|
||||
- 任意**启用账号**可创建「无项目对话」,当作通用智能体使用(多轮对话)。
|
||||
- 无 `project.create`(角色「项目访问」)的用户也能完成该路径,不被「默认项目」创建 403 卡住。
|
||||
- 各无项目对话彼此**独立**:不共享文档、上下文、知识等。
|
||||
- **不得**接入知识树、文档树、成员、RDM 等项目专属功能。
|
||||
|
||||
**非目标(本轮)**
|
||||
|
||||
- scratch → 真项目升级 / 事后绑定 RDM
|
||||
- 分享、协作、邀请进空壳项目
|
||||
- 配额、TTL、自动清理
|
||||
- 将 `conversations.project_id` 改为可空等大 schema 重构
|
||||
|
||||
## 2. 已锁定决策
|
||||
|
||||
| 项 | 决策 |
|
||||
|---|---|
|
||||
| 数据模型 | 方案 A:每个无项目对话 = 一个轻量私有空壳 `Project` + 恰好一条 `Conversation` |
|
||||
| UI | 侧栏虚拟分类「无项目对话」下列会话;**不**进入真项目切换器 |
|
||||
| 谁可建 | 所有启用账号(有无 `project.create` 均可) |
|
||||
| 权限 | **固有能力**,不进角色矩阵,**不可被管理员修改**;与可配置的 `project.create` 完全分离 |
|
||||
|
||||
## 3. 数据模型与权限
|
||||
|
||||
### 3.1 空壳项目约定
|
||||
|
||||
- 仍用现有 `projects` 表;`settings` 写入稳定标记:`host.kind = "scratch"`(解析仅经统一 helper,例如 `IsScratchProject`)。
|
||||
- 每个无项目对话 = **一个 scratch 项目 + 恰好一条会话**(创建时原子写入;禁止在 scratch 下再建第二会话)。
|
||||
- 项目 `owner` = 当前账号;不建成员、不共享、不邀请。
|
||||
- 会话默认 `crossPrivate = true`。
|
||||
|
||||
### 3.2 权限
|
||||
|
||||
| 动作 | 门禁 |
|
||||
|------|------|
|
||||
| `POST /api/projects` 创建真项目 | 仍要 `project.create`(不变) |
|
||||
| 创建无项目对话 | 启用账号即可;**不是**角色可配置动作 |
|
||||
| scratch 上读写会话/发消息 | 仅 owner |
|
||||
| scratch 升成真项目 / 绑 RDM | 本轮不做(明确拒绝) |
|
||||
|
||||
角色 catalog、`PUT .../permissions`、角色编辑 UI **不得**出现或改写「无项目对话」能力。关掉 `project.create` 不影响 scratch 创建。
|
||||
|
||||
### 3.3 专用创建 API
|
||||
|
||||
`POST /api/scratch-sessions` → `{ project, conversation }`
|
||||
|
||||
- 内部:插 scratch 项目 → 建唯一会话 → **不**触发 RDM provision。
|
||||
- **禁止**经 `POST /api/projects` 伪造 scratch 标记。
|
||||
|
||||
### 3.4 列表语义
|
||||
|
||||
- 真项目列表 / 侧栏项目切换器:**过滤掉** scratch。
|
||||
- 「无项目对话」分类:按会话列出(标题来自会话名)。
|
||||
|
||||
## 4. 服务端门禁(禁用项目专属能力)
|
||||
|
||||
判定:`IsScratchProject(p)` ≡ `settings.host.kind == "scratch"`。
|
||||
|
||||
**一律拒绝(scratch 上)**
|
||||
|
||||
- RDM 绑定 / provision;`rdm.*` 命令对 scratch 的 projectId → 403/400
|
||||
- 知识树 / 文档树 / 法规树 / 驾驶舱 / 模板实例化
|
||||
- 成员、职责、项目权限矩阵、项目归档协作
|
||||
- 因「拥有 scratch」而授予 `project.create` 联动能力
|
||||
- Project Context(X1)注入(视为未启用,走普通通用智能体组装)
|
||||
- 跨会话读:scratch 会话不可被对端读出,也不可读其他会话
|
||||
- 在已有 scratch 下再建会话
|
||||
- 普通创建项目接口伪装 scratch
|
||||
|
||||
**允许(通用智能体最小集)**
|
||||
|
||||
- 本会话消息、附件(仅挂本 scratch,不进知识发布)
|
||||
- 会话改名 / 置顶 / 归档 / 删除(删除可级联删空壳项目)
|
||||
- 普通 LLM 对话;显式 `@knowledge` 对 scratch **关闭**
|
||||
|
||||
实现:集中 `rejectIfScratch` / `requireNonScratch`,挂在项目工作、RDM 分发、knowledge、成员 API;不能只藏 UI。
|
||||
|
||||
## 5. 前端信息架构
|
||||
|
||||
- 项目切换器:真项目列表 + 虚拟项「无项目对话」。
|
||||
- 选中「无项目对话」:列出本人所有 scratch 会话;「+ 新建对话」调用 scratch API。
|
||||
- 打开 scratch 会话时:隐藏项目设置、模板生成、依赖当前真项目的 RDM 入口。
|
||||
- `createTask`:无真项目时**不再**自动 `createProject('默认项目')`;无建项权或用户在无项目对话视图时走 scratch API。
|
||||
- 无建项权用户主路径:登录 → 默认落在「无项目对话」→ 可直接新建/继续对话。
|
||||
|
||||
## 6. 验收标准
|
||||
|
||||
1. 无建项权账号可进入「无项目对话」并完成多轮通用对话,不因默认项目 403。
|
||||
2. 有建项权账号同样可建无项目对话;真项目创建仍只认 `project.create`。
|
||||
3. 同一用户两个 scratch 会话互不共享附件知识库、上下文条目、跨会话读取。
|
||||
4. scratch 上 RDM/知识树/文档树/成员/项目设置类能力服务端拒绝;UI 无入口。
|
||||
5. 角色权限 UI / API 不能出现或改写该能力。
|
||||
6. scratch 不出现在项目切换器。
|
||||
7. 无法在已有 scratch 项目下再建第二条会话。
|
||||
|
||||
## 7. 最小验证
|
||||
|
||||
- Backend:创建门禁、RDM/knowledge 拒绝、ListProjects 过滤、1:1 约束单测。
|
||||
- Frontend:侧栏分组、无建项权主路径、scratch 视图隐藏项目入口的测试。
|
||||
+1
-1
Submodule modules/backend updated: 9b40595728...9ee752c380
+1
-1
Submodule modules/frontend updated: 113103f028...279b2d02a2
+1
-1
Submodule modules/ui updated: fadabceade...df1c3ed8d6
@@ -36,6 +36,8 @@
|
||||
"backend/hostapi backend/store",
|
||||
"backend/ingest backend/vision",
|
||||
"backend/instanceguard backend/store",
|
||||
"backend/intentsession backend/intentgate",
|
||||
"backend/intentsession backend/store",
|
||||
"backend/pgbus backend/bus",
|
||||
"backend/pgbus backend/store",
|
||||
"backend/plugins backend/settings",
|
||||
@@ -76,6 +78,7 @@
|
||||
"backend/server backend/execenv",
|
||||
"backend/server backend/ingest",
|
||||
"backend/server backend/intentgate",
|
||||
"backend/server backend/intentsession",
|
||||
"backend/server backend/plugins",
|
||||
"backend/server backend/profiles",
|
||||
"backend/server backend/projectscope",
|
||||
|
||||
@@ -64,6 +64,8 @@ for container in "${containers[@]}"; do
|
||||
done < <(git -C "$container" ls-tree "$commit" deps/)
|
||||
done
|
||||
|
||||
bash "$root/scripts/check_pin_regression.sh" || fail=1
|
||||
|
||||
if [ "$fail" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Executable
+100
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env bash
|
||||
# 组合指针不倒退:HEAD 中每个 gitlink(deps/*、modules/*)相对 origin/main
|
||||
# 只能前进——新 pin 必须以 origin/main 的旧 pin 为祖先。事故原型:PR #682
|
||||
# 合入了过期分支,其树把五个组件 pin 重置回批次前的值,静默丢掉了 main 上
|
||||
# batch-1 的组合。
|
||||
#
|
||||
# ┌─ 逃生口(须 owner Review)──────────────────────────────────────────────┐
|
||||
# │ PIN_REGRESSION_SKIP=1 声明这是一次有意的降级:失败改为醒目的 warn 并 │
|
||||
# │ 退出 0。只用于经组件 owner 审阅的刻意回退,PR │
|
||||
# │ 描述须写明原因;不得用来绕过过期分支。 │
|
||||
# └─────────────────────────────────────────────────────────────────────────┘
|
||||
#
|
||||
# 其他开关:
|
||||
# PIN_REGRESSION_NO_FETCH=1 旧 pin 在子仓本地缺失时不尝试 fetch(测试/离线用)。
|
||||
#
|
||||
# 在当前工作目录所属仓库的顶层运行。origin/main 本地不可解析(浅检出)时只告警、退出 0。
|
||||
set -euo pipefail
|
||||
root=$(git rev-parse --show-toplevel)
|
||||
cd "$root"
|
||||
|
||||
base=origin/main
|
||||
skip=${PIN_REGRESSION_SKIP:-0}
|
||||
|
||||
if ! git rev-parse --verify -q "$base^{commit}" >/dev/null; then
|
||||
echo "warn: $base is not resolvable locally; pin regression check skipped" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
fail=0
|
||||
|
||||
declare -A main_pins=()
|
||||
while read -r mode _typ sha path; do
|
||||
[ "$mode" = "160000" ] || continue
|
||||
main_pins["$path"]=$sha
|
||||
done < <(git ls-tree -r "$base")
|
||||
|
||||
# 子仓须是独立检出的仓库;未初始化时 git -C 会落到 Dev 自身。
|
||||
own_repo() {
|
||||
local path="$1" top
|
||||
top=$(git -C "$path" rev-parse --show-toplevel 2>/dev/null) || return 1
|
||||
[ "$top" = "$root/$path" ]
|
||||
}
|
||||
|
||||
has_commit() {
|
||||
git -C "$1" cat-file -e "$2^{commit}" 2>/dev/null
|
||||
}
|
||||
|
||||
ensure_commit() {
|
||||
local path="$1" sha="$2"
|
||||
has_commit "$path" "$sha" && return 0
|
||||
[ "${PIN_REGRESSION_NO_FETCH:-0}" = "1" ] && return 1
|
||||
git -C "$path" fetch -q origin "$sha" >/dev/null 2>&1 || true
|
||||
has_commit "$path" "$sha"
|
||||
}
|
||||
|
||||
regress() {
|
||||
if [ "$skip" = "1" ]; then
|
||||
echo "warn: PIN_REGRESSION_SKIP=1 — $* (deliberate downgrade; requires owner review)" >&2
|
||||
else
|
||||
echo "pin: $*" >&2
|
||||
fail=1
|
||||
fi
|
||||
}
|
||||
|
||||
while read -r mode _typ new path; do
|
||||
[ "$mode" = "160000" ] || continue
|
||||
old=${main_pins["$path"]:-}
|
||||
if [ -z "$old" ]; then
|
||||
echo "ok $path (new, not on $base)"
|
||||
continue
|
||||
fi
|
||||
if [ "$old" = "$new" ]; then
|
||||
echo "ok $path"
|
||||
continue
|
||||
fi
|
||||
if ! own_repo "$path"; then
|
||||
regress "$path is not checked out; cannot verify ${new:0:12} descends from $base ${old:0:12}"
|
||||
continue
|
||||
fi
|
||||
if ! ensure_commit "$path" "$old"; then
|
||||
regress "$path $base pin $old is missing locally and could not be fetched; cannot verify no regression"
|
||||
continue
|
||||
fi
|
||||
if ! ensure_commit "$path" "$new"; then
|
||||
regress "$path HEAD pin $new is missing locally and could not be fetched; cannot verify no regression"
|
||||
continue
|
||||
fi
|
||||
if git -C "$path" merge-base --is-ancestor "$old" "$new"; then
|
||||
echo "ok $path (forward ${old:0:12}..${new:0:12})"
|
||||
continue
|
||||
fi
|
||||
regress "$path moves BACKWARD: $base is ${old:0:12}, HEAD is ${new:0:12} (not a descendant)." \
|
||||
"This is the PR #682 pattern (a stale branch resetting pins over main): merge $base and re-pin," \
|
||||
"or set PIN_REGRESSION_SKIP=1 for an owner-reviewed deliberate downgrade"
|
||||
done < <(git ls-tree -r HEAD)
|
||||
|
||||
if [ "$fail" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "pin regression ok"
|
||||
@@ -1,244 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare the DHF inventory, immutable originals and one RDM asset catalog.
|
||||
|
||||
This checks provenance and publication metadata. It cannot decide document
|
||||
meaning or Word rendering fidelity; those require the per-asset review.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from collections import Counter
|
||||
from datetime import datetime
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
SOURCE_PREFIX = (".templates", "DHF 文件模板")
|
||||
STATES = {"needs-review", "blocked", "published", "retired"}
|
||||
SHA256 = re.compile(r"[0-9a-f]{64}\Z")
|
||||
COMMIT = re.compile(r"[0-9a-f]{40}\Z")
|
||||
RFC3339 = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})\Z")
|
||||
|
||||
|
||||
def digest(raw):
|
||||
return hashlib.sha256(raw).hexdigest()
|
||||
|
||||
|
||||
def source_path(dev_root, source):
|
||||
path = Path(source)
|
||||
if (path.is_absolute() or path.parts[:2] != SOURCE_PREFIX
|
||||
or ".." in path.parts or path.suffix.lower() not in {".doc", ".docx"}):
|
||||
raise ValueError(f"unsafe or non-Word source path: {source}")
|
||||
segment = dev_root
|
||||
for part in path.parts:
|
||||
segment = segment / part
|
||||
if segment.is_symlink():
|
||||
raise ValueError(f"source path symlink: {source}")
|
||||
absolute = dev_root / path
|
||||
if not absolute.resolve().is_relative_to(dev_root.resolve()) or absolute.is_symlink():
|
||||
raise ValueError(f"source path leaves the Dev tree: {source}")
|
||||
return absolute
|
||||
|
||||
|
||||
def package_digest(root, file_ref):
|
||||
relative = file_ref.get("path")
|
||||
expected = file_ref.get("sha256")
|
||||
if not isinstance(relative, str) or not isinstance(expected, str) or not SHA256.fullmatch(expected):
|
||||
raise ValueError("published asset file reference missing path or hash")
|
||||
path = Path(relative)
|
||||
if path.is_absolute() or ".." in path.parts or path == Path("."):
|
||||
raise ValueError("published asset file reference is unsafe")
|
||||
segment = root
|
||||
for part in path.parts:
|
||||
segment = segment / part
|
||||
if segment.is_symlink():
|
||||
raise ValueError("published asset file symlink")
|
||||
absolute = root / path
|
||||
if not absolute.resolve().is_relative_to(root.resolve()) or absolute.is_symlink() or not absolute.is_file():
|
||||
raise ValueError("published asset file missing or escapes catalog")
|
||||
if digest(absolute.read_bytes()) != expected:
|
||||
raise ValueError("published asset file hash mismatch")
|
||||
return expected
|
||||
|
||||
|
||||
def audit(dev_root, inventory, catalog, expected_count, require_published=False,
|
||||
catalog_root=None, identity_baseline=None):
|
||||
records = inventory.get("templates")
|
||||
assets = catalog.get("assets")
|
||||
if not isinstance(records, list) or not isinstance(assets, list):
|
||||
raise ValueError("inventory/templates or catalog/assets is missing")
|
||||
if len(records) != expected_count or len(assets) != expected_count:
|
||||
raise ValueError(f"expected {expected_count} logical sources and assets")
|
||||
by_path = {}
|
||||
ids = set()
|
||||
for asset in assets:
|
||||
provenance = asset.get("sourceProvenance") or {}
|
||||
path = provenance.get("path")
|
||||
template_id = asset.get("templateId")
|
||||
if path in by_path or not isinstance(path, str):
|
||||
raise ValueError("duplicate or missing provenance path")
|
||||
if template_id in ids or not isinstance(template_id, str) or not template_id:
|
||||
raise ValueError("duplicate or missing logical template ID")
|
||||
by_path[path] = asset
|
||||
ids.add(template_id)
|
||||
if identity_baseline is not None:
|
||||
prior = identity_baseline.get("assets")
|
||||
if not isinstance(prior, list) or len(prior) != expected_count:
|
||||
raise ValueError("identity baseline is incomplete")
|
||||
prior_ids = {}
|
||||
for asset in prior:
|
||||
if not isinstance(asset, dict):
|
||||
raise ValueError("identity baseline has invalid asset")
|
||||
path = (asset.get("sourceProvenance") or {}).get("path")
|
||||
if path in prior_ids or not isinstance(path, str):
|
||||
raise ValueError("identity baseline has duplicate source")
|
||||
prior_ids[path] = asset.get("templateId")
|
||||
for path, asset in by_path.items():
|
||||
if prior_ids.get(path) != asset["templateId"]:
|
||||
raise ValueError(f"template identity drift: {path}")
|
||||
seen = set()
|
||||
states = Counter()
|
||||
hashes = set()
|
||||
candidate_differences = []
|
||||
for record in records:
|
||||
relative = record.get("source")
|
||||
if not isinstance(relative, str) or relative in seen or relative not in by_path:
|
||||
raise ValueError(f"inventory source missing, duplicate or unmapped: {relative}")
|
||||
seen.add(relative)
|
||||
asset = by_path[relative]
|
||||
expected = record.get("sourceSha256")
|
||||
found = (asset.get("source") or {}).get("sha256")
|
||||
if not isinstance(expected, str) or not SHA256.fullmatch(expected) or found != expected:
|
||||
raise ValueError(f"catalog source hash mismatch: {relative}")
|
||||
path = source_path(dev_root, relative)
|
||||
if not path.is_file() or digest(path.read_bytes()) != expected:
|
||||
raise ValueError(f"checkout hash mismatch: {relative}")
|
||||
provenance = asset["sourceProvenance"]
|
||||
commit = provenance.get("commit")
|
||||
if provenance.get("repository") != "Agent-System-Dev" or not isinstance(commit, str) or not COMMIT.fullmatch(commit):
|
||||
raise ValueError(f"untrusted source provenance: {relative}")
|
||||
historical = subprocess.run(
|
||||
["git", "-C", str(dev_root), "show", f"{commit}:{relative}"],
|
||||
capture_output=True, check=False,
|
||||
)
|
||||
if historical.returncode or digest(historical.stdout) != expected:
|
||||
raise ValueError(f"committed source hash mismatch: {relative}")
|
||||
if (asset.get("source") or {}).get("format") != path.suffix.lower()[1:]:
|
||||
raise ValueError(f"catalog format mismatch: {relative}")
|
||||
applicability = asset.get("applicability") or {}
|
||||
catalog_types = applicability.get("docTypes", [])
|
||||
if not isinstance(catalog_types, list):
|
||||
raise ValueError(f"invalid catalog docTypes: {relative}")
|
||||
if record.get("proposedDocType") not in catalog_types:
|
||||
candidate_differences.append({
|
||||
"path": relative,
|
||||
"inventoryCandidate": record.get("proposedDocType"),
|
||||
"catalogCandidates": catalog_types,
|
||||
"confirmed": bool(applicability.get("confirmed")),
|
||||
})
|
||||
state = asset.get("state")
|
||||
if state not in STATES:
|
||||
raise ValueError(f"invalid asset state: {relative}")
|
||||
if state == "published":
|
||||
review = asset.get("review") or {}
|
||||
normalized = asset.get("normalizedDocx") or {}
|
||||
condition = applicability.get("condition")
|
||||
if (not normalized or not catalog_types or applicability.get("confirmed") is not True
|
||||
or not isinstance(condition, str) or not condition.strip()
|
||||
or review.get("decision") != "approved"
|
||||
or review.get("sourceSha256") != expected
|
||||
or not asset.get("structure") or not asset.get("render")):
|
||||
raise ValueError(f"published asset lacks approved binding: {relative}")
|
||||
if (normalized.get("format") != "docx" or asset["structure"].get("format") != "json"
|
||||
or asset["render"].get("format") != "docx"):
|
||||
raise ValueError(f"published asset has invalid derived formats: {relative}")
|
||||
reviewer = review.get("reviewer")
|
||||
evidence = review.get("evidence")
|
||||
reviewed_at = review.get("reviewedAt")
|
||||
if (not isinstance(reviewer, str) or not reviewer.strip()
|
||||
or not isinstance(evidence, str) or not evidence.strip()
|
||||
or not isinstance(reviewed_at, str)):
|
||||
raise ValueError(f"published asset lacks reviewer evidence: {relative}")
|
||||
if not RFC3339.fullmatch(reviewed_at):
|
||||
raise ValueError(f"published asset has invalid review time: {relative}")
|
||||
try:
|
||||
at = datetime.fromisoformat(reviewed_at.replace("Z", "+00:00"))
|
||||
except ValueError as exc:
|
||||
raise ValueError(f"published asset has invalid review time: {relative}") from exc
|
||||
if at.tzinfo is None:
|
||||
raise ValueError(f"published asset has invalid review time: {relative}")
|
||||
converter = asset.get("converter") or {}
|
||||
if (asset["source"].get("format") == "doc" or
|
||||
asset["source"].get("sha256") != normalized.get("sha256")):
|
||||
if (converter.get("inputSha256") != expected or
|
||||
converter.get("outputSha256") != normalized.get("sha256") or
|
||||
any(not isinstance(converter.get(field), str) or not converter[field].strip()
|
||||
for field in ("name", "version", "configuration"))):
|
||||
raise ValueError(f"published asset lacks conversion provenance: {relative}")
|
||||
if any(issue.get("status") != "resolved" for issue in asset.get("issues", [])):
|
||||
raise ValueError(f"published asset has unresolved issues: {relative}")
|
||||
if (review.get("structureSha256") != asset["structure"].get("sha256")
|
||||
or review.get("renderSha256") != asset["render"].get("sha256")
|
||||
or review.get("normalizedSha256") != normalized.get("sha256")):
|
||||
raise ValueError(f"published asset review hash mismatch: {relative}")
|
||||
if catalog_root is not None:
|
||||
package_digest(catalog_root, asset["source"])
|
||||
package_digest(catalog_root, normalized)
|
||||
package_digest(catalog_root, asset["structure"])
|
||||
package_digest(catalog_root, asset["render"])
|
||||
states[state] += 1
|
||||
hashes.add(expected)
|
||||
if require_published and states.get("published", 0) != expected_count:
|
||||
raise ValueError(f"{expected_count - states.get('published', 0)} assets not published")
|
||||
return {
|
||||
"source": "SELF mechanical provenance check",
|
||||
"matched": len(seen),
|
||||
"logicalTemplateIds": len(ids),
|
||||
"uniqueSourceBytes": len(hashes),
|
||||
"published": states.get("published", 0),
|
||||
"states": dict(sorted(states.items())),
|
||||
"candidateTypeDifferences": candidate_differences,
|
||||
"identityBaselineVerified": identity_baseline is not None,
|
||||
"publicationValidation": "NOT_RUN: RDM LoadDHFAssetCatalog and human content/Word review are authoritative",
|
||||
"limits": "This checks provenance and obvious metadata errors; state counts do not prove published bundles are valid.",
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--dev-root", type=Path, required=True)
|
||||
parser.add_argument("--inventory", type=Path, required=True)
|
||||
parser.add_argument("--catalog", type=Path, required=True)
|
||||
parser.add_argument("--expected-count", type=int, default=44)
|
||||
parser.add_argument("--identity-baseline", type=Path)
|
||||
parser.add_argument("--require-published", action="store_true")
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
if args.expected_count < 1:
|
||||
raise ValueError("expected-count must be positive")
|
||||
inventory_raw = args.inventory.read_bytes()
|
||||
catalog_raw = args.catalog.read_bytes()
|
||||
baseline_raw = args.identity_baseline.read_bytes() if args.identity_baseline else None
|
||||
result = audit(args.dev_root, json.loads(inventory_raw), json.loads(catalog_raw),
|
||||
args.expected_count, args.require_published, args.catalog.parent,
|
||||
json.loads(baseline_raw) if baseline_raw is not None else None)
|
||||
if args.catalog.read_bytes() != catalog_raw:
|
||||
raise ValueError("catalog changed during check")
|
||||
result["catalogSha256"] = digest(catalog_raw)
|
||||
result["inventorySha256"] = digest(inventory_raw)
|
||||
if baseline_raw is not None:
|
||||
result["identityBaselineSha256"] = digest(baseline_raw)
|
||||
if args.require_published:
|
||||
raise ValueError("RDM publication validator required for full bundle safety and human fidelity")
|
||||
except (OSError, ValueError, json.JSONDecodeError) as exc:
|
||||
print(f"dhf catalog guard: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
print(json.dumps(result, ensure_ascii=False, sort_keys=True))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -13,6 +13,8 @@
|
||||
不联网、不需要 token;非 pull_request 事件跳过(退出 0)。
|
||||
|
||||
期望目标:child → immediate_target;其余(initiative / standalone / 无卡)→ root_target(无卡即 main)。
|
||||
local 与 ci 均运行 check_pin_regression.sh(gitlink 相对 origin/main 不得倒退);非零即拒绝,
|
||||
PIN_REGRESSION_SKIP=1 由该脚本降级为告警(有意降级须 owner Review)。
|
||||
|
||||
退出码:0 通过;1 门禁拒绝(PR_CHECK_FAILED);2 用法;6 远端无法核验(REMOTE_UNVERIFIED,fail closed)。
|
||||
仅使用标准库,复用 task_triage 的 Gitea 客户端与 token 读取。token 不打印、不写入任何产物。
|
||||
@@ -33,6 +35,7 @@ EXIT_USAGE = 2
|
||||
PRE_MERGE_CLOSED = "已完成(合入前闭合)"
|
||||
CHILD_COLUMNS = ("子任务分支", "必需", "状态", "PR")
|
||||
DONE_STATES = ("已合入", "已延期")
|
||||
PIN_REGRESSION = Path(__file__).resolve().parent / "check_pin_regression.sh"
|
||||
|
||||
|
||||
def sh(*args):
|
||||
@@ -216,6 +219,17 @@ def final_problems(api, slug, token, branch, meta, text):
|
||||
return problems
|
||||
|
||||
|
||||
def pin_regression_problems():
|
||||
proc = subprocess.run(["bash", str(PIN_REGRESSION)], capture_output=True, text=True,
|
||||
encoding="utf-8", errors="replace")
|
||||
for line in (proc.stdout + proc.stderr).splitlines():
|
||||
print(f"[pin-regression] {line}")
|
||||
if proc.returncode != 0:
|
||||
return [f"pin regression gate failed (exit {proc.returncode}): a gitlink moves backward against "
|
||||
"origin/main or cannot be verified; PIN_REGRESSION_SKIP=1 only for owner-reviewed downgrades"]
|
||||
return []
|
||||
|
||||
|
||||
def verdict(problems, kind, base, head):
|
||||
if problems:
|
||||
for problem in problems:
|
||||
@@ -264,6 +278,7 @@ def cmd_local(args):
|
||||
problems.append(f"parent initiative {expected} has no open PR into {meta['root_target']}")
|
||||
if args.final and kind == "initiative":
|
||||
problems.extend(final_problems(api, slug, token, branch, meta, text))
|
||||
problems.extend(pin_regression_problems())
|
||||
return verdict(problems, kind, expected, head)
|
||||
|
||||
|
||||
@@ -281,7 +296,7 @@ def cmd_ci(_args):
|
||||
if base != expected:
|
||||
raise Stop(EXIT_FAILED, f"PR base {base!r} differs from expected {expected!r} "
|
||||
f"for {meta['kind']} branch {head}")
|
||||
return verdict([], meta["kind"], base, head)
|
||||
return verdict(pin_regression_problems(), meta["kind"], base, head)
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
"""check_pin_regression.sh:gitlink 相对 origin/main 只能前进(PR #682 事故形态)。
|
||||
|
||||
全部离线:临时目录内的普通 Git 仓库充当子仓,origin 只指向本地 bare 仓库。
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCRIPT = ROOT / "scripts" / "check_pin_regression.sh"
|
||||
PR_CHECK = ROOT / "scripts" / "task_pr_check.py"
|
||||
COMP = "deps/comp"
|
||||
|
||||
|
||||
class PinRegressionTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = Path(tempfile.mkdtemp())
|
||||
self.addCleanup(shutil.rmtree, self.tmp, ignore_errors=True)
|
||||
cfg = self.tmp / "gitconfig"
|
||||
cfg.write_text("")
|
||||
self.env = dict(
|
||||
os.environ,
|
||||
GIT_CONFIG_GLOBAL=str(cfg),
|
||||
GIT_CONFIG_NOSYSTEM="1",
|
||||
GIT_AUTHOR_NAME="t",
|
||||
GIT_AUTHOR_EMAIL="t@example.invalid",
|
||||
GIT_COMMITTER_NAME="t",
|
||||
GIT_COMMITTER_EMAIL="t@example.invalid",
|
||||
)
|
||||
for key in ("PIN_REGRESSION_SKIP", "PIN_REGRESSION_NO_FETCH"):
|
||||
self.env.pop(key, None)
|
||||
self.dev = self.tmp / "dev"
|
||||
self.dev.mkdir()
|
||||
self.git(self.dev, "init", "-q")
|
||||
# 子仓:A ← B,另有从 A 分叉的 C。
|
||||
self.comp = self.dev / COMP
|
||||
self.comp.mkdir(parents=True)
|
||||
self.git(self.comp, "init", "-q")
|
||||
self.a = self.commit(self.comp, "a")
|
||||
self.b = self.commit(self.comp, "b")
|
||||
self.git(self.comp, "checkout", "-q", "-b", "side", self.a)
|
||||
self.c = self.commit(self.comp, "c")
|
||||
|
||||
def git(self, cwd, *args):
|
||||
return subprocess.run(
|
||||
["git", *args], cwd=cwd, env=self.env, check=True, capture_output=True, text=True
|
||||
).stdout.strip()
|
||||
|
||||
def commit(self, repo, text):
|
||||
(repo / "f").write_text(text)
|
||||
self.git(repo, "add", "f")
|
||||
self.git(repo, "commit", "-qm", text)
|
||||
return self.git(repo, "rev-parse", "HEAD")
|
||||
|
||||
def pin(self, sha, path=COMP):
|
||||
self.git(self.dev, "update-index", "--add", "--cacheinfo", f"160000,{sha},{path}")
|
||||
self.git(self.dev, "commit", "-qm", f"pin {path} {sha[:7]}")
|
||||
return self.git(self.dev, "rev-parse", "HEAD")
|
||||
|
||||
def compose(self, main_pin, head_pin):
|
||||
"""origin/main 钉 main_pin,HEAD(PR 分支)钉 head_pin。"""
|
||||
main = self.pin(main_pin)
|
||||
self.git(self.dev, "update-ref", "refs/remotes/origin/main", main)
|
||||
if head_pin != main_pin:
|
||||
self.pin(head_pin)
|
||||
|
||||
def run_check(self, **extra):
|
||||
return subprocess.run(
|
||||
["bash", str(SCRIPT)], cwd=self.dev, env=self.env | extra, capture_output=True, text=True
|
||||
)
|
||||
|
||||
def test_same_pin_passes(self):
|
||||
self.compose(self.a, self.a)
|
||||
res = self.run_check()
|
||||
self.assertEqual(res.returncode, 0, res.stderr)
|
||||
self.assertIn(f"ok {COMP}\n", res.stdout)
|
||||
self.assertIn("pin regression ok", res.stdout)
|
||||
|
||||
def test_forward_pin_passes(self):
|
||||
self.compose(self.a, self.b)
|
||||
res = self.run_check()
|
||||
self.assertEqual(res.returncode, 0, res.stderr)
|
||||
self.assertIn(f"ok {COMP} (forward", res.stdout)
|
||||
|
||||
def test_pr682_stale_branch_carrying_old_pin_fails(self):
|
||||
self.compose(self.b, self.a)
|
||||
res = self.run_check()
|
||||
self.assertEqual(res.returncode, 1, res.stdout)
|
||||
self.assertIn(f"pin: {COMP} moves BACKWARD", res.stderr)
|
||||
self.assertIn("PR #682", res.stderr)
|
||||
self.assertIn("PIN_REGRESSION_SKIP=1", res.stderr)
|
||||
self.assertNotIn("pin regression ok", res.stdout)
|
||||
|
||||
def test_diverged_pin_fails(self):
|
||||
self.compose(self.b, self.c)
|
||||
res = self.run_check()
|
||||
self.assertEqual(res.returncode, 1, res.stdout)
|
||||
self.assertIn(f"pin: {COMP} moves BACKWARD", res.stderr)
|
||||
|
||||
def test_skip_env_downgrades_to_warning(self):
|
||||
self.compose(self.b, self.a)
|
||||
res = self.run_check(PIN_REGRESSION_SKIP="1")
|
||||
self.assertEqual(res.returncode, 0, res.stderr)
|
||||
self.assertIn("warn: PIN_REGRESSION_SKIP=1", res.stderr)
|
||||
self.assertIn("requires owner review", res.stderr)
|
||||
|
||||
def test_missing_origin_main_warns_and_passes(self):
|
||||
self.pin(self.a)
|
||||
res = self.run_check()
|
||||
self.assertEqual(res.returncode, 0, res.stderr)
|
||||
self.assertIn("warn: origin/main is not resolvable locally", res.stderr)
|
||||
|
||||
def test_new_component_not_on_main_passes(self):
|
||||
self.compose(self.a, self.a)
|
||||
other = self.dev / "deps" / "other"
|
||||
other.mkdir()
|
||||
self.git(other, "init", "-q")
|
||||
self.pin(self.commit(other, "o"), "deps/other")
|
||||
res = self.run_check()
|
||||
self.assertEqual(res.returncode, 0, res.stderr)
|
||||
self.assertIn("ok deps/other (new, not on origin/main)", res.stdout)
|
||||
|
||||
def test_missing_old_object_with_fetch_disabled_fails(self):
|
||||
self.compose("1" * 40, self.a)
|
||||
res = self.run_check(PIN_REGRESSION_NO_FETCH="1")
|
||||
self.assertEqual(res.returncode, 1, res.stdout)
|
||||
self.assertIn(f"pin: {COMP} origin/main pin {'1' * 40} is missing locally", res.stderr)
|
||||
|
||||
def test_missing_old_object_unfetchable_from_local_origin_fails(self):
|
||||
bare = self.tmp / "comp-origin.git"
|
||||
self.git(self.tmp, "init", "-q", "--bare", str(bare))
|
||||
self.git(self.comp, "remote", "add", "origin", str(bare))
|
||||
self.compose("2" * 40, self.a)
|
||||
res = self.run_check()
|
||||
self.assertEqual(res.returncode, 1, res.stdout)
|
||||
self.assertIn("could not be fetched", res.stderr)
|
||||
|
||||
def test_missing_old_object_is_fetched_from_local_origin(self):
|
||||
upstream = self.tmp / "upstream"
|
||||
self.git(self.tmp, "clone", "-q", str(self.comp), str(upstream))
|
||||
newer = self.commit(upstream, "z")
|
||||
bare = self.tmp / "comp-origin.git"
|
||||
self.git(self.tmp, "clone", "-q", "--bare", str(upstream), str(bare))
|
||||
self.git(self.comp, "remote", "add", "origin", str(bare))
|
||||
self.compose(newer, self.b)
|
||||
res = self.run_check()
|
||||
self.assertEqual(res.returncode, 1, res.stdout)
|
||||
self.assertNotIn("could not be fetched", res.stderr)
|
||||
self.assertIn(f"pin: {COMP} moves BACKWARD", res.stderr)
|
||||
self.git(self.comp, "cat-file", "-e", f"{newer}^{{commit}}")
|
||||
|
||||
def test_uninitialized_component_cannot_be_verified(self):
|
||||
self.compose(self.b, self.a)
|
||||
shutil.rmtree(self.comp)
|
||||
self.comp.mkdir()
|
||||
res = self.run_check()
|
||||
self.assertEqual(res.returncode, 1, res.stdout)
|
||||
self.assertIn(f"pin: {COMP} is not checked out", res.stderr)
|
||||
|
||||
def run_pr_check_ci(self, **extra):
|
||||
env = self.env | {"GITHUB_EVENT_NAME": "pull_request", "GITHUB_BASE_REF": "main",
|
||||
"GITHUB_HEAD_REF": "feature/stale", "TASK_TARGET_BRANCH": "main"} | extra
|
||||
return subprocess.run([sys.executable, str(PR_CHECK), "ci"], cwd=self.dev, env=env,
|
||||
capture_output=True, text=True, timeout=60)
|
||||
|
||||
def test_task_pr_check_ci_hard_fails_on_regression(self):
|
||||
self.compose(self.b, self.a)
|
||||
res = self.run_pr_check_ci()
|
||||
self.assertEqual(res.returncode, 1, res.stdout + res.stderr)
|
||||
self.assertIn(f"[pin-regression] pin: {COMP} moves BACKWARD", res.stdout)
|
||||
self.assertIn("[pr-check] FAIL pin regression gate failed", res.stdout)
|
||||
self.assertIn("[verdict] PR_CHECK_FAILED", res.stdout)
|
||||
|
||||
def test_task_pr_check_ci_skip_env_passes(self):
|
||||
self.compose(self.b, self.a)
|
||||
res = self.run_pr_check_ci(PIN_REGRESSION_SKIP="1")
|
||||
self.assertEqual(res.returncode, 0, res.stdout + res.stderr)
|
||||
self.assertIn("[pin-regression] warn: PIN_REGRESSION_SKIP=1", res.stdout)
|
||||
self.assertIn("[verdict] PR_CHECK_OK", res.stdout)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -8,6 +8,7 @@ from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCRIPT = ROOT / "scripts" / "check-pins.sh"
|
||||
REGRESSION = ROOT / "scripts" / "check_pin_regression.sh"
|
||||
CONTAINERS = ("modules/backend", "deps/agent-native", "deps/agent-squad", "deps/agent-intent")
|
||||
|
||||
|
||||
@@ -29,6 +30,7 @@ class CheckPinsTests(unittest.TestCase):
|
||||
self.dev = self.tmp / "dev"
|
||||
(self.dev / "scripts").mkdir(parents=True)
|
||||
shutil.copy(SCRIPT, self.dev / "scripts" / "check-pins.sh")
|
||||
shutil.copy(REGRESSION, self.dev / "scripts" / "check_pin_regression.sh")
|
||||
self.git(self.dev, "init", "-q")
|
||||
|
||||
def git(self, cwd, *args):
|
||||
@@ -112,6 +114,21 @@ class CheckPinsTests(unittest.TestCase):
|
||||
self.assertEqual(res.returncode, 1)
|
||||
self.assertIn("has no Dev pin", res.stderr)
|
||||
|
||||
def test_pin_regression_against_origin_main_fails(self):
|
||||
old, new = self.repo("deps/error-code-system", "e1", "e2")
|
||||
self.build({"error-code-system": old}, {"deps/error-code-system": old})
|
||||
self.git(self.dev, "commit", "-qm", "pr")
|
||||
stale = self.git(self.dev, "rev-parse", "HEAD")
|
||||
self.link(self.dev, "deps/error-code-system", new)
|
||||
self.git(self.dev, "commit", "-qm", "main")
|
||||
self.git(self.dev, "update-ref", "refs/remotes/origin/main", "HEAD")
|
||||
self.git(self.dev, "reset", "-q", stale)
|
||||
res = self.run_pins()
|
||||
self.assertEqual(res.returncode, 1, res.stderr)
|
||||
self.assertIn("ok modules/backend error-code-system", res.stdout)
|
||||
self.assertIn("pin: deps/error-code-system moves BACKWARD", res.stderr)
|
||||
self.assertNotIn("pins ok", res.stdout)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from copy import deepcopy
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
GUARD = ROOT / "scripts" / "dhf_catalog_guard.py"
|
||||
|
||||
|
||||
class DHFCatalogGuardTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.tmp.cleanup)
|
||||
self.root = Path(self.tmp.name)
|
||||
self.dev = self.root / "dev"
|
||||
self.dev.mkdir()
|
||||
subprocess.run(["git", "init", "-q", str(self.dev)], check=True)
|
||||
self.items = []
|
||||
self.assets = []
|
||||
for index, value in enumerate((b"source-one", b"source-two"), 1):
|
||||
path = f".templates/DHF 文件模板/阶段{index}/模板{index}.docx"
|
||||
absolute = self.dev / path
|
||||
absolute.parent.mkdir(parents=True)
|
||||
absolute.write_bytes(value)
|
||||
digest = hashlib.sha256(value).hexdigest()
|
||||
self.items.append({
|
||||
"source": path,
|
||||
"sourceSha256": digest,
|
||||
"proposedDocType": f"type-{index}",
|
||||
})
|
||||
self.assets.append({
|
||||
"templateId": f"dhf.logical-{index}",
|
||||
"version": "1.0.0",
|
||||
"state": "needs-review",
|
||||
"source": {"sha256": digest, "format": "docx"},
|
||||
"sourceProvenance": {"repository": "Agent-System-Dev", "path": path},
|
||||
"applicability": {"docTypes": [f"type-{index}"], "confirmed": False},
|
||||
})
|
||||
subprocess.run(["git", "-C", str(self.dev), "add", ".templates"], check=True)
|
||||
subprocess.run([
|
||||
"git", "-C", str(self.dev), "-c", "user.name=Test",
|
||||
"-c", "user.email=test@example.invalid", "commit", "-qm", "sources",
|
||||
], check=True)
|
||||
commit = subprocess.check_output(
|
||||
["git", "-C", str(self.dev), "rev-parse", "HEAD"], text=True,
|
||||
).strip()
|
||||
for asset in self.assets:
|
||||
asset["sourceProvenance"]["commit"] = commit
|
||||
self.inventory = self.root / "inventory.json"
|
||||
self.catalog = self.root / "catalog.json"
|
||||
self.save()
|
||||
|
||||
def save(self):
|
||||
self.inventory.write_text(json.dumps({"templates": self.items}))
|
||||
self.catalog.write_text(json.dumps({"schemaVersion": "dhf-assets/v1", "assets": self.assets}))
|
||||
|
||||
def run_guard(self, *extra):
|
||||
return subprocess.run([
|
||||
sys.executable, str(GUARD), "--dev-root", str(self.dev),
|
||||
"--inventory", str(self.inventory), "--catalog", str(self.catalog),
|
||||
"--expected-count", "2", *extra,
|
||||
], capture_output=True, text=True)
|
||||
|
||||
def publish_fixture(self, index=0, *, structure=b'{"blocks":[]}', render=b"rendered-docx"):
|
||||
asset = self.assets[index]
|
||||
number = index + 1
|
||||
source = (self.dev / self.items[index]["source"]).read_bytes()
|
||||
names = {"source": f"source{number}.docx", "structure": f"structure{number}.json",
|
||||
"render": f"render{number}.docx"}
|
||||
for label, raw in [("source", source), ("structure", structure), ("render", render)]:
|
||||
(self.root / names[label]).write_bytes(raw)
|
||||
asset["state"] = "published"
|
||||
asset["applicability"].update({"confirmed": True, "condition": "test scope"})
|
||||
asset["source"]["path"] = names["source"]
|
||||
asset["normalizedDocx"] = {"path": names["source"], "format": "docx",
|
||||
"sha256": hashlib.sha256(source).hexdigest()}
|
||||
asset["structure"] = {"path": names["structure"], "format": "json",
|
||||
"sha256": hashlib.sha256(structure).hexdigest()}
|
||||
asset["render"] = {"path": names["render"], "format": "docx",
|
||||
"sha256": hashlib.sha256(render).hexdigest()}
|
||||
asset["review"] = {
|
||||
"decision": "approved", "reviewer": "reviewer", "reviewedAt": "2026-10-14T00:00:00Z",
|
||||
"evidence": "review-record", "sourceSha256": self.items[index]["sourceSha256"],
|
||||
"normalizedSha256": asset["normalizedDocx"]["sha256"],
|
||||
"structureSha256": asset["structure"]["sha256"],
|
||||
"renderSha256": asset["render"]["sha256"],
|
||||
}
|
||||
return asset
|
||||
|
||||
def test_accepts_matching_immutable_sources_but_does_not_call_them_published(self):
|
||||
result = self.run_guard()
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
report = json.loads(result.stdout)
|
||||
self.assertEqual(report["matched"], 2)
|
||||
self.assertEqual(report["states"], {"needs-review": 2})
|
||||
self.assertEqual(report["published"], 0)
|
||||
|
||||
def test_rejects_changed_checkout_even_when_catalog_matches_historical_commit(self):
|
||||
(self.dev / self.items[0]["source"]).write_bytes(b"changed")
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("checkout hash mismatch", result.stderr)
|
||||
|
||||
def test_rejects_original_in_symlinked_parent_directory(self):
|
||||
source = self.dev / self.items[0]["source"]
|
||||
original_parent = source.parent
|
||||
moved_parent = original_parent.parent / "alias-target"
|
||||
original_parent.rename(moved_parent)
|
||||
original_parent.symlink_to(moved_parent, target_is_directory=True)
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("source path symlink", result.stderr)
|
||||
|
||||
def test_rejects_false_publication(self):
|
||||
self.assets[0]["state"] = "published"
|
||||
self.save()
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("published asset lacks", result.stderr)
|
||||
|
||||
def test_reports_revised_candidate_type_without_approving_publication(self):
|
||||
self.assets[0]["applicability"]["docTypes"] = ["unrelated"]
|
||||
self.save()
|
||||
result = self.run_guard()
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
report = json.loads(result.stdout)
|
||||
self.assertEqual(report["published"], 0)
|
||||
self.assertEqual(report["candidateTypeDifferences"], [{
|
||||
"path": self.items[0]["source"],
|
||||
"inventoryCandidate": "type-1",
|
||||
"catalogCandidates": ["unrelated"],
|
||||
"confirmed": False,
|
||||
}])
|
||||
|
||||
def test_publication_gate_does_not_accept_a_complete_inventory_as_delivery(self):
|
||||
result = self.run_guard("--require-published")
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("not published", result.stderr)
|
||||
|
||||
def test_rejects_published_asset_whose_review_does_not_bind_bundle_bytes(self):
|
||||
asset = self.publish_fixture()
|
||||
asset["review"]["structureSha256"] = "0" * 64
|
||||
self.save()
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("published asset review hash mismatch", result.stderr)
|
||||
|
||||
def test_rejects_bundle_file_changed_after_approval(self):
|
||||
asset = self.publish_fixture()
|
||||
self.save()
|
||||
(self.root / asset["render"]["path"]).write_bytes(b"modified-after-review")
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("published asset file hash mismatch", result.stderr)
|
||||
|
||||
def test_rejects_published_asset_with_unresolved_fidelity_issue(self):
|
||||
asset = self.publish_fixture()
|
||||
asset["issues"] = [{"id": "fidelity", "status": "open"}]
|
||||
self.save()
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("published asset has unresolved issues", result.stderr)
|
||||
|
||||
def test_publication_flag_never_claims_authoritative_rdm_validation(self):
|
||||
for index in range(2):
|
||||
self.publish_fixture(index, structure=b"not-a-valid-structure", render=b"not-a-valid-docx")
|
||||
self.save()
|
||||
result = self.run_guard("--require-published")
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("RDM publication validator required", result.stderr)
|
||||
|
||||
def test_rejects_swapped_logical_ids_against_fixed_identity_baseline(self):
|
||||
baseline = self.root / "identity-baseline.json"
|
||||
baseline.write_text(json.dumps({"assets": deepcopy(self.assets)}))
|
||||
self.assets[0]["templateId"], self.assets[1]["templateId"] = (
|
||||
self.assets[1]["templateId"], self.assets[0]["templateId"])
|
||||
self.save()
|
||||
result = self.run_guard("--identity-baseline", str(baseline))
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("template identity drift", result.stderr)
|
||||
|
||||
def test_report_binds_exact_catalog_bytes(self):
|
||||
result = self.run_guard()
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(json.loads(result.stdout)["catalogSha256"],
|
||||
hashlib.sha256(self.catalog.read_bytes()).hexdigest())
|
||||
|
||||
def test_rejects_published_package_through_symlink_directory(self):
|
||||
asset = self.publish_fixture()
|
||||
real = self.root / "real"
|
||||
real.mkdir()
|
||||
for key in ["source", "normalizedDocx", "structure", "render"]:
|
||||
p = self.root / asset[key]["path"]
|
||||
(real / p.name).write_bytes(p.read_bytes())
|
||||
asset[key]["path"] = "alias/" + p.name
|
||||
(self.root / "alias").symlink_to(real, target_is_directory=True)
|
||||
self.save()
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("symlink", result.stderr)
|
||||
|
||||
def test_rejects_string_false_and_null_applicability(self):
|
||||
asset = self.publish_fixture()
|
||||
asset["applicability"]["confirmed"] = "false"
|
||||
self.save()
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("published asset lacks approved binding", result.stderr)
|
||||
|
||||
asset["applicability"]["confirmed"] = True
|
||||
asset["applicability"]["condition"] = None
|
||||
self.save()
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("published asset lacks approved binding", result.stderr)
|
||||
|
||||
def test_rejects_null_reviewer_and_non_rfc3339_timestamp(self):
|
||||
asset = self.publish_fixture()
|
||||
asset["review"]["reviewer"] = None
|
||||
self.save()
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("published asset lacks reviewer evidence", result.stderr)
|
||||
|
||||
asset["review"]["reviewer"] = "reviewer"
|
||||
asset["review"]["reviewedAt"] = "2026-10-14 00:00:00Z"
|
||||
self.save()
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("published asset has invalid review time", result.stderr)
|
||||
|
||||
def test_rejects_changed_normalized_file_without_named_converter(self):
|
||||
asset = self.publish_fixture()
|
||||
normalized = b"changed-normalized-docx"
|
||||
(self.root / "normalized.docx").write_bytes(normalized)
|
||||
asset["normalizedDocx"] = {
|
||||
"path": "normalized.docx", "format": "docx", "sha256": hashlib.sha256(normalized).hexdigest(),
|
||||
}
|
||||
asset["review"]["normalizedSha256"] = asset["normalizedDocx"]["sha256"]
|
||||
asset["converter"] = {
|
||||
"inputSha256": self.items[0]["sourceSha256"],
|
||||
"outputSha256": asset["normalizedDocx"]["sha256"],
|
||||
}
|
||||
self.save()
|
||||
result = self.run_guard()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("published asset lacks conversion provenance", result.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+47
-31
@@ -296,12 +296,14 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 1. 存量 exec= DSN:连续启动两次均恢复,进程参数无秘密 ----
|
||||
|
||||
def test_legacy_exec_dsn_survives_consecutive_restarts(self):
|
||||
self.register_pg("pg1", 18890, password="legacy-pw-1")
|
||||
dsn = "postgresql://agent:legacy-pw-1@127.0.0.1:18890/agent"
|
||||
pg_port = free_test_port()
|
||||
self.register_pg("pg1", pg_port, password="legacy-pw-1")
|
||||
dsn = "postgresql://agent:legacy-pw-1@127.0.0.1:%d/agent" % pg_port
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", 18791,
|
||||
"--addr 127.0.0.1:18791 --data %s --plugins %s --auth auto --dsn %s"
|
||||
% (d / "data", d / "plugins", dsn))
|
||||
port = free_test_port()
|
||||
self.seed_agent("dev", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto --dsn %s"
|
||||
% (port, d / "data", d / "plugins", dsn))
|
||||
for _ in range(2):
|
||||
self.clear_serve_record()
|
||||
self.svc("dev", "restart")
|
||||
@@ -322,12 +324,14 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 2. 仅 dbref:裸 restart 与重新部署均保留绑定 ----
|
||||
|
||||
def test_dbref_binding_survives_bare_restart_and_redeploy(self):
|
||||
self.register_pg("pg1", 18891, password="bind-pw-2")
|
||||
dsn = "postgresql://agent:bind-pw-2@127.0.0.1:18891/agent"
|
||||
pg_port = free_test_port()
|
||||
self.register_pg("pg1", pg_port, password="bind-pw-2")
|
||||
dsn = "postgresql://agent:bind-pw-2@127.0.0.1:%d/agent" % pg_port
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", 18792,
|
||||
"--addr 127.0.0.1:18792 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
port = free_test_port()
|
||||
self.seed_agent("dev", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg")])
|
||||
self.clear_serve_record()
|
||||
self.svc("dev", "restart")
|
||||
@@ -349,18 +353,21 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.register_pg("pg1", 18892, password="addr-pw-3")
|
||||
dsn = "postgresql://agent:addr-pw-3@127.0.0.1:18892/agent"
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", 18793,
|
||||
"--addr 127.0.0.1:18793 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
with socket.socket() as reservation:
|
||||
reservation.bind(("127.0.0.1", 0))
|
||||
port = reservation.getsockname()[1]
|
||||
self.seed_agent("dev", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg")])
|
||||
self.clear_serve_record()
|
||||
self.svc("dev", "restart", "--addr", "127.0.0.1:18794")
|
||||
self.svc("dev", "restart", "--addr", "127.0.0.1:%d" % port)
|
||||
record = self.serve_record()
|
||||
self.assertIn("AGENT_SYSTEM_DSN=%s" % dsn, record)
|
||||
self.assertIn("--auth auto", record)
|
||||
self.assertEqual(self.kv("dev", "dbref"), "pg1/agent")
|
||||
self.assertEqual(self.kv("dev", "addr"), "127.0.0.1:18794")
|
||||
self.assertIn("--addr 127.0.0.1:18794", self.kv("dev", "exec"))
|
||||
self.assertEqual(self.kv("dev", "addr"), "127.0.0.1:%d" % port)
|
||||
self.assertIn("--addr 127.0.0.1:%d" % port, self.kv("dev", "exec"))
|
||||
self.assertEqual(self.kv("dev", "db"), "pg")
|
||||
|
||||
# ---- 4. 外部直连与配置文件来源:后续无参数启动仍恢复 ----
|
||||
@@ -410,9 +417,10 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
|
||||
def test_refusals_keep_registration_without_sqlite_fallback(self):
|
||||
d = self.runtime("a1")
|
||||
self.seed_agent("a1", 18796,
|
||||
"--addr 127.0.0.1:18796 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
port = free_test_port()
|
||||
self.seed_agent("a1", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pgmissing/agent"), ("db", "pg")])
|
||||
self.clear_serve_record()
|
||||
result = self.svc("a1", "restart", check=False)
|
||||
@@ -422,33 +430,41 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertEqual(self.kv("a1", "dbref"), "pgmissing/agent") # 可恢复配置保留
|
||||
|
||||
# 实例登记不全(缺 password)→ 拒启。
|
||||
self.register_pg("pginc", 18895, complete=False)
|
||||
self.register_pg("pginc", free_test_port(), complete=False)
|
||||
d = self.runtime("a2")
|
||||
self.seed_agent("a2", 18797,
|
||||
"--addr 127.0.0.1:18797 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
port = free_test_port()
|
||||
self.seed_agent("a2", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pginc/agent"), ("db", "pg")])
|
||||
result = self.svc("a2", "restart", check=False)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("拒绝", result.stdout)
|
||||
|
||||
# dbref 与 dsn= 指向不同库 → 配置冲突拒启。
|
||||
self.register_pg("pg1", 18896, password="clash-pw")
|
||||
pg_port = free_test_port()
|
||||
self.register_pg("pg1", pg_port, password="clash-pw")
|
||||
other_pg_port = free_test_port()
|
||||
while other_pg_port == pg_port:
|
||||
other_pg_port = free_test_port()
|
||||
d = self.runtime("a3")
|
||||
self.seed_agent("a3", 18798,
|
||||
"--addr 127.0.0.1:18798 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
port = free_test_port()
|
||||
self.seed_agent("a3", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg"),
|
||||
("dsn", "postgresql://u:p@127.0.0.1:18999/otherdb")])
|
||||
("dsn", "postgresql://u:p@127.0.0.1:%d/otherdb"
|
||||
% other_pg_port)])
|
||||
result = self.svc("a3", "restart", check=False)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("拒绝", result.stdout)
|
||||
|
||||
# db=pg 但无可恢复 DSN → 拒启。
|
||||
d = self.runtime("a4")
|
||||
self.seed_agent("a4", 18799,
|
||||
"--addr 127.0.0.1:18799 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
port = free_test_port()
|
||||
self.seed_agent("a4", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"),
|
||||
extra_rows=[("db", "pg")])
|
||||
result = self.svc("a4", "restart", check=False)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
|
||||
@@ -10,11 +10,15 @@ import os
|
||||
import shutil
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from _port_alloc import free_test_port # noqa: E402
|
||||
|
||||
SOURCE = Path(__file__).resolve().parents[1]
|
||||
SVC_SCRIPT = SOURCE / "scripts" / "svc.sh"
|
||||
|
||||
@@ -67,7 +71,9 @@ class SvcAgentBackupTests(unittest.TestCase):
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout)
|
||||
return proc
|
||||
|
||||
def register_agent(self, name="t1", port=8899):
|
||||
def register_agent(self, name="t1", port=None):
|
||||
if port is None:
|
||||
port = free_test_port()
|
||||
d = self.root / "runtime" / name
|
||||
(d / "data").mkdir(parents=True, exist_ok=True)
|
||||
(d / "svc.env").write_text(
|
||||
|
||||
+18
-28
@@ -184,12 +184,17 @@ esac
|
||||
"""
|
||||
|
||||
|
||||
def free_fixture_port():
|
||||
with socket.socket() as reservation:
|
||||
reservation.bind(("127.0.0.1", 0))
|
||||
return reservation.getsockname()[1]
|
||||
|
||||
|
||||
class SvcAutoRecoverTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
tmp = tempfile.mkdtemp(prefix="svc-autorecover-")
|
||||
self.addCleanup(shutil.rmtree, tmp, True)
|
||||
self.root = Path(tmp)
|
||||
self._allocated_ports = set()
|
||||
(self.root / "scripts").mkdir(parents=True)
|
||||
shutil.copy2(SVC_SCRIPT, self.root / "scripts" / "svc.sh")
|
||||
self.trace = self.root / "trace"
|
||||
@@ -249,21 +254,6 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout)
|
||||
return proc
|
||||
|
||||
def available_port(self):
|
||||
"""Allocate a distinct service port for this test, including address changes.
|
||||
|
||||
The socket closes before svc.sh binds it, leaving a small allocation-to-start
|
||||
race with unrelated processes. Keep allocated numbers distinct within the
|
||||
test so stopped/restarted services never share another instance's port.
|
||||
"""
|
||||
while True:
|
||||
with socket.socket() as sock:
|
||||
sock.bind(("0.0.0.0", 0))
|
||||
port = sock.getsockname()[1]
|
||||
if port not in self._allocated_ports:
|
||||
self._allocated_ports.add(port)
|
||||
return port
|
||||
|
||||
def runtime(self, name):
|
||||
return self.root / "runtime" / name
|
||||
|
||||
@@ -346,14 +336,14 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
# ---- 1. 期望态:start 成功置位 / 显式 stop 清除(含进程已死的 stop)----
|
||||
|
||||
def test_start_records_autostart_and_stop_clears(self):
|
||||
self.seed_agent("dev", self.available_port())
|
||||
self.seed_agent("dev", free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
self.assertEqual(self.kv("dev", "autostart"), "1")
|
||||
self.svc("dev", "stop")
|
||||
self.assertIsNone(self.kv("dev", "autostart"))
|
||||
|
||||
def test_stop_on_dead_process_clears_autostart(self):
|
||||
self.seed_agent("dev", self.available_port())
|
||||
self.seed_agent("dev", free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
self.kill9("dev")
|
||||
# 进程已死但 autostart 保留(等待 recover);显式 stop 表示"保持停止"。
|
||||
@@ -364,7 +354,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
# ---- 2. recover:崩溃拉回 / 运行中不动 / 显式停止不复活 / 未启动不碰 ----
|
||||
|
||||
def test_recover_restarts_sigkilled_service(self):
|
||||
port = self.available_port()
|
||||
port = free_fixture_port()
|
||||
self.seed_agent("dev", port)
|
||||
self.start_ok("dev")
|
||||
old_pid = self.kill9("dev")
|
||||
@@ -381,7 +371,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
self.assertIn("--addr 127.0.0.1:%d" % port, self.kv("dev", "exec"))
|
||||
|
||||
def test_recover_noop_while_running(self):
|
||||
self.seed_agent("dev", self.available_port())
|
||||
self.seed_agent("dev", free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
pid = self.pid_of("dev")
|
||||
result = self.svc("recover")
|
||||
@@ -390,7 +380,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
self.assertEqual(self.history_events("dev", "recover"), [])
|
||||
|
||||
def test_recover_respects_explicit_stop(self):
|
||||
self.seed_agent("dev", self.available_port())
|
||||
self.seed_agent("dev", free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
self.svc("dev", "stop")
|
||||
result = self.svc("recover")
|
||||
@@ -399,7 +389,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
self.assertEqual(self.history_events("dev", "recover"), [])
|
||||
|
||||
def test_recover_ignores_never_started_service(self):
|
||||
self.seed_agent("dev", self.available_port()) # 从未 start:无 autostart 键
|
||||
self.seed_agent("dev", free_fixture_port()) # 从未 start:无 autostart 键
|
||||
result = self.svc("recover")
|
||||
self.assertEqual(result.returncode, 0, result.stdout)
|
||||
self.assertFalse(self.pid_alive(self.pid_of("dev")))
|
||||
@@ -408,7 +398,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
# ---- 3. 并发防护:生命周期锁被持有时 recover 跳过 ----
|
||||
|
||||
def test_recover_skips_when_lock_held(self):
|
||||
self.seed_agent("dev", self.available_port())
|
||||
self.seed_agent("dev", free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
self.kill9("dev")
|
||||
lock = self.runtime("dev") / ".start.lock"
|
||||
@@ -428,7 +418,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
# ---- 4. 恢复失败路径:端口被外部进程占用 → fail 事件 + 非零退出 ----
|
||||
|
||||
def test_recover_fails_when_port_stolen(self):
|
||||
port = self.available_port()
|
||||
port = free_fixture_port()
|
||||
self.seed_agent("dev", port)
|
||||
self.start_ok("dev")
|
||||
self.kill9("dev")
|
||||
@@ -453,7 +443,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
if bound:
|
||||
break
|
||||
time.sleep(0.05)
|
||||
self.assertTrue(bound, f"thief 未能在 5s 内监听 {port}")
|
||||
self.assertTrue(bound, "thief 未能在 5s 内监听 18808")
|
||||
try:
|
||||
result = self.svc("recover", check=False)
|
||||
self.assertEqual(result.returncode, 1, result.stdout)
|
||||
@@ -497,7 +487,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
"""模拟 update 误清 pidfile 后本 bin 仍占口:裸 start 应回收并拉起新进程。"""
|
||||
if not self._ss_can_attribute_pid():
|
||||
self.skipTest("ss -ltnp 无法归因 PID(reclaim_own_port 需此能力)")
|
||||
port = self.available_port()
|
||||
port = free_fixture_port()
|
||||
self.seed_agent("dev", port)
|
||||
self.start_ok("dev")
|
||||
old = self.pid_of("dev")
|
||||
@@ -518,7 +508,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
|
||||
def test_start_rejects_foreign_port_occupant(self):
|
||||
"""外部 python http.server 占口:start 必须失败并提示非本服务。"""
|
||||
port = self.available_port()
|
||||
port = free_fixture_port()
|
||||
self.seed_agent("dev", port)
|
||||
thief = subprocess.Popen(
|
||||
["python3", "-m", "http.server", str(port), "--bind", "127.0.0.1"],
|
||||
@@ -555,7 +545,7 @@ class SvcAutoRecoverTests(unittest.TestCase):
|
||||
self.assertFalse((unit_dir / "agent-system-svc-recover.service").exists())
|
||||
|
||||
def test_recover_reserved_name_rejected(self):
|
||||
self.seed_agent("recover", self.available_port()) # 保留字不可作服务名
|
||||
self.seed_agent("recover", free_fixture_port()) # 保留字不可作服务名
|
||||
result = self.svc("recover", "recover", check=False)
|
||||
self.assertEqual(result.returncode, 2, result.stdout)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user