Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b360bcb974 | ||
|
|
efa0b2af7e | ||
|
|
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,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 任务卡
|
||||
|
||||
任务主记录: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。
|
||||
@@ -0,0 +1,177 @@
|
||||
# feature/container-storage-plan 任务卡
|
||||
|
||||
任务主记录:<PR 链接,交付前填写>;状态:进行中;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-container-storage-plan(worktree);分支:feature/container-storage-plan
|
||||
- 分支类型:initiative;initiative_id:feature-container-storage-plan;root_target:main;immediate_target:main;accepting_requirements:true
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=53a3aac8b5638347861577e2ab6f487c182b364e44b1fb0b563df0fdb5834870;triage_receipt=/tmp/container-storage-plan-triage-20261022-b.json;receipt_sha256=31e6accb098e8572e3698e04e3dbafc340fd9ef300df7a09110f1cb249aabb44
|
||||
|
||||
- 授权与交付终点:按已审阅方案实施 U1—U6(M1—M3)并交付到 main;含各仓代码/测试/文档变更、PR/CI/合入。不含:生产迁移 O1、生产数据读取/写入、部署到生产、数据库大版本升级。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:实施与自检(M1/U1 起);当前增量:I1=U1
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
### 章程(charter)
|
||||
|
||||
**用途与目标**:按已审阅方案(`docs/superpowers/plans/2026-10-22-container-storage-plan.md`)把部署形态改造为:一个完整应用服务二进制、两个 PostgreSQL 容器(业务基座库 + RDM 领域库)、知识库 PostgreSQL/pgvector 容器(第三个逻辑库)、外置持久文件根;生产/测试/验证三套环境独立配置、数据、身份与凭据;形成可验证的完整备份恢复与环境克隆隔离流程(需求 R1—R8、决策 KTD1—KTD7 见方案)。
|
||||
|
||||
**接纳范围**:方案 Implementation Units U1—U6 及其测试;各仓仅做阻塞目标的最小接线。U1 环境与数据归属合同;U2 容器数据库与 svc 接线;U3 迁移与文件引用兼容;U4 组合备份与恢复新目标;U5 测试/验证克隆与环境选择;U6 单二进制与组合验收、运维交付。
|
||||
|
||||
**非目标**:生产迁移执行(方案 O1,另获窗口授权后单独安排);高可用平台、Kubernetes、托管云数据库、全量对象存储、应用微服务化;零停机/实时复制/PITR;删除既有 SQLite/原生 PG 路径;PG 大版本升级。
|
||||
|
||||
**分级测试策略**:L2→L1 用 `make test-affected` 最小直接检查;L1→main 最终窗口跑完整组合(`make test`,含 `test-pg`、`svc-realcombo`、`test-regulations-kb`);容器与文件恢复必须有真实临时环境验证,缺 DSN 的跳过不计通过。
|
||||
|
||||
**验收条件**:方案 Definition of Done——单二进制在目标环境启动并符合两容器三库边界;三环境独立可启停重建且隔离实测;完整备份恢复到新目录且原文/引用不变;真实迁移 fixture 对账通过;精确组件组合、测试证据、独立 Review、必需 CI 与规范文档齐备。
|
||||
|
||||
**追加领域规则**:manifest 是身份与期望态唯一权威,Compose 只作版本化模板(KTD3);普通启停/重建不隐式删卷、不隐式恢复旧备份、不在数据库失败时回退空 SQLite(R7);镜像按选定 digest/扩展版本逐项核对(KTD7);秘密不入仓不入日志(baseline 硬边界 2)。
|
||||
|
||||
**生命周期**:M1(U1→U2)→ M2(U3→U4)→ M3(U5→U6)→ M4 生产切换(O1 另行授权,不在本 initiative 内)。U1 完成后按实测数据规模细化后续工期;全部必需子任务合入后置 `accepting_requirements=false` 收口合入 main。
|
||||
|
||||
## 子任务登记
|
||||
|
||||
| 子任务分支 | 必需 | 状态 | PR |
|
||||
|---|---|---|---|
|
||||
| feature/container-storage-plan-u1 | 是 | 未开始 | — |
|
||||
| feature/container-storage-plan-u2 | 是 | 未开始 | — |
|
||||
| feature/container-storage-plan-u3 | 是 | 未开始 | — |
|
||||
| feature/container-storage-plan-u4 | 是 | 未开始 | — |
|
||||
| feature/container-storage-plan-u5 | 是 | 未开始 | — |
|
||||
| feature/container-storage-plan-u6 | 是 | 未开始 | — |
|
||||
|
||||
## 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 | — |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:适用(本 initiative 的方案与实施计划)
|
||||
- 主记录路径/版本:`docs/superpowers/plans/2026-10-22-container-storage-plan.md`(2026-10-22 审阅快照,含落库状态注记)
|
||||
- 对应里程碑:M1(U1→U2)→ M2(U3→U4)→ M3(U5→U6);M4/O1 生产切换不在本 initiative
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1—R8 | 见方案 Requirements 表(R1 单二进制、R2 容器化、R3 环境绑定、R4 恢复可核对、R5 迁移覆盖、R6 克隆隔离、R7 无隐式破坏、R8 操作记录) | 符合(方案已核对现有 svc/manifest/迁移器/KB Compose 能力) | 高 | 见方案各单元完成条件与 Definition of Done | U1←无;U2←U1;U3←U1/U2;U4←U2/U3;U5←U4;U6←U1—U5 | 纳入本增量(U1—U6) |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:`docs/superpowers/plans/2026-10-22-container-storage-plan.md`(2026-10-22 审阅快照;Product Contract、KTD1—KTD7、High-Level Technical Design)
|
||||
- 实施计划引用:同上 Implementation Units U1—U6 与里程碑 M1—M3(M4/O1 不在本 initiative)
|
||||
- 测试计划引用:同上 Verification Contract 与各单元测试文件/场景清单
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 方案 Requirements R1—R8 | 已完成 | 主模型(方案会话)+ triage NEW | 2026-10-22 | /tmp/container-storage-plan-triage-20261022-b.json |
|
||||
| 需求评估 | 方案 Problem Frame/边界假设 | 已完成 | 三视角文档审阅 + 主模型复核 | 2026-10-22 | /tmp/container-storage-plan-20261022/review.md |
|
||||
| 需求表确认 | R1—R8 纳入 U1—U6 | 已完成 | 主模型 | 2026-10-22 | 方案 Requirements 表 |
|
||||
| 方案设计 | KTD1—KTD7 | 已完成 | 方案会话(已审阅) | 2026-10-22 | plan.md |
|
||||
| 实施计划 | U1—U6 + M1—M4 | 已完成 | 方案会话(已审阅) | 2026-10-22 | plan.md Implementation Units |
|
||||
| 实施与自检 | 待定 | 待执行 | — | — | — |
|
||||
| 正式测试与验收 | 待定 | 待执行 | — | — | — |
|
||||
| 代码 Review | 待定 | 待执行 | — | — | — |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 955a32de2bb49171348bad5f566cce935df19212 | 955a32de2bb49171348bad5f566cce935df19212 | 2026-10-22T09:09:21Z |
|
||||
|
||||
## 证据(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-22T09:08:28.920468+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",
|
||||
"[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": "ed0263b348ce265d081aab1faa0d1370f2a9478410fadfc3b9cec8a810c1295f",
|
||||
"receipt_sha256": "124a47e6bfbb43cbbbc398ad10bf42e3d448373a80affd297ed25c41d8b2b07a",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/124a47e6bfbb43cbbbc398ad10bf42e3d448373a80affd297ed25c41d8b2b07a.json"
|
||||
}
|
||||
```
|
||||
@@ -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,266 @@
|
||||
---
|
||||
artifact_contract: ce-unified-plan/v1
|
||||
product_contract_source: ce-plan-bootstrap
|
||||
execution: code
|
||||
date: 2026-10-22
|
||||
title: 单一应用二进制与容器化数据库、知识库存储改造方案
|
||||
---
|
||||
|
||||
> **落库状态(2026-10-22)**:方案已获审阅并授权实施。任务以 L1 initiative
|
||||
> `feature/container-storage-plan` 重新登记(R0=955a32de2bb49171348bad5f566cce935df19212)。
|
||||
> 原始规划工作区(基线 9a80897)因子模块物化失败已保留现场并对账后回收:
|
||||
> 全部子模块 HEAD 均在远端分支上、无独有提交、无未跟踪文件,任务卡副本存于
|
||||
> `/tmp/container-storage-plan-20261022/card-auto-generated.md`。本文正文为审阅时快照,
|
||||
> 其中基线 9a80897 与工作区失败描述属历史记录;实施以本卡与各子任务卡为准。
|
||||
|
||||
# 单一应用二进制与容器化数据库、知识库存储改造方案
|
||||
|
||||
本文是待用户审阅的方案与实施计划,不代表业务实施、部署或迁移授权。应用二进制及数据库容器的总体方向来自本次会话;具体分工、步骤与验收为本方案建议。
|
||||
|
||||
计划归属 Agent-System-Dev;本文因新工作区子模块物化失败暂存仓外,尚未提交或合入。路径以各标明仓库的根目录为基准。后续正规落点建议为 Dev `docs/superpowers/plans/2026-10-22-container-storage-plan.md`。
|
||||
|
||||
## Goal Capsule
|
||||
|
||||
- **目标**:运维人员能重建生产、测试、验证环境,把一个完整环境迁到新机器或新目录,并通过恢复验证确认账号、项目、文档、文件和知识库引用仍可用。
|
||||
- **手段**:一个应用服务二进制,两个 PostgreSQL 容器,三套逻辑数据库,外置持久文件;同一制品使用独立环境配置(KTD1—KTD4)。
|
||||
- **当前授权**:制定方案和计划。产品代码、容器部署、生产数据读取/复制、迁移切换均未在本轮执行;后续按用户明确授权范围实施。
|
||||
- **执行责任**:各仓遵循自身 AGENTS;Dev 范围的可独立实施单元按 AGENT-EXECUTION.md 分配全新外部 Agent 会话,host 管理范围、验证、独立 Review 和交付。任务进度放任务卡,不在本文伪装已完成。
|
||||
- **停止条件**:需要新增数据库类型、改变业务权限、无法完整迁移现有数据、需要丢弃新写入或扩大业务范围时停止相关动作,提交最小决策;不为容器化引入高可用平台、通用运维平台或应用微服务化。
|
||||
|
||||
## Product Contract
|
||||
|
||||
### Summary
|
||||
|
||||
保留前后端、Agent、RDM、知识库接入在一个应用进程内;数据库依赖使用 Compose。业务 PostgreSQL 容器分别承载基座库和 RDM 领域库,知识库 PostgreSQL/pgvector 容器承载语料与检索索引。生产、测试、验证环境分别建立,不通过临时修改生产连接串来“切环境”。
|
||||
|
||||
### Problem Frame
|
||||
|
||||
现有宿主数据库运维、应用快照、RDM 领域备份和知识库恢复各有入口,但尚未形成可核验的完整环境恢复闭环。容器化只改变数据库运行方式,必须同时处理身份绑定、数据库与文件的一致恢复点,以及复制环境后的副作用隔离。
|
||||
|
||||
### Requirements
|
||||
|
||||
| ID | 要求与可核验结果 |
|
||||
|---|---|
|
||||
| R1 | 完整应用服务只需一个可执行文件,包含前端资源、后端、Agent、RDM、知识库接入;运行机无需源码、Go 或 Node 构建环境。 |
|
||||
| R2 | 业务库和知识库存储容器化,明确持久卷、镜像版本、扩展版本及生命周期;容器重建后业务数据仍在。 |
|
||||
| R3 | 每环境独立绑定基座库、RDM 库、知识库、文件根、实例身份、端口和凭据;错误绑定拒绝启动。 |
|
||||
| R4 | 从备份恢复到新目录/主机后,账号、权限、业务记录、原文和历史引用可核对;成功备份必须是一个可解释的一致恢复点。 |
|
||||
| R5 | 从当前实际 SQLite/宿主 PG 迁移时覆盖所有必需持久数据所有者,保留业务标识、引用与原始内容,缺失或未知数据阻止切换。 |
|
||||
| R6 | 测试/验证克隆获得新实例身份及独立凭据,启动前隔离生产密钥、会话、自动任务和外部副作用;生产实例保持固定绑定。 |
|
||||
| R7 | 普通启停、重建、升级不隐式删卷、不执行数据库大版本升级、不恢复旧备份、不在数据库失败时回退到空 SQLite。 |
|
||||
| R8 | 形成可重复的组合验证和生产切换/回滚操作记录;实现完成、测试通过、生产迁移完成、业务验收通过分别记录。 |
|
||||
|
||||
### 边界与假设
|
||||
|
||||
- “单一二进制”按用户原话“最终的服务”解释为一个常驻应用服务。允许部署配置、脚本和离线迁移工具存在;不新增常驻 admin、KB HTTP 或前端服务。本期不为统一离线工具而重构全部 CLI。
|
||||
- 一期面向单机部署,每套环境独立;可在同机不同端口运行,也可迁到不同主机。宿主应用通过回环地址连接本机容器。
|
||||
- 知识库容器化指语料数据库及向量索引容器化;检索编排、权限与引用业务仍在应用二进制内。原始文档使用外置持久存储。
|
||||
- 允许维护窗口,先实现可验证的停写备份与迁移,不承诺零停机、实时复制、PITR 或自动故障切换。
|
||||
- 保留既有 SQLite/原生 PG 运行路径用于兼容与受控回退;本方案目标环境使用容器 PG,不顺带删除旧实现。
|
||||
- 公有知识库共享、跨环境共用只读卷、Kubernetes、托管云数据库、全量对象存储替换均不进入首期。
|
||||
- 生产停写窗口、源实例、机器容量、实际源版本与数据规模在 U1 核实;它们不妨碍方案审阅,但未核实前不能安排生产切换。规划不承诺未经测量的 RTO/RPO。
|
||||
|
||||
## Planning Contract
|
||||
|
||||
### 当前依据与可复用基础
|
||||
|
||||
本轮主仓基线 Dev `9a80897cb7e9108091d302e849f59662f21ce260`。以下是源码/文档核查,未探测实际生产实例,也未运行产品测试。
|
||||
|
||||
| 范围 | 已核对的依据 | 本次改造含义 |
|
||||
|---|---|---|
|
||||
| 单二进制 | Dev `Makefile`;Backend `web/embed.go`;RDM `main.go`、`scripts/build-product.sh` | 已有静态组合和前端嵌入,复用完整产品构建;不能用只有基座的二进制代替 RDM 产品。 |
|
||||
| 宿主 PG | Dev `scripts/svc.sh` 的 pg_bindir/db_create/pg_do_start/pg_do_stop/pg_run/pg_dump_run/db_backup/db_restore/db_clone | 容器支持需要完整后端分派,不能只替换启动命令;备份恢复继续使用已有安全边界。 |
|
||||
| 实例身份 | Dev `scripts/svc_manifest.py` 的 PROTECTED/db_anchor_of/reanchor;`tests/test_svc_identity.py` | manifest 已有权威身份;扩展三库和环境绑定,不另造第二份登记权威。 |
|
||||
| 基座迁移 | Backend `cmd/storage-migrate/main.go` | 已有 SQLite→PG 工具,但只迁 Backend;artifacts 参数只盘点不复制,不能据此声称迁了整个产品。 |
|
||||
| RDM 迁移 | RDM `main.go` 的 migrate-to-pg/migrate-check/backup-domain/restore-domain | 独立 RDM_DSN;RDM 领域库和 uploads 必须明确纳入。 |
|
||||
| KB Compose | KB-Regulations `deploy/compose.yaml`、`deploy/README.md` | 已有本地 pgvector/pgvector:pg16、回环端口、卷与扩展初始化模板;固定项目名 kbreg 不适合作为所有环境的全局名称,已有标签不等于生产版本批准。 |
|
||||
| 文件迁移 | Backend `internal/blobstore/blobstore.go`;KB-Regulations `migrations/0001_schema.sql`、`docs/operations/corpus-import.md` | 工件目录相关引用、source_assets.storage_uri 中 file 路径会影响异地恢复;不能只做 dump/restore。 |
|
||||
| 组合恢复 | Dev `scripts/svc.sh` 的 agent_backup_snapshot;`tests/integration/regulations-kb/README.md` | 应用快照不含全套数据库和原始 manifest;KB 测试可复用,但不证明全套外部文件已恢复。 |
|
||||
|
||||
RDM 研究对象为兄弟检出 `b14c84c6dcc945bd4d0a9ad446cf1ec980396491`,KB 为 `393555bec8d964f294f932616aa69ab833a06de1`;它们与 Dev `tests/integration/regulations-kb/components.pin` 的固定组合不同。本方案引用它们用于定位能力,正式实施前必须选定并验证精确组件组合,不能把研究检出当作已采纳版本。
|
||||
|
||||
已有恢复设计 `modules/documents/docs/proposals/2026-10-12-service-restart-data-recovery.md` 是设计约束来源,本文沿用其停写一致恢复点、恢复新目标、旧写者隔离思路,不把历史执行状态当现状。
|
||||
|
||||
### Key Technical Decisions
|
||||
|
||||
**KTD1.应用保持单一服务二进制。** 复用现有产品组合构建,配置和数据外置;容器承载持久数据库依赖,知识库插件不拆进程。落实 R1/R2。(session-settled: user-directed — chosen over containerizing the whole application: user requires the final service to remain a single binary)
|
||||
|
||||
**KTD2.两个 PG 容器、三个逻辑库。** 业务容器中分别设基座库与 RDM 库,知识库另用带 vector/pg_trgm 的 PG 容器。数据库使用独立应用角色;引导/迁移角色与应用权限分开,先核对现有启动迁移要求,再安排显式迁移阶段,不能直接收紧权限导致启动失败。落实 R2/R3/R5。
|
||||
|
||||
**KTD3.复用 svc 管理入口。** 新增窄的容器驱动和版本化环境配置,保留现有命令语义。manifest 是身份与期望态权威;Compose 文件是版本化模板,环境参数由同一配置解析生成。不得并存两份可独立修改的 DSN/卷权威。只管理明确归属本环境的容器,不启停外部数据库。落实 R3/R7。
|
||||
|
||||
**KTD4.稳定绝对数据根与环境独立资源。** 建议形态为 `state/<env>/` 与其外部 `backups/<env>/`,实际根由配置给出绝对路径;不相对代码 checkout/worktree 解析。Compose project、两套卷、各库、角色与端口都显式区分环境。凭据放受保护文件/secret 引用,日志和清单脱敏。文件树示意仅表达归属,不规定最终名称。
|
||||
|
||||
**KTD5.先备份完整恢复点,再追求不停机。** 关闭新写入口,排空或隔离在途任务,停止应用、定时任务、KB 导入/索引写者及迁移器;确认其他客户端不再写入后,对三库分别逻辑导出并复制受控文件。三次 dump 各自的快照不自动构成跨库一致性,靠整个窗口无写者保持一致。完成校验后才发布恢复包完成标记。落实 R4/R8。
|
||||
|
||||
**KTD6.恢复和克隆是不同操作。** 原实例恢复保留业务与实例身份,但须先停旧写者,绝不双开同一生产身份。测试克隆保留业务 ID 及引用关系,创建新的部署身份和守护绑定、数据库凭据;使生产登录会话/长期 API key 在副本失效,外部 provider 密钥不带入。后台任务与外发能力默认关闭,授权后逐项启用;不通过批量改业务 ID 实现隔离。落实 R3/R6。
|
||||
|
||||
**KTD7.迁移与升级分离。** 源 PG 采用同大版本兼容目标与匹配工具链,SQLite 迁移用已有逻辑迁移器;本次不顺带 PG 大版本升级或强制统一业务/KB PG 大版本。实施时固定经过验证的镜像 digest、平台与扩展版本。镜像官方文档说明不同主版本的 PGDATA/卷位置存在差异,配置按选定镜像逐项核对,不能把 KB pg16 的挂载路径盲用到另一主版本。初始化脚本仅负责新卷引导,既有库变更走显式迁移。落实 R2/R5/R7。
|
||||
|
||||
### High-Level Technical Design
|
||||
|
||||
以下是关系和流程草图,新增配置/命令的最终命名由实现单元在既有接口中确定。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Client[浏览器] --> App[一个完整应用二进制]
|
||||
Profile[环境配置与实例 manifest] --> App
|
||||
Profile --> Compose[Compose 数据依赖]
|
||||
App --> Base[业务容器 / 基座库]
|
||||
App --> RDM[业务容器 / RDM 领域库]
|
||||
App --> KB[知识库容器 / 语料库与索引]
|
||||
App --> Files[持久文件根 / 工件 uploads 原件]
|
||||
Compose --> Base
|
||||
Compose --> RDM
|
||||
Compose --> KB
|
||||
```
|
||||
|
||||
```text
|
||||
state/<env>/
|
||||
instance/ manifest 与受保护配置、应用版本引用
|
||||
files/ 工件、RDM uploads、KB 原件及必要解析产物
|
||||
Docker 持久卷:<project>-business、<project>-knowledge(各环境独立)
|
||||
backups/<env>/<backup-id>/ 三库导出、文件、配置恢复材料、版本与校验清单
|
||||
```
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[识别目标与锁定操作] --> B[预检配置/身份/镜像/资源]
|
||||
B --> C[启动两库容器]
|
||||
C --> D[认证/扩展/schema/三库身份检查]
|
||||
D --> E[按需执行显式迁移并核验]
|
||||
E --> F[启动应用]
|
||||
F --> G[业务就绪验证]
|
||||
D -->|失败| H[拒绝启动并保留现场]
|
||||
```
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Running
|
||||
Running --> Quiescing: 请求一致备份
|
||||
Quiescing --> Frozen: 写者已停且在途状态已记录
|
||||
Frozen --> Capturing: 三库与文件导出
|
||||
Capturing --> Verified: 完整性和清单校验
|
||||
Capturing --> Incomplete: 失败或中断
|
||||
Verified --> Running: 发布完成标记后恢复原实例
|
||||
Incomplete --> Running: 确认现场可安全恢复后解除停写
|
||||
```
|
||||
|
||||
恢复流程:读取完成清单 → 校验兼容版本与文件 hash → 选择空的新目标 → 建库/恢复角色权限及三库 → 还原文件与受控路径映射 → 核对实例模式(原实例/克隆)→ 禁止外部副作用下启动 → 登录、权限、文档、KB 引用验证 → 单独决定是否开放写入。任一步失败保持目标隔离,不覆盖旧系统。
|
||||
|
||||
### 完整恢复包合同
|
||||
|
||||
- 基座、RDM、KB 三库的逻辑导出及 schema/扩展版本;必需角色/权限用受控恢复定义记录,不能盲目导入生产全局超级用户。
|
||||
- 工件、uploads、KB 原件及必要解析产物;包含全部必需插件声明的数据。对对象存储记录不可变版本/校验值及可恢复获取方式;无法获取的必需对象阻止完整恢复声明。
|
||||
- 原始实例 manifest、部署配置、应用制品及插件/前端组合的 SHA、容器镜像 digest、平台信息;配置清单不含明文秘密,恢复秘密通过独立受保护介质或重新注入。仅含引用时必须验证引用在目标可解析,不能称恢复包独立可恢复。
|
||||
- 文件清单与 hash、数据所有者清单、停写时间和操作状态、恢复工具版本、校验结果及完成标记。备份与 runtime 卷分离,至少验证一份异机/独立存储副本可读取。
|
||||
- 原件路径变更仅调整定位信息,不改变内容 hash、语料修订、节点 ID、历史 excerpt 和审计封印。受保护审计内容若包含原路径,必须通过解析映射兼容,禁止重写封印记录。
|
||||
- 向量索引可重建,但 embedding 模型/维度和语料版本要记录;恢复测试不依赖付费重新嵌入,优先复用保存的向量重建索引。
|
||||
|
||||
## Implementation Units
|
||||
|
||||
### U1.环境与数据归属合同
|
||||
|
||||
- **需求**:R1/R3/R5/R6,KTD1—KTD4。
|
||||
- **目标**:一份可解析配置确定完整实例资源,明确现有数据来源和迁移边界。
|
||||
- **主要文件**:Dev `scripts/svc_manifest.py`、`scripts/svc.sh`、`docs/svc.md`、`PROJECT.md`;新增环境 schema/示例置于 Dev `deploy/`(建议路径)。
|
||||
- **做法**:扩展现有 manifest 绑定基座/RDM/KB,版本化旧配置升级;保护环境命令不受调用者环境变量污染。列出各数据所有者、类型、源版本、目录、体量、表/对象清单、引用形式和唯一写者。不输出秘密或业务内容。生产盘点需先获得对应访问授权。
|
||||
- **测试文件**:扩展 `tests/test_svc_identity.py`、`tests/test_svc_lifecycle_isolation.py`、`tests/test_svc_precheck.py`。
|
||||
- **场景**:旧配置升级保留身份;错库/错根/foreign marker 拒绝;同名项目或冲突端口在修改前失败;在另一 checkout 调用仍定位同一资源;三库中任一必需绑定缺失不能静默新建空库。
|
||||
- **完成条件**:配置、归属清单和迁移范围有唯一解释;明确需迁移的每个数据所有者。依赖:无。
|
||||
|
||||
### U2.容器数据库与 svc 接线
|
||||
|
||||
- **需求**:R2/R3/R7,KTD2—KTD4/KTD7。依赖 U1。
|
||||
- **主要文件**:Dev `scripts/svc.sh`(后端分派)、新增 `scripts/svc_containers.sh` 和 `deploy/compose.storage.yaml`(建议路径);KB-Regulations `deploy/compose.yaml`、`deploy/initdb/01-extensions.sql`、`deploy/README.md` 作为复用来源,避免复制成两套互相漂移的模板。
|
||||
- **做法**:start/stop/status/SQL/dump/restore 覆盖同一容器驱动;记录明确所有权;卷重建、普通停止和数据销毁分别处理。应用仅连 loopback 发布端口。引导创建基座/RDM 独立库与角色,KB 创建扩展;后续变更使用显式迁移。启动顺序验证真实认证、扩展和身份,不能只依赖 pg_isready。
|
||||
- **测试文件**:新增 Dev `tests/test_svc_containers.py`;扩展 `tests/test_svc_precheck.py`、`tests/test_svc_realcombo.py`。
|
||||
- **场景**:prod/test/verify 同时运行互不冲突;停止/重建一套不影响其他套且不丢数据;缺 Docker/镜像/磁盘/凭据时在破坏性动作前失败;密码变更不误以为改 env 就改变已初始化库;容器意外停止时应用不得写入 SQLite。
|
||||
- **完成条件**:用固定制品及临时环境完成两容器三库启动、持久化、重建与失败路径验证。运行冒烟优先,不用大量模拟测试替代真实容器验证。
|
||||
|
||||
### U3.迁移与文件引用兼容
|
||||
|
||||
- **需求**:R4/R5/R7,KTD5—KTD7。依赖 U1/U2。
|
||||
- **主要文件**:Backend `cmd/storage-migrate/`、`internal/blobstore/blobstore.go`;RDM `main.go` 与其现有领域迁移实现;KB-Regulations 的 source_assets 读取/原件定位实现及 `docs/operations/corpus-import.md`。函数名在实施启动时定点核对,不另建并行迁移框架。
|
||||
- **做法**:基座和 RDM 分别复用现有迁移器;覆盖实际 schema 的新增表、外键、序列/标识和身份记录;未知表不能悄悄遗漏。已有 PG 走兼容逻辑恢复。文件迁移校验内容和所有权,新增受控资产根映射或兼容相对引用,维持旧引用可读取。禁止迁移时跨出允许根或追随外部软链复制未知文件。
|
||||
- **测试文件**:Backend `cmd/storage-migrate/main_test.go`、`integration_test.go`、`seeded_singletons_test.go` 与 `internal/blobstore/` 对应测试;RDM 现有领域迁移测试(实施前定位);KB-Regulations `tests/integration/recovery_pg_test.go`。
|
||||
- **场景**:SQLite 基座+RDM 迁入不同逻辑库;原 PG 同版本恢复;空库与真实结构 fixture;失败/重跑/非空目标拒绝;未知表拒绝;不同根目录恢复后读取旧工件与 file URI 原件;越界路径拒绝;历史 citation/审计 hash 不变。
|
||||
- **完成条件**:逐所有者对账,不仅比较行数,还核对 ID、关键引用、内容 hash、权限和新写入;不能完成的必需所有者阻止继续生产准备。
|
||||
|
||||
### U4.组合备份与恢复新目标
|
||||
|
||||
- **需求**:R4/R5/R8,KTD5/KTD7。依赖 U2/U3。
|
||||
- **主要文件**:Dev `scripts/svc.sh` 的 backup/restore 入口;新增组合恢复辅助实现(建议 `scripts/svc_bundle.py`);`docs/svc.md`、恢复操作说明;各组件只提供必要导出/校验接口。
|
||||
- **做法**:按恢复包合同采集全部数据,锁定同环境操作,停写并确认所有写者;失败包无完成标记。完整备份默认只恢复到空的新目标;应用制品和 schema 兼容预检先于开放写入。秘密处理有明确备份/重新注入路径及权限校验。
|
||||
- **测试文件**:扩展 `tests/test_svc_backup.py`;新增 `tests/test_svc_bundle.py`;扩展 `tests/ops/run_backup_drill.sh` 的不同目标根场景。
|
||||
- **场景**:三库与文件可恢复;备份中断、空间不足、错包、hash 损坏、缺原件、缺必要 secret、镜像不兼容拒绝;目标非空拒绝;不得覆盖生产;重新启动源实例不把未完成包发布为成功;保留原生产身份恢复时,目标接管前必须隔离旧写者;独立克隆从已完成的一致备份恢复,源实例可在备份完成后继续运行,但目标首次启动前必须完成新身份、独立凭据及副作用隔离。
|
||||
- **完成条件**:干净环境恢复完整产品并生成可复核清单;实测备份大小、停写时长和恢复用时。
|
||||
|
||||
### U5.测试/验证克隆与环境选择
|
||||
|
||||
- **需求**:R3/R6/R8,KTD4/KTD6。依赖 U4。
|
||||
- **主要文件**:Dev `scripts/svc.sh` 的 clone/启动接线、`scripts/svc_manifest.py`;Backend/RDM 现有身份、会话和任务控制入口仅作必要接线;测试数据 fixture 与说明。
|
||||
- **做法**:以明确目标环境创建新实例。业务 ID 不改,部署身份、守护与凭据换新;登录会话/API key 不在副本继续有效,账号密码处理按克隆用途明确。需要复制生产敏感数据时先落实访问范围和脱敏策略;常规自动验收优先合成数据。外部密钥、自动调度与恢复任务副作用默认禁用,并在首次启动前生效。
|
||||
- **测试文件**:扩展 `tests/test_svc_identity.py`、`tests/test_svc_lifecycle_isolation.py`;新增 `tests/test_svc_environment_clone.py`。
|
||||
- **场景**:克隆后生产仍正常;新身份不触发原守护冲突;旧 token 无法访问副本;待执行任务不自动发信/调用真实 provider/写生产目录;选择 test 不修改 prod 配置;操作摘要明确目标环境。
|
||||
- **完成条件**:三套环境同机并存,隔离由实际读写与副作用拒绝验证,不靠名称或配置声明。
|
||||
|
||||
### U6.单二进制与组合验收、运维交付
|
||||
|
||||
- **需求**:R1—R8,KTD1—KTD7。依赖 U1—U5。
|
||||
- **主要文件**:Dev `Makefile`、`tests/test_svc_realcombo.py`、`tests/integration/regulations-kb/run.sh`、`components.pin`、`PROJECT.md`、`docs/svc.md`;RDM `scripts/build-product.sh`;各仓必要 runbook/组合指针。
|
||||
- **做法**:固定完整产品与镜像组合,在没有源码及 Go/Node 的机器/隔离环境用同一应用制品启动。把恢复、克隆、数据库容器重启加入组合验证。必要管理操作优先复用同一产品已有入口,离线工具不成为常驻依赖。受影响仓独立验证后再推进 Dev 组合,不重做发布体系。
|
||||
- **场景**:UI/API、正常账号登录、角色边界、项目/会话、RDM 上传/文档原件读取、知识库检索、历史引用解析;两库容器依次重启;新目录恢复;test/verify 重建不影响 prod;客户端制品无 secret。
|
||||
- **完成条件**:满足 Definition of Done;输出生产切换操作单与回滚分界,实测停写时间供用户安排窗口。生产执行属于下面 O1,不由代码测试替代。
|
||||
|
||||
## Verification Contract
|
||||
|
||||
- 本轮是规划:产品测试、真实容器试验、迁移演练、生产盘点/部署均为 **NOT_RUN**;源码核查属于 SELF,不能提升为 GATE 或 REPLAY。
|
||||
- 实施每单元先运行其最小直接检查,容器与文件恢复必须有真实临时环境验证。禁止把缺 DSN 导致的跳过计作通过;真实测试仅指向显式临时环境。
|
||||
- Backend 真 PG 迁移测试使用 `AGENT_PG_TEST_DSN`;法规组合门禁使用 `KBREG_TEST_DATABASE_URL`、精确 RDM/KB pin 及既有 `make test-regulations-kb`。
|
||||
- Dev 治理脚本确有修改时运行 `make test-governance`;最终组合按项目要求执行 `make test`,并以当前 CI 要求核对 `test-pg`、`svc-realcombo` 等检查,不能凭旧清单声称全部通过。
|
||||
- 推进 Dev gitlink 前完成所需组合验证;命令原始输出经 `make evidence` 写任务卡。正式 Review 前重放最小集合、同步权威目标;合入信任来自精确 HEAD 的必需 CI 或独立 REPLAY。
|
||||
- 全量检查只在最终候选或有必要时运行;发现无关非阻塞问题不扩大本次任务。
|
||||
|
||||
## O1.生产迁移与回滚计划(另行授权执行)
|
||||
|
||||
1. **确认源与窗口**:记录实际源实例、三库/文件清单、schema、版本、容量、现有身份、备份位置及授权范围;确认恢复演练达标和可接受停写时间。
|
||||
2. **提前准备目标**:拉取固定镜像、准备空库/角色/持久目录、验证磁盘与网络;用演练副本验证完整应用制品。此阶段不改生产连接。
|
||||
3. **冻结生产写入**:暂停入口及所有后台/外部写者,排空或隔离在途工作,确认旧实例不会被 supervisor 自动拉起;记录已确认写入的对账点。
|
||||
4. **最终备份与迁移**:按 U3/U4 生成最终完整包并恢复目标;核对所有必需数据与文件,运行只读业务验证;保持外部副作用关闭。
|
||||
5. **受控切换**:以迁移操作更新目标绑定、身份和文件路径;先验证登录/权限/文档/知识库,再开放写入及经确认的后台任务。避免两个生产写者同时存在。
|
||||
6. **观察与保留**:保留旧数据及最终备份,只读保护并记录新系统写入开始时间;到达业务验收及约定保留期后才单独安排旧资源回收。
|
||||
|
||||
回滚分界必须写入操作单:
|
||||
|
||||
- **新目标尚无业务写入**:停止新实例,确认无新数据后恢复旧配置/旧写者;不可让双方同时写。
|
||||
- **新目标已有业务写入**:停止写入并保存新数据,禁止简单切回旧库;评估向前修复或经过验证的数据对账/回迁。未经确认不丢弃新写入。
|
||||
- **schema 已改变**:旧应用制品不一定兼容。代码回滚、数据库恢复和业务数据回迁分别验证,不能用“换回旧二进制”代替恢复方案。
|
||||
|
||||
## Definition of Done
|
||||
|
||||
代码交付完成须同时满足:
|
||||
|
||||
- 一个完整应用服务二进制在目标运行环境启动,业务/RDM/KB 数据存储符合两容器三逻辑库边界。
|
||||
- prod/test/verify 可独立启动、停止、重建与选择,配置/数据/身份/权限隔离已验证。
|
||||
- 完整备份可以恢复到不同根目录;原文可读、业务引用和历史法规引用不变;缺失数据时明确失败。
|
||||
- 真实迁移 fixture 对账通过,重启与容器重建不丢数据,失败不会回退空系统或启动外部副作用。
|
||||
- 精确组件组合、测试证据、独立 Review、所需 CI 及规范文档齐全;没有遗留试验实现进入最终 diff。
|
||||
- O1 的执行另记:代码完成不等于生产迁移完成,恢复演练也不等于业务验收。
|
||||
|
||||
## 顺序与建议里程碑
|
||||
|
||||
| 里程碑 | 工作 | 出口 |
|
||||
|---|---|---|
|
||||
| M1 最小可运行 | U1 → U2 | 同一应用连接两容器三库,test/verify 并存,持久化与错误绑定验证通过 |
|
||||
| M2 可迁移可恢复 | U3 → U4 | 完整数据恢复到新目录,原文和历史引用可核对 |
|
||||
| M3 可安全切环境 | U5 → U6 | 克隆隔离、完整产品组合验证、操作说明齐备 |
|
||||
| M4 生产落地 | O1 | 获得窗口授权后迁移、业务验收,旧数据按约定保留 |
|
||||
|
||||
U1 完成后再按实际 schema 覆盖、文件规模及演练用时细化工期;当前不把未测量的数据量换算为承诺日期。每个里程碑只处理阻塞目标的问题。
|
||||
|
||||
## 外部依据与待核验项
|
||||
|
||||
本轮尝试读取 Docker Compose、PostgreSQL 备份与 pgvector 官方文档;网络读取不完整。成功获取 Docker Official Image 的 PostgreSQL 说明(docker-library/docs 的 postgres/README.md),其中确认 PGDATA/卷位置随主版本变化、初始化参数仅对新数据目录生效、密码支持文件注入。这些仅作为镜像适配约束,未确定最终镜像 digest,也未验证某个部署平台。
|
||||
|
||||
实施 U2 前需针对所选精确版本再次核对 Compose/镜像/pgvector 官方资料,并做真实冒烟;失败则阻止该镜像采用,不阻止本方案的源码设计工作。
|
||||
@@ -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
|
||||
@@ -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...a75783fd2d
+1
-1
Submodule modules/frontend updated: 113103f028...279b2d02a2
+1
-1
Submodule modules/ui updated: fadabceade...df1c3ed8d6
@@ -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"
|
||||
@@ -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()
|
||||
|
||||
+9
-6
@@ -349,18 +349,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. 外部直连与配置文件来源:后续无参数启动仍恢复 ----
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user