Compare commits
76
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
304a2d2530 | ||
|
|
0bef900271 | ||
|
|
0e47879818 | ||
|
|
3ddee11275 | ||
|
|
58ad91b0f2 | ||
|
|
b75da20afa | ||
|
|
92b6937ec2 | ||
|
|
ec89964880 | ||
|
|
3dae91ecf4 | ||
|
|
a49aee1bbc | ||
|
|
bccb88f294 | ||
|
|
d610a99274 | ||
|
|
fa79a8eb84 | ||
|
|
c21601454b | ||
|
|
274a118924 | ||
|
|
ce2754f9c2 | ||
|
|
bbd80aa388 | ||
|
|
7f95c61a63 | ||
|
|
90eddbfa8e | ||
|
|
e62092a447 | ||
|
|
f6a43bd23b | ||
|
|
8148120e87 | ||
|
|
224771dd1e | ||
|
|
35524f3d65 | ||
|
|
2068d0a630 | ||
|
|
235d700831 | ||
|
|
3f9a121f47 | ||
|
|
7434a7f2a9 | ||
|
|
e767759af5 | ||
|
|
c3fbf85732 | ||
|
|
e5c491a693 | ||
|
|
6a3e6764fe | ||
|
|
78676bd0a2 | ||
|
|
317afaab2d | ||
|
|
c1bc29f8a6 | ||
|
|
6ced938a12 | ||
|
|
b9560c7e21 | ||
|
|
9a6ae6c571 | ||
|
|
c3fc389b9f | ||
|
|
9eeba5afa2 | ||
|
|
66e2c98060 | ||
|
|
b58caf2d99 | ||
|
|
ade7083c57 | ||
|
|
f673c2a15a | ||
|
|
b0d481e91e | ||
|
|
c4bd492230 | ||
|
|
2739dcbfcc | ||
|
|
03ed068408 | ||
|
|
d0b2bde9b0 | ||
|
|
cb36b59acc | ||
|
|
826398c969 | ||
|
|
9d5ed689ce | ||
|
|
c22b9fb34a | ||
|
|
7f23ed9ed1 | ||
|
|
abf4c27736 | ||
|
|
a14752bf1e | ||
|
|
f84d7c724e | ||
|
|
7a289378a1 | ||
|
|
c92443280e | ||
|
|
ce7b0d9c44 | ||
|
|
1651efa97b | ||
|
|
88581f3440 | ||
|
|
8cb2d2297b | ||
|
|
3d3474a731 | ||
|
|
9126b09b5d | ||
|
|
41391776e1 | ||
|
|
74f3278514 | ||
|
|
d138dd8b45 | ||
|
|
c88cb95654 | ||
|
|
a29562ef83 | ||
|
|
b63685d7b5 | ||
|
|
98ee59c477 | ||
|
|
60bd1bedb4 | ||
|
|
10b751f159 | ||
|
|
ceb6bb9f5a | ||
|
|
940409a85e |
@@ -178,6 +178,31 @@ jobs:
|
||||
tar -C /usr/local --strip-components=1 -xJf "$RUNNER_TEMP/node.tar.xz"
|
||||
- name: Install house-style Python packages
|
||||
run: python3 -m pip install --break-system-packages -i https://pypi.tuna.tsinghua.edu.cn/simple python-docx python-pptx openpyxl
|
||||
- name: Provision the Gitea token file for facts verification
|
||||
env:
|
||||
T1: ${{ secrets.GITEA_TOKEN }}
|
||||
T2: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "provision: GITEA_TOKEN length=${#T1}, GITHUB_TOKEN length=${#T2}"
|
||||
if [ -n "$T1" ]; then
|
||||
token="$T1"; source_name=GITEA_TOKEN
|
||||
elif [ -n "$T2" ]; then
|
||||
token="$T2"; source_name=GITHUB_TOKEN
|
||||
else
|
||||
echo "provision: GITEA_TOKEN/GITHUB_TOKEN both empty"
|
||||
exit 1
|
||||
fi
|
||||
echo "provision: using ${source_name}"
|
||||
mkdir -p "$HOME/.secrets"
|
||||
printf 'HOME:\n- ACCESS_TOKEN=%s\n' "$token" > "$HOME/.secrets/gitea"
|
||||
chmod 600 "$HOME/.secrets/gitea"
|
||||
code="$(curl -fsS -m 10 -H "Authorization: token ${token}" https://gitea.naitangbaobao.cn/api/v1/user -o /dev/null -w '%{http_code}')" || true
|
||||
if [ "$code" != "200" ]; then
|
||||
echo "provision: /api/v1/user self-check failed with http code ${code:-none} (source ${source_name})"
|
||||
exit 1
|
||||
fi
|
||||
echo "provision: /api/v1/user self-check ok (http 200)"
|
||||
- name: make test (boundaries, pins, libs, core, backend, frontend, ui, agent-command)
|
||||
run: make test
|
||||
- name: Build the single binary
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
# feature/catalog-recall-eval-a3 任务卡
|
||||
|
||||
任务主记录:PR #713 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/713;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 6ced938a126981ee2a4fab671a2da94f49cb56c5
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-catalog-recall-eval-a3(worktree);分支:feature/catalog-recall-eval-a3
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=bf205afb1f04e503573784c93b52a991095554ed7314d9ac0f6c1996660a3211;triage_receipt=/tmp/triage-a3-1792716554.json;receipt_sha256=1a4ee8fe2133a52386520d35955d0fc600817f9a6bd94d40730eb8fce0904f44
|
||||
|
||||
- 授权与交付终点:用户已批准启动 A3 能力目录召回评测(方案 Functional Follow-Ups 第三项:先建脱敏评测集并产出指标基线,"是否需要检索/调模型"的决策由基线数据支撑、另行决策)。范围内连续推进:设计、agent-per-unit 实施、验证、组合、Review、PR/CI/合入。未授权:检索实现、模型/提示词调整、A2 后续、A4、部署。
|
||||
- 设计决定(2026-10-22,host):
|
||||
1. 评测落在 agent-intent 既有机制上(cmd/intentctl eval + testdata/corpus.json 共用格式),不新建平行体系。
|
||||
2. 数据集五类覆盖(否定/引用指令、多意图、错误槽位、过期确认、生成规则一致性),全部合成脱敏、由现有 fixture 规格派生;规模 ≥30 例。
|
||||
3. 指标层只记录不设质量阈值:top-1 命中率、误执行数(命中≠预期动作)、澄清率、fallback 率、生成 vs 规则一致性(离线 fake generator)。
|
||||
4. 基线报告(evaluation/BASELINE.md + metrics.json)随库提交,附复跑方式;质量结论由人/后续任务基于数据做出。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:需求拆解;当前增量:I1(待定义)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
**目标**:脱敏评测集(五类 ≥30 例)+ intentctl eval 指标输出(JSON 与表格)+ 基线报告;离线可复跑(fake generator,无真实模型调用)。
|
||||
|
||||
**非目标**:检索/召回增强实现;提示词或模型调整;真实用户数据;控制台;A2 后续/A4。
|
||||
|
||||
## 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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | 6ced938a126981ee2a4fab671a2da94f49cb56c5 | 6ced938a126981ee2a4fab671a2da94f49cb56c5 | 2026-10-23T00:51:18Z |
|
||||
|
||||
## 证据(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 与规划记录)
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- 实施收据:a3-recall-eval-dataset(cursor attempt1,五类语料+指标+基线报告,intent 6aa4925,verify-a3 exit=0);gitlink 集成(native d1f72aa/squad a5afe9c/backend 8dc91c1 含嵌套 intent/native 级联,a3git5/a3git10-run.json)。
|
||||
- 组合验证:全量 make test PASS(verify-a3-combo3.json exit=0)。
|
||||
- Review 记录:独立 CLI 评审单元 a3-independent-review(收据 /tmp/agent-stack-b2/reviewa3-run.json);结论 APPROVE;报告 .task/reviews/2026-10-22-a3-recall-eval-independent-review.md;被审对象 35524f3d 组合 vs main
|
||||
- 冲突记录: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-23T00:50:13.586423+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "6ced938a126981ee2a4fab671a2da94f49cb56c5",
|
||||
"after_head": "6ced938a126981ee2a4fab671a2da94f49cb56c5",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "6ced938a126981ee2a4fab671a2da94f49cb56c5",
|
||||
"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=6ced938a12",
|
||||
"[remote] origin/main=6ced938a12 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=8",
|
||||
"[recycle] merged un-recycled branches: feature/exec-recovery-diagnostics-a2 fix/rdm-pin-advance-latest-2",
|
||||
"[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] 6ced938a12(完整:6ced938a126981ee2a4fab671a2da94f49cb56c5)",
|
||||
"[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": "9f6b7e8979503847e2ec5623a17ee9624cbafd5450149556482244a86abe9192",
|
||||
"receipt_sha256": "1cf2c62aea8ef98b5d211cb94c053174407c70aeab274681c1b77db0a2878db9",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/1cf2c62aea8ef98b5d211cb94c053174407c70aeab274681c1b77db0a2878db9.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #713(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/713)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,167 @@
|
||||
# feature/exec-recovery-diagnostics-a2 任务卡
|
||||
|
||||
任务主记录:PR #704 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/704;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= c92443280ef8e9ed29bf5bf5250df650c01b4478
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-exec-recovery-diagnostics-a2(worktree);分支:feature/exec-recovery-diagnostics-a2
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=34e72676959d0e970f1a3cb0e90ac2067dfb31f3c2e7a6650b5de9f3f77923c7;triage_receipt=/tmp/triage-a2-1792672248.json;receipt_sha256=7924fdbaeba41d98994c2e11bc96026d4db3cdbd0b28a9d1e381400eae9a6de4
|
||||
|
||||
- 授权与交付终点:用户已批准启动 A2 执行与恢复诊断(方案 Functional Follow-Ups 第二项)。范围内连续推进:设计、agent-per-unit 实施、验证、组合、Review、PR/CI/合入。未授权:控制台/前端界面、A3/A4、部署。
|
||||
- 设计决定(2026-10-22,host):
|
||||
1. 只读诊断 API:GET /api/runs/{id}/diagnostics(owner/project 授权,复用既有 run 鉴权),响应聚合:状态、当前责任方与等待原因(running / waiting_hitl / confirmation_pending / recovery_hold(原因+created_at) / reconciling / failed+reason / completed)、最后稳定 checkpoint(如可得)、已知副作用与关联 operation 记录(复用 cmdbus 审计信封查询)、下一步动作建议(确认卡片 / 解除 hold / 可安全重试 / 需人工核对 / 无)。
|
||||
2. 纯读聚合,不改任何执行/恢复行为;复用既有 store 方法(GetRun/RecoveryHold/ListInterruptedSubagentRuns/审计查询)与 U7b 的 operation 查询。
|
||||
3. 不建控制台、不加写接口;批量列表走既有会话 run 列表。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:需求拆解;当前增量:I1(待定义)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
**目标**:单只读端点聚合"卡在哪里/谁在等/为何等/最后 checkpoint/已知副作用/下一步",覆盖 HITL 等待、确认等待、恢复 hold(批二 needs-reconciliation)、失败原因等全部可诊断状态。
|
||||
|
||||
**非目标**:控制台/UI;任何写操作或状态变更端点;A3/A4; squad sqlite 协作层的跨库聚合(只读后端 store 与审计)。
|
||||
|
||||
## 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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | c92443280ef8e9ed29bf5bf5250df650c01b4478 | c92443280ef8e9ed29bf5bf5250df650c01b4478 | 2026-10-22T12:37:28Z |
|
||||
|
||||
## 证据(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 与规划记录)
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- 实施收据:a2-diagnostics-api(pi attempt1,rundiag 聚合 + GET /api/runs/{id}/diagnostics,backend 06b0c9f,verify-a2b exit=0);架构基线边登记(repair,a2arch-run.json);backend pin 集成(a2git2-run.json,cursor 配额间歇故障重试一次后成功)。
|
||||
- 组合验证:全量 make test PASS(verify-a2-combo2.json exit=0,本轮 svc 既有 flake 未复现)。
|
||||
- Review 记录:独立 CLI 评审单元 a2-independent-review(第 2 次派发 success;首轮 pi-glm 终态记录不可解码,现场保留);结论 APPROVE(0 blocker / 0 major);报告 .task/reviews/2026-10-22-a2-diagnostics-independent-review.md;被审对象合并基线 vs main
|
||||
- 冲突记录: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-22T12:30:58.698213+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "c92443280ef8e9ed29bf5bf5250df650c01b4478",
|
||||
"after_head": "c92443280ef8e9ed29bf5bf5250df650c01b4478",
|
||||
"branch": "fix/gitea-address-migration",
|
||||
"target_branch": "main",
|
||||
"target_head": "c92443280ef8e9ed29bf5bf5250df650c01b4478",
|
||||
"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=fix/gitea-address-migration head=c92443280e",
|
||||
"[remote] origin/main=c92443280e ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=7",
|
||||
"[recycle] merged un-recycled branches: fix/gitea-endpoint-facts",
|
||||
"[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] c92443280e(完整:c92443280ef8e9ed29bf5bf5250df650c01b4478)",
|
||||
"[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": "d670ed210b367c72f877fb63bbbeb5b28411e95cb56fab782f27172fa481ed89",
|
||||
"receipt_sha256": "2829e17065c329449fac8ef2690d50babf82015a1f546dcbfd3f27f67789dd0c",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/2829e17065c329449fac8ef2690d50babf82015a1f546dcbfd3f27f67789dd0c.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #704(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/704)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,168 @@
|
||||
# feature/intent-multi-turn-a1 任务卡
|
||||
|
||||
任务主记录:PR #700 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/700;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 955a32de2bb49171348bad5f566cce935df19212
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-intent-multi-turn-a1(worktree);分支:feature/intent-multi-turn-a1
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=5dd60900f5e7d69e382c367bfde373c087528dd9dcae7e16199c956ef4f3d9bf;triage_receipt=/tmp/triage-a1-1792658810.json;receipt_sha256=35b32668d143e39f386f069ae4c0004cae9761125d95241bb66e6aff1530d24b
|
||||
|
||||
- 授权与交付终点:用户已批准启动 A1 多轮意图闭环(方案 Functional Follow-Ups 第一项)。范围内连续推进:设计、agent-per-unit 实施、验证、组合、Review、PR/CI/合入。未授权:A2-A4、A2A、部署/发布、其余业务动作族扩展。
|
||||
- 设计决定(2026-10-22,host):
|
||||
1. 第一类真实业务动作 = **运行取消**("取消刚才的任务"):cancel 命令已注册且为高后果动作,走既有 cmdbus 确认卡;后续动作族另行增量。
|
||||
2. 会话状态存 Backend 既有 store(新表 intent_session,键 owner+project+conversation),保存挂起预期(action ref/revision/缺失槽位/已累积槽位)与过期时间(30 分钟 TTL);revision 递增防过期回答污染新任务。
|
||||
3. 库层不动 Stage 1 语义:Backend 实现 ExpectationProvider 数据源并在 intentgate/route 消费 set_slot/change_slot/cancel/expectation 命令(route.go 现按"宿主不消费"转 turn——本任务改变这一点)。
|
||||
4. 高危动作仍必须经 cmdbus 确认;会话只是槽位与预期的载体,不授予执行权。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:需求拆解;当前增量:I1(待定义)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
**目标**:多轮对话中:缺槽动作 → 澄清 → 用户回答补槽 → 动作完成(第一类=运行取消);"取消刚才的任务"作用于明确对象;歧义先澄清;过期回答(TTL 外)不影响新任务;双用户隔离;revision 防串轮。拆两个单元:U-A1b 会话状态与命令消费(intentgate/route/server + store 表);U-A1c 取消意图路由到 cmdbus(确认卡 + 明确对象解析)。
|
||||
|
||||
**非目标**:A2-A4;A2A;更多业务动作族;前端界面改动;部署。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:待核对(无适用规划时说明原因)
|
||||
- 主记录路径/版本:待核对(路径按仓库根目录定位,归档后不变;只引用唯一正文)
|
||||
- 对应里程碑:待核对
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 待填写 | 待评估 | 待定 | 待定义 | 待确认 | 待评审 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:待填写(含对象版本)
|
||||
- 实施计划引用:待填写(含增量与需求 ID)
|
||||
- 测试计划引用:待填写(含需求 ID 与验收条件)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 待定 | 待执行 | — | — | — |
|
||||
| 需求评估 | 待定 | 待执行 | — | — | — |
|
||||
| 需求表确认 | 待定 | 待执行 | — | — | — |
|
||||
| 方案设计 | 待定 | 待执行 | — | — | — |
|
||||
| 实施计划 | 待定 | 待执行 | — | — | — |
|
||||
| 实施与自检 | 待定 | 待执行 | — | — | — |
|
||||
| 正式测试与验收 | 待定 | 待执行 | — | — | — |
|
||||
| 代码 Review | 待定 | 待执行 | — | — | — |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 955a32de2bb49171348bad5f566cce935df19212 | 955a32de2bb49171348bad5f566cce935df19212 | 2026-10-22T08:48:01Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:未入队;调度记录:无
|
||||
- entry_id:无;attempt_id:无;窗口执行者:无
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:待核对(依据实际成果更新适用方案/规划)
|
||||
- 首选下一任务:待核对(无适用规划、已完成或受阻时如实说明,不虚构任务)
|
||||
- 推荐依据:待填写
|
||||
- 前置条件/待确认:待核对
|
||||
- 预期产物/验收条件:待定义
|
||||
- 重复任务核对:待核对(已有/在途任务、PR 与规划记录)
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- 实施收据:U-A1b(pi attempt1,intent-session 会话/命令消费,backend 0fb64c4,verify-a1b exit=0);U-A1c(pi attempt1,cancel 经 cmdbus 路由,backend c2e3104→verify-a1c exit=0);架构基线边登记(repair,a1arch4-run.json)与 storage-migrate manifest 补齐(repair,backend 9ee752c,verify-a1sr exit=0)。
|
||||
- 组合验证:test-governance 存在 svc 环境不稳定失败(两次运行分别挂在 test_addr_change / test_U2_health;addr_change 已在干净 main 复现,见批二后验证),其余全部目标 PASS(verify-a1-rest.json exit=0);CI 环境为最终裁决(批二同代码 CI 全绿)。
|
||||
- Review 记录:待执行(独立 CLI 评审,随 PR 流程)
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-22T08:46:59.325275+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"after_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||
"[repo] branch=main head=955a32de2b",
|
||||
"[remote] origin/main=955a32de2b ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=4",
|
||||
"[recycle] merged un-recycled branches: feature/container-storage-plan",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 955a32de2b(完整:955a32de2bb49171348bad5f566cce935df19212)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||
],
|
||||
"output_sha256": "f06e6cc8ddb0c414c2ac92e5100dc58b0b926a2bf7196273056b01be80253217",
|
||||
"receipt_sha256": "32899ef2b4ebee5bf1725fb79e1bba0382177731155cebab04178821e6da3f1b",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/32899ef2b4ebee5bf1725fb79e1bba0382177731155cebab04178821e6da3f1b.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #700(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/700)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,100 @@
|
||||
# fix/rdm-pin-advance-latest-2 任务卡
|
||||
|
||||
任务主记录:PR #711 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/711;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 6ced938a126981ee2a4fab671a2da94f49cb56c5
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-rdm-pin-advance-latest-2(worktree);分支:fix/rdm-pin-advance-latest-2
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=93558e2670e0b23a79612fed5a161be7cb54de39485d98d72f67b6541ee38a8a;triage_receipt=/tmp/pin-f2-triage.json;receipt_sha256=00841f4dfa7626161dcab13a8a598b10fdd81dcf26679b8de6d8177beaf3d655
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式: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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | 6ced938a126981ee2a4fab671a2da94f49cb56c5 | 6ced938a126981ee2a4fab671a2da94f49cb56c5 | 2026-10-23T00:50:42Z |
|
||||
|
||||
## 证据(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-23T00:49:47.450962+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "6ced938a126981ee2a4fab671a2da94f49cb56c5",
|
||||
"after_head": "6ced938a126981ee2a4fab671a2da94f49cb56c5",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "6ced938a126981ee2a4fab671a2da94f49cb56c5",
|
||||
"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=6ced938a12",
|
||||
"[remote] origin/main=6ced938a12 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=7",
|
||||
"[recycle] merged un-recycled branches: feature/exec-recovery-diagnostics-a2",
|
||||
"[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] 6ced938a12(完整:6ced938a126981ee2a4fab671a2da94f49cb56c5)",
|
||||
"[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": "d78ae27fdc16acddac83815947924a7eb3d6031e421381f3d9313f29f3c0fc9e",
|
||||
"receipt_sha256": "aa32f1bbde2a3b12b355fedc1e5ed3b91dea43758fb2a307e93f56fd2c5ae9af",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/aa32f1bbde2a3b12b355fedc1e5ed3b91dea43758fb2a307e93f56fd2c5ae9af.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | c3fbf85732788ba91d32c921783993a645580041 | c3fbf85732788ba91d32c921783993a645580041 | 2026-10-23T01:26:28Z |
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #711(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/711)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | e62092a4479bbe101e74e91b39e03577ce0786ae | bbd80aa38874dbac8a3188b42bc6ed977a4fb379 | 2026-10-23T02:14:09Z |
|
||||
@@ -0,0 +1,98 @@
|
||||
# fix/svc-test-port-governance-batch1 任务卡
|
||||
|
||||
任务主记录:PR #702 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/702(merge cb36b59acc);状态:已完成(回填);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 3d3474a731f9ef3ee1f41935e63bca3afb70db56
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-svc-test-port-governance-batch1(worktree);分支:fix/svc-test-port-governance-batch1
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=1723e9e3e6bd50bedc041afba3533c35e8b663edb12472f29c067df860df799b;triage_receipt=/tmp/pa1-triage.json;receipt_sha256=71d236166f2a1de8cecd6eb93042ef5199a09e2c49318dbf8eb9d5b5582a99e5
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式: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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | 3d3474a731f9ef3ee1f41935e63bca3afb70db56 | 3d3474a731f9ef3ee1f41935e63bca3afb70db56 | 2026-10-22T11:15:53Z |
|
||||
|
||||
## 证据(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-22T11:15:04.613824+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "3d3474a731f9ef3ee1f41935e63bca3afb70db56",
|
||||
"after_head": "3d3474a731f9ef3ee1f41935e63bca3afb70db56",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "3d3474a731f9ef3ee1f41935e63bca3afb70db56",
|
||||
"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=3d3474a731",
|
||||
"[remote] origin/main=3d3474a731 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=6",
|
||||
"[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] 3d3474a731(完整:3d3474a731f9ef3ee1f41935e63bca3afb70db56)",
|
||||
"[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": "87e58b98732700984715adb3f7a46aeacdc3749aacf51643f68a2ad9ede2451d",
|
||||
"receipt_sha256": "72f14e2080b227c1fc4094a0a4f573b0dc12fb08ed30c86400265a610fb09b0d",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/72f14e2080b227c1fc4094a0a4f573b0dc12fb08ed30c86400265a610fb09b0d.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | c92443280ef8e9ed29bf5bf5250df650c01b4478 | c22b9fb34a3702191a9e5f6007f3538520db694b | 2026-10-22T13:10:38Z |
|
||||
|
||||
## 闭合记录(card-autoclose 回填)
|
||||
|
||||
- 依据:merge cb36b59accd0418c5ca952912d7c181b83038a4c(PR #702)已包含于 origin/main,分支成果已落盘;本段为合入后回填。
|
||||
- 合并主题:Merge pull request 'Use dynamic ports across the five test files that shared hardcoded bindings' (#702) from fix/svc-test-port-governance-batch1 into main
|
||||
- 归档:随闭合移入 .task/archive/;任务执行期的过程证据见原任务记录与 PR。
|
||||
@@ -0,0 +1,99 @@
|
||||
# fix/svc-test-port-governance-deploy-txn 任务卡
|
||||
|
||||
任务主记录:PR #705 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/705(merge f673c2a15a);状态:已完成(回填);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= cb36b59accd0418c5ca952912d7c181b83038a4c
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-svc-test-port-governance-deploy-txn(worktree);分支:fix/svc-test-port-governance-deploy-txn
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=37c57479990e37eb048ba81763fe402e1e679a822679f315a4be18e4b965c333;triage_receipt=/tmp/plast-triage.json;receipt_sha256=d403df8b2564bf4e1fe16fb12ecc48cf31c25be4f7472e6b27bce6da3ecd93e0
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式: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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | cb36b59accd0418c5ca952912d7c181b83038a4c | cb36b59accd0418c5ca952912d7c181b83038a4c | 2026-10-22T13:39:45Z |
|
||||
|
||||
## 证据(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-22T13:28:41.022157+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "cb36b59accd0418c5ca952912d7c181b83038a4c",
|
||||
"after_head": "cb36b59accd0418c5ca952912d7c181b83038a4c",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "cb36b59accd0418c5ca952912d7c181b83038a4c",
|
||||
"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=cb36b59acc",
|
||||
"[remote] origin/main=cb36b59acc ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=9",
|
||||
"[recycle] merged un-recycled branches: fix/gitea-address-migration fix/svc-test-port-governance-batch1",
|
||||
"[cards] 已合入未闭合任务卡:fix-svc-test-port-governance-batch1.md——make task-close 回填归档(主检出运行)",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] cb36b59acc(完整:cb36b59accd0418c5ca952912d7c181b83038a4c)",
|
||||
"[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": "9f6260af81b0761dbb20afc5d145a9cb310303d426f655840a50f0879d81a49e",
|
||||
"receipt_sha256": "eeb4f9ac9bdc0e30a0a7bbaa49176626bb8aca54a20f152b1771e1df8fd855d3",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/eeb4f9ac9bdc0e30a0a7bbaa49176626bb8aca54a20f152b1771e1df8fd855d3.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(card-autoclose 回填)
|
||||
|
||||
- 依据:merge f673c2a15a50bef2c55f2c79a17d7601f82c8a07(PR #705)已包含于 origin/main,分支成果已落盘;本段为合入后回填。
|
||||
- 合并主题:Merge pull request 'Use a dynamic port in the last hardcoded test binding' (#705) from fix/svc-test-port-governance-deploy-txn into main
|
||||
- 归档:随闭合移入 .task/archive/;任务执行期的过程证据见原任务记录与 PR。
|
||||
@@ -0,0 +1,99 @@
|
||||
# fix/svc-test-port-governance-final 任务卡
|
||||
|
||||
任务主记录:PR #698 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/698;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 955a32de2bb49171348bad5f566cce935df19212
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-svc-test-port-governance-final(worktree);分支:fix/svc-test-port-governance-final
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=04cbe9df900a03163aeadb42fd22e5a86ddeced179901b50ac0f4642ce7f6881;triage_receipt=/tmp/pg3-triage.json;receipt_sha256=aca390790027841efaefe2082123780b1ec8d775f9ca0be80262a80f7121bde9
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
<预期结果、受影响入口/模块/契约;明确不做什么。>
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 955a32de2bb49171348bad5f566cce935df19212 | 955a32de2bb49171348bad5f566cce935df19212 | 2026-10-22T08:55:33Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:NOT_RUN(审阅智能体/平台身份、base/head、结论、问题闭合及重放依据)
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-22T08:54:47.013779+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"after_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "955a32de2bb49171348bad5f566cce935df19212",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev'",
|
||||
"[repo] branch=main head=955a32de2b",
|
||||
"[remote] origin/main=955a32de2b ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[recycle] merged un-recycled branches: feature/container-storage-plan",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 955a32de2b(完整:955a32de2bb49171348bad5f566cce935df19212)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev'"
|
||||
],
|
||||
"output_sha256": "b63c0d5c11be0292024cc972eee78fbcef7349381ee3bdecb432815b903373e6",
|
||||
"receipt_sha256": "3ee914c56ff0e9cf2a4d11db1ec0a2e6c74cf745cceb7eff1cefd2f9d938ee89",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/3ee914c56ff0e9cf2a4d11db1ec0a2e6c74cf745cceb7eff1cefd2f9d938ee89.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #698(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/698)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | 98ee59c477785b2d5094fcbea3e67a4dc864c388 | a29562ef8315c6cbe71bf531720ce5ffa5ca2717 | 2026-10-22T09:35:11Z |
|
||||
@@ -1,179 +0,0 @@
|
||||
# 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。
|
||||
|
||||
**外部前置阻塞(2026-10-22 登记影响 M1 合入门禁)**:Backend 仓代码编译依赖旧一代 deps API(agent-command/agent-intent/agent-native/agent-squad)。pin 一致性门禁(test-pins)要求 Dev/backend 嵌套 pin 与 Dev 顶层一致(最新 main),但把 backend 的这四个依赖 pin 推到最新 main 会让 Backend vet/build 失败(SideEffects/ProcessEvent 等符号缺失,已在 gitea-endpoint-migration 任务实测)。同时 Gitea 服务器迁移后旧端点已死,Dev CI checkout allowlist 只接受新端点 URL,因此 pin 又必须前移。**解除条件:先完成 Backend 依赖 API 升级集成任务**——该任务已于 2026-10-23 完成并合入 Backend main(Backend PR #248:四依赖前移 + API 适配 + 行为修复,Review r1 FAIL→修复→r2 PASS,CI 绿)。**剩余阻塞缩小为 Agent-Command 双线语义冲突**:command main(新 receipt API,Backend #248 已适配)vs Dev main 现 pin 的 fe7987dc governance line(trusted-unknown-receipts 旧式 API,被其他在途工作依赖);两线合并会使 Backend intentgate 回归。需 owner 决策 command 两线归一方向后 re-pin。在此之前 U1(PR #710)与 #697 的 CI 无法通过,M1 合入门禁暂停;U2—U6 的代码实施可继续在分支上推进,不受影响。
|
||||
|
||||
## 子任务登记
|
||||
|
||||
| 子任务分支 | 必需 | 状态 | PR |
|
||||
|---|---|---|---|
|
||||
| feature/container-storage-plan-u1 | 是 | 已完成待合入(PR #710,CI 受外部前置阻塞) | #710 |
|
||||
| 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,75 @@
|
||||
# 独立代码 Review:feature/intent-multi-turn-a1(A1 多轮意图闭环)
|
||||
|
||||
- Reviewer:独立 CLI 会话(pi,未参与本分支实施)
|
||||
- 日期:2026-10-22(UTC)
|
||||
- 对象:`git diff 955a32de...9126b09b`(Dev 超级仓);组件焦点 `modules/backend a75783fd..9ee752c3`
|
||||
- 任务卡:`.task/feature-intent-multi-turn-a1.md`(设计决定:run-cancel 为第一业务动作、30min TTL、revision、cmdbus 确认不变)
|
||||
- 性质:findings-only,未改任何代码;只新增本报告文件。
|
||||
|
||||
## 1. 范围与方法
|
||||
|
||||
- 超级仓 diff:`policy/architecture-baseline.json` +3 边、`modules/backend` gitlink a75783fd→9ee752c3、任务卡新增。
|
||||
- backend 组件 diff(3 commits,+3267/−21):新包 `internal/intentsession`、`internal/store/intent_session.go`(表 v52)、`internal/route`(Cancel 裁决)、`internal/server`(`intent_session.go`、`intent_cancel.go`、`server.go` chat 接线)、`cmd/storage-migrate`(copyOrder 补 intent_session)。
|
||||
- 方法:全量逐文件静态精读 + 调用链追踪(chat postMessage → fuseIntentSession → route.Decide → admitIntentCommand → Bus.Dispatch / handleIntentCancel → dispatchCancel;runmgr HITL 转轨判定的 route 契约),测试断言逐条对照审查镜头。
|
||||
|
||||
### 测试执行(诚实出口:NOT_RUN)
|
||||
|
||||
- 本会话**未能复跑**聚焦测试(`go test ./internal/intentsession/... ./internal/route/... ./internal/server/...`):当前工作区组件 checkout 只部分物化——`modules/backend/deps/agent-system-core、error-code-system、logging-system、agent-document-renderer` 为空目录,go.mod replace 闭包无法解析;无网络;本机模块缓存仅有 2026-10-13 伪版本(≠本次 pin 的提交),用它们编译出的结果不能作为本 PR 的证据,故弃用。
|
||||
- 尝试过程可复核:为诊断曾临时在 `modules/backend/deps/` 建指向超级仓 `deps/` 的符号链接并随即删除(超级仓 deps 亦缺上述四个组件,`go build` 报缺 go.mod 为证);现场已恢复,`git status --porcelain` 干净(见 §5)。
|
||||
- 任务卡记录的 verify-a1b/a1c exit=0、批二 CI 全绿:本会话无法重放,按 `[未经证实]` 处理;下述结论全部来自静态审查。按任务单要求未跑 `make test`。
|
||||
|
||||
## 2. 按镜头的结论(spec / security / regressions / tests)
|
||||
|
||||
**spec(多轮闭环)**:缺槽动作 → 澄清(`suspendIntentSession`,intent_session.go:273-310)→ 回答折叠(`fuseIntentSession` → `ApplyAnswer`,slot 齐则合成与完整识别同形的 MatchResult 走同一准入/派发路径,server.go:1252-1258 + intent_session.go:110-155)→ 完成;TTL 30min、写时续期、读侧惰性删除(intentsession.go:12-13、344-357);revision 单调、`ErrStale` 拒绝陈旧回答(intentsession.go:206-212、359-364);双用户/项目/会话键隔离(store 全查询带 owner 过滤)。主链路成立,测试覆盖到每一环。**例外见 B1/M1**。
|
||||
|
||||
**security**:会话不携带授权——补齐后仍走 `admitIntentCommand` + `Bus.Dispatch`,高危确认卡由 cmdbus 收口(TestIntentSessionStillRequiresHighRiskConfirmation 断言确认前处理器不执行);取消经 `run.cancel` 注册命令走同款 Dispatch,卡确认前不记「已取消」(intent_cancel.go:150-186,TestIntentCancelSurfacesHighDangerConfirmCard 覆盖确认→执行全链);跨用户运行 `GetRun(owner,…)` 不可见即澄清、不泄露存在性(intent_cancel.go:73-79,TestIntentCancelNeverResolvesAnotherOwnerRun);候选只列本人本会话未终态运行(intent_cancel.go:97-112)。**例外见 M2**。
|
||||
|
||||
**regressions**:无挂起预期时 fuse 段空转,`TestIntentSessionLegacyResponsesUnchanged` 做逐字节对照;route 契约——cancel 移出 unconsumedKinds 但 `StartFound` 刻意不置位(route.go:181-188),runmgr HITL 转轨判定(runmgr.go:2331-2334 只看 StartFound)行为与 main 一致;U6 准入——fused 结果同样过 `dispatchCommand`(res.Complete())与 `admitIntentCommand`(声明 schema 映射、必填不满足即 Blocked)两道闸,非法/缺失槽位不可能执行。**例外见 M1(累计槽位跨任务泄漏)。**
|
||||
|
||||
**tests**:覆盖面好——触发条件(有/无挂起、同意图/异意图、过期、隔离)、revision、高危确认、legacy 逐字节、显式/会话/歧义靶子、确认卡。缺口:无「同会话内换任务(异意图)再挂起」用例(正是 B1 场景);无真实并发写测试(revision 逻辑覆盖属逻辑等价,可接受);`answerFromRecognition` 显式落槽不看意图归属(intent_session.go:203)无专项用例。
|
||||
|
||||
## 3. Findings
|
||||
|
||||
### Blocker
|
||||
|
||||
**B1(spec/正确性)换任务再挂起时,上一个动作的累计槽位被继承,可把陈旧值派发给新动作。**
|
||||
`Service.Suspend` 无条件合并旧行槽位:`slots = mergeSlots(cur.Slots, slots)`(internal/intentsession/intentsession.go:193-194)。宿主侧 `suspendIntentSession` 对「route 层槽位未就绪」的新动作一律走 Suspend(internal/server/intent_session.go:273-310),不区分新旧预期是否同一动作。可达序列(同会话、TTL 内):
|
||||
1. T1 意图 A(如 `demo.run-cancel`)缺 reason → 挂起,累计 `{run_id: R-7}`;
|
||||
2. T2 用户改做意图 B(如 `demo.purge`,缺 scope)→ fuse 判「异意图、无落槽」不碰会话(intent_session.go:124-127)→ route 澄清 → `Suspend(B)` 继承 `{run_id: R-7}`;
|
||||
3. T3 用户补 scope → `ApplyAnswer` 合并全量槽位判齐(intentsession.go:227)→ `completedRecognition` 以累计槽位派发 B(intent_session.go:245-262);
|
||||
4. `mapSchemaParams` 只丢弃**未声明**键,B 声明过的 `run_id` 会带陈旧值 R-7 进 params(internal/server/intent_admission.go:69-84)——若 run_id 是 B 的必填,准入反而被陈旧值满足。
|
||||
这与包自己的不变量直接冲突(「绝不允许旧槽位漏进新任务」intentsession.go:25;「别的任务的输入绝不混进当前动作」intent_session.go:53、188)——该保护只做在了 fuse 的回答投影上(answerFromRecognition 异意图不并入,intent_session.go:180-208),Suspend 覆写预期时漏了。建议:Suspend 时若 `cur.Expectation` 存在且 `IntentID/Action` 与新预期不同,则丢弃旧累计槽位(或显式要求宿主先 Drop);并补「异意图换任务」回归用例。涉同一镜头的次生风险见 minor m4。
|
||||
|
||||
### Major
|
||||
|
||||
**M2(security/错误路径)`applyCancel` 吞掉存储错误后,可能把「放弃补槽」滑成「立即取消一个真实运行」。**
|
||||
同意图取消的设计语义是只清挂起预期、不派发(intent_session.go:20-24)。但 `applyCancel` 对非 ErrStale 的存储错误同样返回 nil 且错误被丢弃(internal/server/intent_session.go:165-170),`fuseIntentSession` 整体返回无会话语义 → postMessage 继续走 `route.KindCancel` 规划(server.go:1273-1279)。此时挂起行的 `run_id` 槽位经 `sessionRunTarget`→`Binding` 仍解出靶子(intent_session.go:313-327),`planIntentCancel` 对发起人可见即放行;若部署未注册 `run.cancel`,走 `dispatchCancel` 本地路径**立即 `CancelRun`、无任何确认**(intent_cancel.go:164-176)。即:一次瞬时存储故障把「算了不取消」变成「未确认取消 R-7」。建议:applyCancel 对 `err != nil`(非 ErrStale)向上传播错误(writeStoreErr),fail-closed。
|
||||
|
||||
### Minor
|
||||
|
||||
- **m1(文档失实)** `internal/store/intent_session.go:11-12` 声称该表「不进 storage-migrate 的 copyOrder(同 admission_leases)」,但 `cmd/storage-migrate/main.go:311-313` 已把 `intent_session` 加入 copyOrder(且是清单夹具测试 `main_test.go:218` 的强制要求,9ee752c 补得对)。代码正确、注释过期,需同步删改;旧源库缺表已有 `sqliteHasTable` 跳过(main.go:400-402),无行为问题。
|
||||
- **m2(回话如实性)** 总线路径派发被拒(StateRejected)时 `cancel.accepted=false` 但 `cancel.reason` 为空(internal/server/intent_cancel.go:142),与本地路径「失败带原因」不对称;receipt 虽随响应透出,建议把拒绝原因写入 `result.Reason`。
|
||||
- **m3(过期边界 fail-open)** `expires_at` 解析失败得零值时 `live()` 视为「永不过期」(intentsession.go:366-373 + store `parseTime` 容错返回零值)。当前写入方恒写 TTL,风险仅来自自身写坏的数据,但与全文件 fail-closed 纪律不一致:解析失败宜按已过期处理。
|
||||
- **m4(与 B1 联动)** 显式 `set_slot/change_slot` 命令「不看意图归属」直接写入当前挂起预期(intent_session.go:196-203,注释称有意)。B1 修复(换任务即弃旧槽位)落地后,此路径仍可把异动作的落槽命令并入当前预期;建议改为「命令名 ∈ 当前预期 Missing/声明键才采纳」,或补用例锁定现状语义。
|
||||
|
||||
### Notes
|
||||
|
||||
- **n1** 「放弃补槽」轮仍按 KindTurn 起普通回合(server.go:1273-1279 不改 routeKind;1340-1345 仅 KindCancel 拦截),响应同时带 `intent_session` note 与 run——与 main 上 cancel 落 turn 的旧行为一致,测试未固定该组合,建议在任务卡确认一次。
|
||||
- **n2** `completedRecognition` 不搬运 `Risk/RequiresConfirm`(intent_session.go:245-262):全仓 grep 确认 `Decision.RequiresConfirm` 目前无消费者(route.go:114-116 仅透传),确认权在 cmdbus danger,今日无行为影响;将来若有人消费该字段,fused 路径会静默不同——建议留注释或补字段。
|
||||
- **n3** `PutIntentSession` upsert 后读回非原子(store/intent_session.go:74-93),并发双写可能都读到同一新 revision;当前会话请求路径单写者,无实害,建议加注释固化「每会话单写者」前提。
|
||||
- **n4** 未注册 `run.cancel` 的本地回落取消无确认卡——与既有 `POST /api/runs/{id}/cancel` 逐字同款、属设计决定(任务卡决定 1/4 与 intent_cancel.go:15-17 注释一致),非本 PR 引入;注册了命令的部署确认链有测试护栏。记录在案以回应「确认是否被绕过」镜头:**会话路径在命令注册时不绕过确认**。
|
||||
- **n5** 架构基线 3 条新边与实际 import 逐一核对相符(intentsession→intentgate/store;server→intentsession);基线治理测试本会话未复跑(同 §1 NOT_RUN 原因)。
|
||||
- **n6** 任务卡记载的 svc governance 偶发失败为已知预存问题、按任务单不属本次范围,未复跑 `make test`。
|
||||
- **n7** 测试缺口清单:异意图换任务再挂起(B1 回归用例)、applyCancel 存储错误注入、显式落槽跨意图(m4)。
|
||||
|
||||
## 4. 结论
|
||||
|
||||
主链路(挂起→澄清→补齐→同路派发)、过期隔离、revision、双用户隔离、取消三态(显式/会话绑定/歧义→如实澄清)与 cmdbus 确认收口均实现且有测试护栏,route/runmgr 契约保持。但以下问题应先修再合:
|
||||
|
||||
- Blocker B1:跨动作挂起继承旧累计槽位,陈旧值可进新动作派发参数(intentsession.go:193-194;admission 只滤未声明键)。
|
||||
- Major M2:applyCancel 吞存储错误 fail-open,可滑向无确认的本地取消(intent_session.go:165-170 → server.go:1273 → intent_cancel.go:164-176)。
|
||||
- Minor m1–m4 建议随修复一并处理(m1 文档、m2 回话、m3 过期 fail-closed、m4 落槽归属)。
|
||||
|
||||
最终判定(理由如上三条):
|
||||
|
||||
REVIEW VERDICT: BLOCK
|
||||
@@ -0,0 +1,44 @@
|
||||
# Independent Code Review — feature/exec-recovery-diagnostics-a2 (A2 run diagnostics)
|
||||
|
||||
- Reviewer: independent CLI review session (`a2-independent-review` unit; fresh session, not the implementer)
|
||||
- Date: 2026-10-22
|
||||
- Review object: this checkout @ `d0b2bde9` (branch `feature/exec-recovery-diagnostics-a2`) versus authoritative main. No `origin/*` refs exist in this sandbox; task card R0 = `c9244328` (= `d0b2bde9~5`, the merged main tip `c92443280ef8e9ed29bf5bf5250df650c01b4478`), so `git diff c9244328...d0b2bde9` ≡ `git diff origin/main...HEAD`.
|
||||
- Dev-level scope: task card `.task/feature-exec-recovery-diagnostics-a2.md` (new), `modules/backend` gitlink `9ee752c3` → `06b0c9f1`, `policy/architecture-baseline.json` +3 edges. Verified: dev diff is exactly these 3 files; the three edges (`backend/rundiag backend/cmdbus`, `backend/rundiag backend/store`, `backend/server backend/rundiag`) sit in correct sorted positions and match the actual import graph (see N5).
|
||||
- Backend scope: `git -C modules/backend diff 9ee752c..06b0c9f` — 7 files, +1418/−0: `internal/rundiag/rundiag.go` (488), `internal/rundiag/rundiag_test.go` (440), `internal/server/run_diagnostics.go` (32), `internal/server/run_diagnostics_test.go` (226), `internal/server/server.go` (+2), `internal/store/run_diagnostics.go` (110), `internal/store/run_diagnostics_test.go` (120).
|
||||
- Method: read-only review of the full diff plus the neighboring run-read/cancel routes, the pre-existing store methods the aggregator reuses, and the cmdbus pending-confirm write path. No code changed; the only write is this report.
|
||||
|
||||
## Findings
|
||||
|
||||
### Blockers
|
||||
|
||||
None.
|
||||
|
||||
### Major
|
||||
|
||||
None.
|
||||
|
||||
### Minor
|
||||
|
||||
- **MI1 — Evidence-driven wait states outrank terminal run statuses; a completed/failed run can be reported as `waiting_hitl`.** In the responsibility switch the HITL arm `case ev.Run.Status == core.RunWaitingHITL || card != nil || decision != nil` (`modules/backend/internal/rundiag/rundiag.go:252`) is evaluated before `failed` (:281) and `completed` (:296). A run that ended while a decision card scoped only by owner+conversation (human_decisions has no run column; attributed by the time-window inference at :432-448) remained `pending`, or with a leftover unresolved run-scoped HITL card, yields `state=waiting_hitl`, `role=human`, `next_step.action=confirm_card` for a terminal run. This is exactly the fixed precedence the task card approved and the documented bounded attribution (`rundiag.go:15-19`), and `run.status` stays visible in the report — but a follow-up that suppresses evidence-driven wait mapping (or adds a note) when the run is terminal would remove the confusing "confirm card" advice on finished runs. Not blocking: matches the approved design; purely advisory output.
|
||||
- **MI2 — Silent truncation of side-effect evidence contradicts the package's own honesty contract.** `sideEffects` stops appending at `maxSideEffects = 50` with no note (`rundiag.go:342-366`, cap at :57), and `Gather` fetches only the 200 newest audit envelopes (`maxAuditScan`, `rundiag.go:175` → `ListAudits` orders `time DESC`, `internal/store/audit.go:147-152`), so a busy conversation can under-report a run's operations without any indication. Every other evidence gap in the package adds a note (even "absence of records is not proof of no effect", :313-315); a "side effects truncated" note would complete that contract. Diagnostic-only impact; recorded evidence is still correct as far as it goes.
|
||||
- **MI3 — Precedence is tested only at the top of the chain.** Of the ordered arms at `rundiag.go:224-310`, only "unreleased recovery hold wins over everything" (`rundiag_test.go:81-89`) exercises interleaved evidence; combinations such as released-hold+interrupted+open-HITL-card (→ `reconciling`) or decision+pending-confirm (→ `waiting_hitl`) are untested and rest on the switch order alone. Low risk because the order is literal and table-covered per state; suggesting coverage only because precedence is the stated spec center of this unit.
|
||||
|
||||
### Notes
|
||||
|
||||
- **N1 — Per-state derivation and precedence are complete and match the task-card contract.** All eight states are derivable: `unknown` fail-closed on unreadable hold (:226-233), `recovery_hold` on an open hold row (:234-240), `reconciling` for released-hold+`RunInterrupted` residue (:241-251, predicate at :402-403), `waiting_hitl` from status/run-scoped card/time-windowed decision (:252-273), `confirmation_pending` from an owner-scoped pending confirm parsed out of the dispatch payload (:274-280, :453-471), `failed` with retry-safety gated on unknown receipts (:281-295), `completed` (:296-298), `running` for running+pending (:299-303), `unknown` default for unmapped statuses (cancelled; interrupted without hold; :304-310). Precedence order is exactly `recovery_hold > reconciling > waiting_hitl > confirmation_pending > failed > completed > running > unknown`, with the fail-closed `HoldUnknown` degradation deliberately first.
|
||||
- **N2 — Security: scoping is byte-for-byte the neighboring run-read pattern; no cross-owner leakage path exists.** The handler resolves the run via `s.st.GetRun(s.owner(r), r.PathValue("id"))` (`internal/server/run_diagnostics.go:25`) — identical to `getRun` (`server.go:1592`) — where `owner` comes only from the auth context (`auth.go:220-225`), `GetRun` filters `WHERE id=? AND owner=?` with cross-tenant = `ErrNotFound` (`messages.go:307-316`), and `writeStoreErr` maps it to 404 indistinguishable from a missing run (`server.go:2617-2620`). Every follow-on query is keyed on the already-authorized run's own fields, never on request parameters: `GetRecoveryHold(run.ID)` (`recovery_holds.go:36`), `ActiveHITLCardsByRun(run.ID)` (`recovery.go:156`), `PendingHumanDecisionsByConversation(run.Owner, run.ConversationID)` with empty-owner defense (`store/run_diagnostics.go:51-53`), `ListOwnerPendingConfirms(run.Owner)` (:88-90), `LastRunCheckpoint(run.ConversationID, run.ID)` (:28-30), `ListAudits(run.Owner, run.ConversationID)` (`audit.go:134-140`). The `pending_confirms` payload is parsed only to extract command/owner/conversation identity; the raw payload is never echoed (`rundiag.go:453-471`).
|
||||
- **N3 — Read-only holds end to end.** All six store entry points the aggregator uses are SELECT-only (verified by reading each); `Build` is a pure function; the handler path contains no store write and the mux registers only the GET pattern (`server.go:396`). The author's `TestRunDiagnosticsIsReadOnly` additionally asserts the run row and hold are unchanged after a GET and that POST/PUT/DELETE/PATCH are not served (`run_diagnostics_test.go:153-193`).
|
||||
- **N4 — No route-registration conflicts.** `GET /api/runs/{id}/diagnostics` differs from `GET /api/runs/{id}` (:394) and `POST /api/runs/{id}/cancel` (:393) by a literal extra segment / method; ServeMux precedence is specificity-based, single registration, no duplicate pattern anywhere in the diff.
|
||||
- **N5 — Architecture-baseline edges are correct and validated.** `internal/rundiag` imports exactly `cmdbus` and `store` within the backend module (`rundiag.go:21-24`); the `agent-command` import is intentionally untracked — the baseline records only intra-repo package edges and has no deps-module edges anywhere (same precedent as `backend/cmdbus`, which also imports agent-command without an edge). `backend/server backend/rundiag` matches the handler import. Sorted positions are correct (`route < rundiag`; `ratelimit < rundiag < runmgr`), and `scripts/lint-architecture.go` compares this file against the collected import graph, so the edges are machine-checked in CI.
|
||||
- **N6 — Go tests NOT_RUN in this review workspace (environment, not code).** The backend's nested dependency checkouts (`modules/backend/deps/agent-command`, `deps/agent-system-core`, …) are empty gitlinks here and no `.git/modules` gitdirs exist; `git submodule init/update` is forbidden for this unit and network is off, so `go build ./internal/rundiag/ …` fails at module setup (`agent-command@v0.0.0 (replaced by ./deps/agent-command): reading deps/agent-command/go.mod: no such file or directory`), before any code compiles. Fallbacks applied: every changed file was fully line-read; all `core.Run*`/phase/event symbols referenced are used by pre-existing compiling backend code; `cmdbus.Request` carries `ID/Owner/ConversationID` (`cmdbus.go:163-177`) and the pending-confirm payload is the marshaled dispatch request (`cmdbus.go:522-523`), so `waitingConfirm`'s parsing is sound; the `events` table has `run_id`/`seq`/`ts`/`type` in both dialects (`migrate.go:47,71`) and the checkpoint query is served by `idx_events_conv(conversation_id, seq)`. Author-recorded receipts were reviewed and are internally consistent with the task card: `/tmp/agent-stack-b2/verify-a2b.json` (`go build ./... && go test -count=1 ./internal/server/... ./internal/rundiag/... ./internal/store/... && go vet …`, exit=0, `ok agent-system-backend/internal/{server,rundiag,store}`) and `/tmp/agent-stack-b2/verify-a2-combo2.json` (`make test`, exit=0) — author-claimed (SELF), not independently re-executed in this session.
|
||||
- **N7 — Attribution boundaries are documented and bounded; one sibling-run overlap remains.** Audit envelopes carry no runId today (host bus doesn't write it), so `belongsToRun` (`rundiag.go:371-383`) attributes by conversation + "not issued before run start". Concurrent sibling runs (subagents) in the same conversation have overlapping windows, so until the bus persists `runId`, an envelope from a sibling can appear in both runs' `side_effects`. The limitation is stated in the package doc (:17-19) and the report surfaces `operation_id`/`command_id` so a human can disambiguate; when the bus starts writing `env.RunID` the strict branch (:372-373) takes over automatically. Acceptable for a diagnostic surface.
|
||||
- **N8 — "No records ≠ no effect" honesty contract is consistently enforced** (unrecorded checkpoints → null + note at :311-312, tested at `rundiag_test.go:280-302`; empty side effects → note :313-315; unreadable hold → fail-closed unknown that can never yield `safe_retry` :226-233, tested :304-339). Only the truncation gap of MI2 falls short of it.
|
||||
|
||||
## Lens summaries
|
||||
|
||||
1. **Spec** — pass. State derivation and precedence are complete and per-state correct (N1); the JSON contract (snake_case, `last_checkpoint` explicit null, `side_effects` always an array, `next_step` never reason-less) is pinned by `TestReportJSONContract` (`rundiag_test.go:341-389`). Dev-level artifacts (task card, 3 baseline edges, gitlink `06b0c9f`) are exactly as claimed.
|
||||
2. **Security** — pass. Auth-context-only owner, tenant-filtered run fetch, 404 on cross-owner/missing (tested `run_diagnostics_test.go:62-97`), all downstream reads anchored on the authorized run, strictly read-only (N2/N3), no payload echo, empty-owner fail-closed store helpers.
|
||||
3. **Regressions** — pass. The diff is additive: 7 new files plus a 2-line route registration; no execution/recovery/runmgr path touched, no existing signature changed, no route conflicts (N4), no migration/schema/index change.
|
||||
4. **Tests** — pass with minor gaps. Per-state table of 12 cases covering all eight states plus degradation (`rundiag_test.go:78-225`), scoping at both server and store layers including cross-owner/cross-conversation/empty-owner (`run_diagnostics_test.go:62-97`, `store/run_diagnostics_test.go:13-120`), unknown-degradation at three levels (unreadable source, unmapped status, fail-closed hold; `run_diagnostics_test.go:195-226`), read-only and write-method rejection (`:153-193`), pre-run evidence exclusion (`rundiag_test.go:391-406`). Gaps: MI3 (chain precedence below the top arm), MI2 (truncation without note). Focused tests could not be re-run here (N6); author receipts for `go test ./internal/rundiag/... ./internal/server/... ./internal/store/...` and full `make test` are on record and uncontradicted.
|
||||
|
||||
REVIEW VERDICT: APPROVE
|
||||
@@ -0,0 +1,48 @@
|
||||
# 独立代码 Review:feature/catalog-recall-eval-a3(A3 召回评测集与指标基线)
|
||||
|
||||
- Reviewer:独立 CLI session(未参与本单元实施;本报告为唯一产出变更)
|
||||
- 日期:2026-10-22
|
||||
- Review 对象:`git diff 6ced938a...HEAD`(6ced938a = R0 / origin-main 合入点,与任务卡 R0 一致)
|
||||
- 组件重点:`deps/agent-intent` 765a7e7..6aa4925(单提交 "Add the sanitized recall evaluation set and metrics baseline",9 文件 +1354/-18);Dev 层 4 个 gitlink 前移 + 任务卡
|
||||
- 方法:全量 diff 阅读被测文件;对语料/指标做独立的脚本化交叉复核(只读);静态追踪指标口径、生成回放路径与 JSON 兼容性;检查嵌套 pin 级联与越界改动。聚焦测试见下文 NOT_RUN 说明。
|
||||
|
||||
## 复核结论摘要(独立重算,非转抄)
|
||||
|
||||
- 语料 `intent/testdata/recall_corpus.json`:**37 例 / 46 轮**,五类齐全且恰好五类(expired_confirmation 7/14、generation_consistency 8/8、multi_intent 7/7、negation_reference 8/10、wrong_slot 7/7),`record_only: true`;`expect_top` 轮次 31、生成用例 8 —— 与 BASELINE.md 表头逐项一致。
|
||||
- 语料 digest `sha256:53d26cfc…bc2885` 与 `evaluation/metrics.json` 的 `corpus_digest`、BASELINE.md 摘要三方一致(本机 `sha256sum` 独立重算)。
|
||||
- `evaluation/metrics.json` 全部 10 组计数与比率逐项等于 BASELINE.md 表;比率均可用 `round(n/d*10000)/10000` 独立复算(如 30/31→0.9677、13/46→0.2826、6/46→0.1304、5/14→0.3571、5/8→0.625、5/7→0.7143、3/7→0.4286)。
|
||||
- 总体口径自洽:top1_expected 3+3+7+11+7=31、hits 30(唯一 miss 即 multi_greet_then_report 的 1 次误执行)、mis_exec 5+1=6、clarify 0+0+5+5+3=13、fallback 2+0+0+3+0=5+1(gen)=6、pass 3+6+7+7+8=27。
|
||||
- 嵌套 pin 级联一致:native@d1f72aa→intent@6aa4925;squad@a5afe9c→intent@6aa4925+native@d1f72aa;backend@8dc91c1→intent@6aa4925+native@d1f72aa+squad@a5afe9c(各自 `git ls-tree` 核对);三个组件 bump 均为 pins-only(`git diff --stat` 仅 gitlink),superrepo 层 diff 仅 4 gitlink + 任务卡,无评测范围外源码改动。
|
||||
|
||||
## Findings
|
||||
|
||||
### Blocker
|
||||
|
||||
无。
|
||||
|
||||
### Major
|
||||
|
||||
无。
|
||||
|
||||
### Minor
|
||||
|
||||
1. **基线数值漂移不设防(结构性盲区,刻意取舍但建议记录)** — `intent/recall_eval_test.go:119-162`(`checkSummaryJSON`/`checkMetricsBounds`):结构测试对已提交基线只校验 schema、digest、类别计数与「内部一致」(rate==ratio(n,d)),不把基线里的 hits/rates 与本次新鲜运行比对。若识别器行为改变而语料不变(digest 不变、计数不变),过期的基线数值不会被任何测试发现;BASELINE.md「重跑」一节只靠流程约束。这与「只记录不设阈值」的定位一致(比对数值即变相质量断言),属可辩护的设计取舍,但建议在 BASELINE.md 明示「引擎变更不会触发基线失效告警,须人工重跑」。
|
||||
2. **missing-slots 断言用 `fmt.Sprint` 比较切片** — `intent/corpus.go:194-222`(`matchTurn` 中 `fmt.Sprint(got) != fmt.Sprint(turn.ExpectMissingSlots)`):`[]string(nil)` 与 `[]string{}` 都打印 `[]`,且比较对元素顺序敏感。当前引擎输出顺序确定、语料只断言单元素缺失(`["run_id"]`/`["topic"]`),不构成现实缺陷;但后续一旦断言多缺失槽位,顺序耦合会变成脆弱测试。建议改为逐元素显式比较。
|
||||
3. **任务卡内部记录不一致(文档级)** — `.task/feature-catalog-recall-eval-a3.md`:「Agent 实施收据」表全部为 NOT_RUN/待定义 占位,而「合入与清理」节已记载 `a3-recall-eval-dataset(…intent 6aa4925,verify-a3 exit=0)`、嵌套级联与 `组合验证:全量 make test PASS`;且「证据(Evidence)」表为空,组合验证结论以带外文件名(verify-a3-combo3.json 等)引用。按仓库门禁「命令输出只经 make evidence 写入任务卡」,合入前应把收据表与证据表补齐一致。不阻塞本次代码评审结论。
|
||||
|
||||
### Notes
|
||||
|
||||
1. **指标口径正确** — `intent/evalmetrics.go:129-141`(`misExecuted`):仅当实际 `matched`/`expectation_hit` 出 Top 且与期望动作不同时计数;期望 matched/expectation_hit 而未写 expect_top 的轮次不约束动作,不计;期望 fallback/ambiguous/guard_hit 却执行了即计。与 BASELINE.md「指标口径」一节逐条一致。分母:top1=写了 expect_top 的轮次(`evalmetrics.go:103-108`)、clarify/fallback=全部轮次(`evalmetrics.go:111-118,153-159`)、gen_agree=写了 generator_commands 的用例数(用例级,`evalmetrics.go:120-124`)。`ratio` 定点 4 位舍入保证 JSON 字节稳定(`evalmetrics.go:161-166`)。
|
||||
2. **汇总对账安全** — `intent/evalmetrics.go:61-92`(`SummarizeCorpus`):cases/results 按下标一一对应(越界即 break,契约已在注释声明),空 category 归入 `uncategorized`(旧语料兼容),类别名升序、overall 独立。`intent/eval_test.go:78-89` 在既有 `corpus.json`(无 category)上跑了一遍汇总,兼容路径有覆盖。
|
||||
3. **生成回放路径离线、确定性、fail-closed** — `intent/corpus.go:156-186`:`replayGenerator.Generate` 忽略输入与目录、只回放脚本命令(无模型/无网络),并深拷贝 slots 防串染;`compareGeneratorPath` 复用识别器兜底同款 `generateCommands`(`intent/recognizer.go:352-385`:start 的 ActionRef 必须从同一快照唯一解析、命令种类受限、槽位键集校验,任一不过即无 Top)。三个「故意不一致」用例路径成立:`run_id=43` 槽位分歧(sameSlots 判否)、`taskrun.restart` 错动作(handler 反查到 restart spec)、`ghost.flow` 幻觉动作(resolveActionRef 失败→校验拒绝→无 Top)。两边都无 Top 也算一致,`gen_chitchat_agree` 覆盖该分支。Nil expectation 传入 `NewRecognizer` 在该路径安全(`generateCommands` 不触达链式预期)。
|
||||
4. **eval 向后兼容** — `cmd/intentctl/main.go:254,260-262,365-390`:`-metrics-out` 缺省为空不写文件(唯一新增写文件入口,显式 opt-in;`-` 走 stdout);旧语料(无 `record_only`)失败语义不变,且 `TestEvalRecordOnlyWritesMetrics`(`cmd/intentctl/main_test.go:79-112`)同时断言 record_only 不失败+strict 语料仍失败。输出新增的指标表为追加行,不破坏既有两行指标输出。`evalCorpus` 结构仅增可选字段(`omitempty`),旧 JSON 解析不受影响。
|
||||
5. **结构测试无质量阈值伪装** — `intent/recall_eval_test.go:64-117`:只断言 ≥30 例、恰好五类、每例可执行(无「识别错误」)、generation_consistency 用例形态、JSON 合法且计数与语料一致、基线 digest 与语料一致;`checkMetricsBounds`(:164-186)只做数学界校验(n≤d、rate==ratio)。无任何质量数值断言,符合「基线只记录」边界。测试与 CLI 同源建快照(:36-62,语料自声明 builtin_specs/guard/protected + 同款 specs 目录),指标同源声明成立。
|
||||
6. **语料确为夹具派生、五类行为真实可辨** — 全部期望 top ∈ {demo.run.status_query/restart/cancel, demo.greet.hello, demo.misc.deploy, report.generate, guard.config_change},动作 id ∈ base.json 的 handler 集 + 刻意的幻觉 id;生成动作 id 与 `intent/testdata/specs/base.json` 的 handler/action 一一对应。各类用例确实 exercising 所标行为:语义否定(「不要/别/不用/先不要」无 excludes 声明,理想为 fallback)对照 `excludes_any` 声明否定(「配置」);multi_intent 覆盖 动作+动作→ambiguous、寒暄+动作→动作、动作+guard→guard_hit;wrong_slot 覆盖 缺失/中文数字/双候选/越字符集/仅符号/空主题;expired_confirmation 覆盖 链式命中、过期后裸回答回落、过期后完整重述走 matched、宿主声明 pending 再过期(`pending_expectation`+`expectation_expired` 组合,语义为「先声明后作废」,实现顺序 `intent/corpus.go:120-126` 与用例意图一致);wrong_slot 用单轮 `turns` 包装以使用本轮新增的 `expect_slots`/`expect_missing_slots` 能力。
|
||||
7. **安全边界** — 语料全合成(无真实用户数据/PII);`record_only: true` 无阈值断言;生成路径为确定性回放,无网络/模型调用;未发现任何对外部输入的写路径(除显式 `-metrics-out`)。
|
||||
8. **聚焦测试 NOT_RUN(环境受限,非变更缺陷)** — `cd deps/agent-intent && GOWORK=off go test ./intent/...` 无法编译:`agent-intent` 的嵌套 gitlink(deps/error-code-system@a3ee67b、logging-system@49b5d8b 等)在本工作区未物化,且目标提交不在任何本地对象库(`git cat-file` fatal)、无网络。按诚实出口如实标注 NOT_RUN;作为补偿,本文「复核结论摘要」的计数/digest/比率均为独立脚本重算,与基线三方对账一致。任务卡所记 `make test PASS(verify-a3-combo3.json exit=0)`本评审无法复现,标注 [未经证实](属 host 组合门禁范畴,不阻塞代码评审)。
|
||||
|
||||
## Verdict
|
||||
|
||||
未发现 blocker/major 级代码缺陷;三项 minor(基线漂移不设防的取舍说明、fmt.Sprint 切片比较的脆弱写法、任务卡收据/证据表未同步)均不阻塞合入,可在本次或后续增量处理。语料、指标、基线、CLI 兼容、结构守卫与嵌套 pin 级联均通过独立静态复核与数值对账。
|
||||
|
||||
REVIEW VERDICT: APPROVE
|
||||
Vendored
+1
-1
Submodule deps/agent-intent updated: 765a7e7177...6aa4925301
Vendored
+1
-1
Submodule deps/agent-native updated: f2fb9ffa6d...d1f72aa974
Vendored
+1
-1
Submodule deps/agent-squad updated: b6d87638cc...a5afe9c8cc
@@ -1,266 +0,0 @@
|
||||
---
|
||||
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 官方资料,并做真实冒烟;失败则阻止该镜像采用,不阻止本方案的源码设计工作。
|
||||
+1
-1
Submodule modules/backend updated: a75783fd2d...8dc91c1783
+1
-1
Submodule modules/frontend updated: 279b2d02a2...807c656886
@@ -36,6 +36,8 @@
|
||||
"backend/hostapi backend/store",
|
||||
"backend/ingest backend/vision",
|
||||
"backend/instanceguard backend/store",
|
||||
"backend/intentsession backend/intentgate",
|
||||
"backend/intentsession backend/store",
|
||||
"backend/pgbus backend/bus",
|
||||
"backend/pgbus backend/store",
|
||||
"backend/plugins backend/settings",
|
||||
@@ -49,6 +51,8 @@
|
||||
"backend/rag backend/settings",
|
||||
"backend/rag backend/store",
|
||||
"backend/route backend/intentgate",
|
||||
"backend/rundiag backend/cmdbus",
|
||||
"backend/rundiag backend/store",
|
||||
"backend/runmgr backend/bus",
|
||||
"backend/runmgr backend/cardsvc",
|
||||
"backend/runmgr backend/docgen",
|
||||
@@ -76,12 +80,14 @@
|
||||
"backend/server backend/execenv",
|
||||
"backend/server backend/ingest",
|
||||
"backend/server backend/intentgate",
|
||||
"backend/server backend/intentsession",
|
||||
"backend/server backend/plugins",
|
||||
"backend/server backend/profiles",
|
||||
"backend/server backend/projectscope",
|
||||
"backend/server backend/prompts",
|
||||
"backend/server backend/ratelimit",
|
||||
"backend/server backend/route",
|
||||
"backend/server backend/rundiag",
|
||||
"backend/server backend/runmgr",
|
||||
"backend/server backend/settings",
|
||||
"backend/server backend/store",
|
||||
|
||||
+3
-3
@@ -15,9 +15,9 @@
|
||||
|
||||
| ID | 事实 | 验证命令 | last_verified | TTL(天) |
|
||||
|---|---|---|---|---|
|
||||
| F1 | Gitea Web/API 可达:https://git-home.u1680832.nyat.app:30319(1.27.3) | `curl -fsS -m 10 https://git-home.u1680832.nyat.app:30319/api/v1/version` | 2026-10-14 | 30 |
|
||||
| F2 | API token 存在且可用(值在 ~/.secrets/gitea,HOME/IMC 两段各一组 `- ACCESS_TOKEN=…`,本条取 HOME 段;不入仓不回显,不进子进程 argv) | `bash scripts/facts-verify.sh api-token https://git-home.u1680832.nyat.app:30319/api/v1/user` | 2026-10-14 | 30 |
|
||||
| F3 | Git over SSH 可达:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/<Repo>.git(key ~/.ssh/home_gitea) | `GIT_SSH_COMMAND="ssh -i ~/.ssh/home_gitea -o BatchMode=yes" git ls-remote ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Development-Guidelines.git HEAD` | 2026-10-14 | 30 |
|
||||
| F1 | Gitea Web/API 可达(2026-10-22 迁移后新权威):https://gitea.naitangbaobao.cn(443,1.27.3);旧地址 git-home…:30319 过渡期仍可达,随时退役 | `curl -fsS -m 10 https://gitea.naitangbaobao.cn/api/v1/version` | 2026-10-22 | 30 |
|
||||
| F2 | API token 存在且可用(值在 ~/.secrets/gitea,HOME/IMC 两段各一组 `- ACCESS_TOKEN=…`,本条取 HOME 段;不入仓不回显,不进子进程 argv);在新权威 URL 实测 200 | `bash scripts/facts-verify.sh api-token https://gitea.naitangbaobao.cn/api/v1/user` | 2026-10-22 | 30 |
|
||||
| F3 | Git over SSH 可达(新地址,端口 52222):ssh://git@gitea.naitangbaobao.cn:52222/weichen/<Repo>.git(key ~/.ssh/home_gitea);旧地址 git-ssh…:31411 迁移期 push 间歇失败,不再作为权威 | `GIT_SSH_COMMAND="ssh -i ~/.ssh/home_gitea -p 52222 -o BatchMode=yes" git ls-remote ssh://git@gitea.naitangbaobao.cn:52222/weichen/Development-Guidelines.git HEAD` | 2026-10-22 | 30 |
|
||||
| F4 | 旧端点 git-panel.u1680832.nyat.app:30319 TCP 可连通但 TLS 握手失败(仅 TLS 层握手错误判 PASS) | `bash scripts/facts-verify.sh tls-handshake-fails git-panel.u1680832.nyat.app 30319` | 2026-10-14 | 180 |
|
||||
| F5 | 基座 users 表存有姓名(name 列,CreateAccount/ListUsersAdmin 读写;EXT-19 目录自 P3 起向授权管理读面下发) | `cd modules/backend && go test ./internal/app/ -run TestInprocHostSearchAccounts -count=1` | 2026-10-14 | 180 |
|
||||
| F6 | 基座账号无停用位(users 表无 disabled;EXT-19 Valid 恒为存在性)——停用机制未实现,不得当已具备 | `cd modules/backend && ! grep -rn -e disabled -e Disabled internal/store/auth.go pkg/extension/extension.go` | 2026-10-14 | 180 |
|
||||
|
||||
+130
-58
@@ -3,11 +3,13 @@
|
||||
#
|
||||
# 动线(与 frontend/e2e/rdm-audit.spec.ts 的环境契约一一对应):
|
||||
# 1. 构建:RDM 二进制(RDM 仓 origin/main 临时 worktree,pins 物化)
|
||||
# + 基座 CLI(本 worktree modules/backend,建号用)
|
||||
# + 基座 CLI(与 RDM pin 同源;identity ensure / plain-base serve)
|
||||
# + frontend dist(本 worktree modules/frontend)
|
||||
# 2. 种子:RDM -bootstrap-admin(域管理员)→ serve 首启建基座存储 → identity ensure →
|
||||
# 基座 user bootstrap-admin(login-key 激活契约;--password 已被拒绝)→
|
||||
# redeem+设密 → API rdm.project.create×2 + rdm.ingest(主项目)
|
||||
# 2. 种子:RDM -bootstrap-admin(域管理员)→ SUPER_ADMIN_* → serve 首启建基座存储
|
||||
# (超管)→ identity ensure → 超管密码登录会话 → API rdm.project.create×2
|
||||
# → 主项目宿主会话来源(会话+一条用户消息)→ 正式命令 rdm.reqs.import /
|
||||
# rdm.ingest 等携 conversationId/anchorMessageId;
|
||||
# 普通账号(e2e-auditor 等)经超管会话 POST /api/admin/users 创建
|
||||
# 3. 验收:vite preview(dist+/api 代理)→ npx playwright test
|
||||
# 4. 收尾:停 serve/preview,保存认证/失败工件到 $E2E_OUT
|
||||
#
|
||||
@@ -17,7 +19,7 @@
|
||||
# RDM_REPO RDM 仓路径(默认 <本仓上级>/Agent-System-RDM)
|
||||
# E2E_API_PORT/E2E_UI_PORT 服务端口(默认 18791/4173)
|
||||
# E2E_OWNER/E2E_PASSWORD 登录账号(默认 e2e-auditor/E2eAudit#2026)
|
||||
# E2E_PASSWORD2 首次登录后的设密结果(默认 E2eAudit#2026b;种子命令用它)
|
||||
# E2E_PASSWORD2 首次登录后的设密结果(默认 E2eAudit#2026b;Playwright 改密门消费)
|
||||
# E2E_PROJECT_MAIN/E2E_PROJECT_ISOLATED 项目名(默认 审计演示项目/隔离检查项目)
|
||||
# E2E_OUT 工件目录(默认 .tmp/e2e/<ts>;.gitignore 已覆盖 .tmp)
|
||||
# 依赖:go、node/npm、curl;首次运行会 npx playwright install chromium。
|
||||
@@ -60,8 +62,9 @@ done
|
||||
|
||||
# ── 1. 构建 ─────────────────────────────────────────────
|
||||
# RDM 源码经 git archive 从本地对象导出(不建 worktree、不触远端——Gitea 抖动时
|
||||
# submodule 克隆会挂死);pins/* 从 RDM 主检出的已物化 submodule 快照拷贝(cp 排除
|
||||
# .git,39MB 级),构建输入由此固定为 RDM origin/main + 其 gitlink 指向的本地内容。
|
||||
# submodule 克隆会挂死);pins/* 从 RDM 主检出的已物化 submodule 快照拷贝(tar 排除
|
||||
# .git;含 go.mod replace 引用的全部 pin,不仅 backend),构建输入由此固定为
|
||||
# RDM origin/main + 其 gitlink 指向的本地内容。
|
||||
log "build RDM binary (archive origin/main + snapshot pins from local checkout)"
|
||||
RDM_WT="$OUT/rdm-src"
|
||||
RDM_SHA=$(git -C "$RDM_REPO" rev-parse origin/main) || die "no origin/main in RDM repo"
|
||||
@@ -75,7 +78,9 @@ PIN_SHA_WANT=$(git -C "$RDM_REPO" ls-tree "$RDM_SHA" pins/agent-system-backend |
|
||||
[ "$PIN_SHA_MAIN" = "$PIN_SHA_WANT" ] \
|
||||
|| die "RDM checkout pins/backend ($PIN_SHA_MAIN) != origin/main pin ($PIN_SHA_WANT); git -C $RDM_REPO submodule update first"
|
||||
mkdir -p "$RDM_WT/pins"
|
||||
tar -C "$RDM_REPO/pins" --exclude=.git -cf - agent-system-backend | tar -C "$RDM_WT/pins" -xf -
|
||||
# 快照全部 pins/*(go.mod replace 依赖 agent-document-renderer、agent-squad 等,
|
||||
# 仅拷贝 backend 会导致 RDM 构建缺 pin);--exclude=.git 保持不变。
|
||||
tar -C "$RDM_REPO/pins" --exclude=.git -cf - . | tar -C "$RDM_WT/pins" -xf -
|
||||
cleanup() {
|
||||
[ -n "${API_PID:-}" ] && { kill "$API_PID" 2>/dev/null || true; wait "$API_PID" 2>/dev/null || true; }
|
||||
# Preview runs directly as one owned Vite process. Verify its command before
|
||||
@@ -88,14 +93,23 @@ trap cleanup EXIT
|
||||
(cd "$RDM_WT" && go build -o "$OUT/agent-system-rdm" .) || die "rdm build failed"
|
||||
log "rdm binary: $OUT/agent-system-rdm (RDM@${RDM_SHA:0:12}, pins@${PIN_SHA_WANT:0:12})"
|
||||
|
||||
log "build base CLI (account bootstrap/activation) from the same pinned backend as the RDM binary"
|
||||
# 版本一致性:建号 CLI 必须与被测 RDM 二进制共用同一基座 pin——用 Dev 顶层
|
||||
# backend(main 最新)建号会把库迁到更高 schema,RDM pins(较旧)打不开。
|
||||
log "build base CLI (identity ensure + plain-base serve) from the same pinned backend as the RDM binary"
|
||||
# 版本一致性:基座 CLI 必须与被测 RDM 二进制共用同一基座 pin——用 Dev 顶层
|
||||
# backend(main 最新)会把库迁到更高 schema,RDM pins(较旧)打不开。
|
||||
(cd "$RDM_WT/pins/agent-system-backend" && go build -o "$OUT/agent-system" ./cmd/agent-system) \
|
||||
|| die "backend cli build failed"
|
||||
|
||||
log "build frontend dist (contracts + ui deps + bundle, via make frontend)"
|
||||
make -C "$DEV_ROOT" frontend >/dev/null || die "frontend build failed"
|
||||
# 直接构建 FE dist:不走 make frontend(其 contracts→test-worktree 要求全部
|
||||
# submodule 已物化;本脚本只需 FE+@ui 源码组合)。contracts.ts 以 FE pin 为准。
|
||||
log "build frontend dist (npm ci @ui + frontend, vite build)"
|
||||
[ -f "$DEV_ROOT/modules/ui/package.json" ] \
|
||||
|| die "modules/ui missing (need gitlink checkout for @ui alias)"
|
||||
[ -f "$DEV_ROOT/modules/frontend/package.json" ] \
|
||||
|| die "modules/frontend missing"
|
||||
(cd "$DEV_ROOT/modules/ui" && npm ci --no-fund --no-audit) >/dev/null \
|
||||
|| die "ui npm ci failed"
|
||||
(cd "$DEV_ROOT/modules/frontend" && npm ci --no-fund --no-audit && npm run build) \
|
||||
|| die "frontend build failed"
|
||||
|
||||
# ── 2. 种子 ─────────────────────────────────────────────
|
||||
DATA="$OUT/data"
|
||||
@@ -105,9 +119,13 @@ RDM="$OUT/agent-system-rdm -data $DATA"
|
||||
log "bootstrap: RDM domain admin = $OWNER"
|
||||
$RDM -bootstrap-admin "$OWNER" >/dev/null
|
||||
|
||||
# ── 凭据契约(2026-10-13 起):--password 建号被 RECOVERY_CONTRACT_REPLACED 拒绝,
|
||||
# 改走 login-key 激活:serve 首启创建基座存储 → identity ensure → 基座首位账号
|
||||
# bootstrap-admin(激活 Key 交付)→ API redeem → 设密(PASSWORD2 为登录密码)。
|
||||
# ── 凭据契约:auth on 时超管由 serve 启动期 SUPER_ADMIN_OWNER/SUPER_ADMIN_PASSWORD
|
||||
# 建立(基座已禁用 bootstrap-admin)。种子 API 会话走超管密码登录;普通账号
|
||||
# (e2e-auditor 等)经 POST /api/admin/users 创建(初始密码合成值,mustChangePassword
|
||||
# 由 Playwright 首登改密门处理)。合成测试凭据(非真实秘密)。
|
||||
: "${SUPER_ADMIN_OWNER:=e2e-superadmin}"
|
||||
: "${SUPER_ADMIN_PASSWORD:=E2eSuperAdmin#FixedTest9kQm2NpR}"
|
||||
export SUPER_ADMIN_OWNER SUPER_ADMIN_PASSWORD
|
||||
log "serve RDM at $API_BASE (auth on;首启创建基座存储)"
|
||||
start_serve() {
|
||||
"$OUT/agent-system-rdm" -data "$DATA" -addr "127.0.0.1:$API_PORT" -auth on \
|
||||
@@ -125,64 +143,115 @@ stop_serve() {
|
||||
[ -n "${API_PID:-}" ] && { kill "$API_PID" 2>/dev/null || true; wait "$API_PID" 2>/dev/null || true; API_PID=""; }
|
||||
}
|
||||
login() {
|
||||
# Seed API session uses serve-established superadmin (not E2E_OWNER).
|
||||
local i
|
||||
for i in $(seq 1 20); do
|
||||
if curl -sf -c "$JAR" -X POST "$API_BASE/api/auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"owner\":\"$OWNER\",\"password\":\"$PASSWORD\"}" >/dev/null; then
|
||||
-d "{\"owner\":\"$SUPER_ADMIN_OWNER\",\"password\":\"$SUPER_ADMIN_PASSWORD\"}" >/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.25
|
||||
done
|
||||
die "login failed"
|
||||
die "superadmin login failed"
|
||||
}
|
||||
cmd() { # cmd <id> <params-json> → payload(失败即死,种子步骤不容忍部分成功)
|
||||
local id=$1 params=${2:-{\}}
|
||||
cmd() { # cmd <id> <params-json> [extra-body-fields] → payload(失败即死,种子步骤不容忍部分成功)
|
||||
local id=$1 params=${2:-{\}} extra=${3:-}
|
||||
curl -sf -b "$JAR" -X POST "$API_BASE/api/commands/$id" \
|
||||
-H 'Content-Type: application/json' -d "{\"params\":$params}" \
|
||||
-H 'Content-Type: application/json' -d "{${extra:+$extra,}\"params\":$params}" \
|
||||
| python3 -c 'import json,sys; d=json.load(sys.stdin); r=(d or {}).get("receipt",{}); sys.exit(1) if r.get("state")=="rejected" else None; print(json.dumps(d.get("payload", d), ensure_ascii=False))' \
|
||||
|| die "command $id rejected"
|
||||
}
|
||||
admin_create_user() { # admin_create_user <owner> <password> [display-name] [isAdmin]
|
||||
# 超管会话 POST /api/admin/users;200 且 mustChangePassword:true(Playwright 首登改密)。
|
||||
# 第 4 参为 true 时任命基座管理员(admin-user-create 需「系统管理…」入口;仅超管可任命)。
|
||||
local owner=$1 password=$2 name=${3:-$1} is_admin=${4:-false} body
|
||||
body=$(curl -sf -b "$JAR" -X POST "$API_BASE/api/admin/users" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"owner\":\"$owner\",\"password\":\"$password\",\"profile\":{\"name\":\"$name\"},\"isAdmin\":$is_admin}") \
|
||||
|| die "admin create user $owner failed"
|
||||
printf '%s' "$body" | python3 -c 'import json,sys; d=json.load(sys.stdin); sys.exit(0 if d.get("mustChangePassword") is True else 1)' \
|
||||
|| die "admin create user $owner: expected mustChangePassword:true"
|
||||
}
|
||||
# 浏览器用例若不以改密门处理该账号(如 cockpit 的 e2e-reader),种子阶段代完 must-change。
|
||||
complete_must_change() { # complete_must_change <owner> <initial> <final>
|
||||
local owner=$1 initial=$2 final=$3 jar i
|
||||
jar="$OUT/${owner}.changepw.jar"
|
||||
for i in $(seq 1 20); do
|
||||
if curl -sf -c "$jar" -X POST "$API_BASE/api/auth/login" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"owner\":\"$owner\",\"password\":\"$initial\"}" >/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 0.25
|
||||
[ "$i" = 20 ] && die "must-change login failed for $owner"
|
||||
done
|
||||
curl -sf -b "$jar" -c "$jar" -X POST "$API_BASE/api/auth/password" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"current\":\"$initial\",\"next\":\"$final\"}" >/dev/null \
|
||||
|| die "must-change password failed for $owner"
|
||||
}
|
||||
# 正式项目命令(rdm.reqs.*/rdm.review.*/rdm.draft.*/rdm.ingest 等,基座
|
||||
# cmdbus.RequiresProjectWorkDecision)须带已认证会话来源:conversationId 绑定宿主项目,
|
||||
# anchorMessageId 为该会话中已提交、经 RDM 来源分析放行的用户消息;裸调用被预检拒绝。
|
||||
seed_source() { # seed_source <hostProjectId>:超管在宿主项目建种子会话并提交一条来源消息
|
||||
local host=$1 body
|
||||
body=$(curl -sf -b "$JAR" -X POST "$API_BASE/api/projects/$host/conversations" \
|
||||
-H 'Content-Type: application/json' -d '{"title":"e2e seed source"}') \
|
||||
|| die "seed source conversation create failed"
|
||||
SRC_CONV=$(printf '%s' "$body" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')
|
||||
body=$(curl -sf -b "$JAR" -X POST "$API_BASE/api/conversations/$SRC_CONV/messages" \
|
||||
-H 'Content-Type: application/json' -d '{"text":"e2e 种子:导入需求台账、入库需求语料、登记驾驶舱探针与文档职责"}') \
|
||||
|| die "seed source message failed"
|
||||
SRC_MSG=$(printf '%s' "$body" | python3 -c 'import json,sys; print(json.load(sys.stdin)["message"]["id"])')
|
||||
}
|
||||
fcmd() { # fcmd <id> <params-json>:正式项目命令,附种子会话来源
|
||||
[ -n "${SRC_MSG:-}" ] || die "formal command $1 needs seed_source first"
|
||||
cmd "$1" "$2" "\"conversationId\":\"$SRC_CONV\",\"anchorMessageId\":\"$SRC_MSG\""
|
||||
}
|
||||
|
||||
# 阶段①:激活账号并建项目与需求台账(API;需 serve)。
|
||||
# 阶段①:超管会话 + 管理 API 建号,再建项目与需求台账(API;需 serve)。
|
||||
start_serve
|
||||
JAR="$OUT/cookies.txt"
|
||||
log "seed: identity ensure + base first account (login-key 契约)"
|
||||
log "seed: identity ensure + base first account (SUPER_ADMIN session)"
|
||||
ID_OK=0
|
||||
for i in $(seq 1 15); do
|
||||
if "$OUT/agent-system" identity ensure --data "$DATA" --instance-id "e2e-audit" >/dev/null 2>&1; then ID_OK=1; break; fi
|
||||
sleep 1 # 服务端受控迁移(含 identity 表)可能晚于 HTTP 就绪
|
||||
done
|
||||
[ "$ID_OK" = 1 ] || { tail -5 "$OUT/serve.log"; die "identity ensure failed"; }
|
||||
"$OUT/agent-system" user bootstrap-admin --data "$DATA" --owner "$OWNER" \
|
||||
--name "E2E 审计员" --out-file "$OUT/$OWNER.bootstrap.key" >/dev/null \
|
||||
|| die "base bootstrap-admin failed"
|
||||
K=$(cat "$OUT/$OWNER.bootstrap.key")
|
||||
curl -sf -c "$JAR" -X POST "$API_BASE/api/auth/login-key/redeem" \
|
||||
-H 'Content-Type: application/json' -d "{\"key\":\"$K\"}" >/dev/null || die "login-key redeem failed"
|
||||
# 激活会话仅能设密:设为 PASSWORD2(后续登录与规格消费一致)。
|
||||
curl -sf -b "$JAR" -c "$JAR" -X POST "$API_BASE/api/auth/password" \
|
||||
-H 'Content-Type: application/json' -d "{\"next\":\"$PASSWORD2\"}" >/dev/null || die "password setup failed"
|
||||
PASSWORD=$PASSWORD2
|
||||
# Legacy bootstrap-admin is disabled; super admin is established at serve startup
|
||||
# from SUPER_ADMIN_OWNER/SUPER_ADMIN_PASSWORD. Login via the normal session endpoint.
|
||||
login
|
||||
# Fixture owner(默认 e2e-auditor):RDM -bootstrap-admin 已授域管理员;此处再建基座账号
|
||||
# 并任命 isAdmin(admin-user-create 需「系统管理…」)。初始密码=PASSWORD,mustChange
|
||||
# 由 Playwright 首登改密门处理为 PASSWORD2(不覆盖超管会话 jar)。
|
||||
admin_create_user "$OWNER" "$PASSWORD" "$OWNER" true
|
||||
log "seed: create projects"
|
||||
PID_MAIN=$(cmd rdm.project.create "{\"name\":\"$PROJ_MAIN\"}" | python3 -c 'import json,sys; print(json.load(sys.stdin)["projectId"])')
|
||||
read -r PID_MAIN HOST_MAIN < <(cmd rdm.project.create "{\"name\":\"$PROJ_MAIN\"}" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d["projectId"], d["hostProjectId"])')
|
||||
[ -n "${HOST_MAIN:-}" ] || die "rdm.project.create: missing projectId/hostProjectId"
|
||||
PID_ISO=$(cmd rdm.project.create "{\"name\":\"$PROJ_ISO\"}" | python3 -c 'import json,sys; print(json.load(sys.stdin)["projectId"])')
|
||||
log "seed: projects main=$PID_MAIN iso=$PID_ISO"
|
||||
log "seed: projects main=$PID_MAIN (host $HOST_MAIN) iso=$PID_ISO"
|
||||
|
||||
# I1 requires a current business grant even for a project creator or system
|
||||
# administrator. Both projects are ordinary test fixtures in this private data root.
|
||||
log "seed: grant explicit audit.read for the fixture owner"
|
||||
# Projects are created under the SUPER_ADMIN session (immutable creator). The
|
||||
# Playwright fixture owner is a separate account: add it as project owner so
|
||||
# doc visibility comes from membership + member-role defaults.
|
||||
log "seed: add fixture owner as project member (owner role; doc via member defaults)"
|
||||
cmd rdm.project.member.add "{\"projectId\":\"$PID_MAIN\",\"member\":\"$OWNER\",\"role\":\"owner\"}" >/dev/null
|
||||
cmd rdm.project.member.add "{\"projectId\":\"$PID_ISO\",\"member\":\"$OWNER\",\"role\":\"owner\"}" >/dev/null
|
||||
# audit.read is never a member-role default and its platform cap ships closed
|
||||
# (rdm_perm_caps audit.read=0): open the cap, then grant it explicitly per project
|
||||
# (rdm.perm.set is rejected above the cap).
|
||||
log "seed: audit.read cap + explicit grant to fixture owner on both projects"
|
||||
cmd rdm.perm.cap '{"action":"audit.read","allow":true}' >/dev/null
|
||||
cmd rdm.perm.set "{\"projectId\":\"$PID_MAIN\",\"member\":\"$OWNER\",\"action\":\"audit.read\",\"allow\":true}" >/dev/null
|
||||
cmd rdm.perm.set "{\"projectId\":\"$PID_ISO\",\"member\":\"$OWNER\",\"action\":\"audit.read\",\"allow\":true}" >/dev/null
|
||||
|
||||
log "seed: grant explicit doc.read for the cockpit fixture owner"
|
||||
cmd rdm.perm.set "{\"projectId\":\"$PID_MAIN\",\"member\":\"$OWNER\",\"action\":\"doc.read\",\"allow\":true}" >/dev/null
|
||||
cmd rdm.perm.set "{\"projectId\":\"$PID_ISO\",\"member\":\"$OWNER\",\"action\":\"doc.read\",\"allow\":true}" >/dev/null
|
||||
for p in "$PID_MAIN" "$PID_ISO"; do
|
||||
cmd rdm.perm.set "{\"projectId\":\"$p\",\"member\":\"$OWNER\",\"action\":\"audit.read\",\"allow\":true}" >/dev/null
|
||||
done
|
||||
|
||||
log "seed: conversation source in $HOST_MAIN (formal project commands)"
|
||||
seed_source "$HOST_MAIN"
|
||||
log "seed: import requirement ledger into $PID_MAIN"
|
||||
cmd rdm.reqs.import "{\"projectId\":\"$PID_MAIN\",\"items\":$(
|
||||
fcmd rdm.reqs.import "{\"projectId\":\"$PID_MAIN\",\"items\":$(
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
rows = [
|
||||
@@ -225,33 +294,35 @@ SEED_MD='# 用户需求与系统需求
|
||||
审计工作区按项目隔离读取;管理员可跨项目目录进入。事件按操作归组,
|
||||
历史模式按 seq 还原,缺失事实明确标注不可恢复。'
|
||||
log "seed: ingest corpus into $PID_MAIN"
|
||||
cmd rdm.ingest "{\"projectId\":\"$PID_MAIN\",\"source\":\"01-用户需求与系统需求.md\",\"stage\":\"BD\",\"text\":$(python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))' <<<"$SEED_MD")}" >/dev/null
|
||||
fcmd rdm.ingest "{\"projectId\":\"$PID_MAIN\",\"source\":\"01-用户需求与系统需求.md\",\"stage\":\"BD\",\"text\":$(python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))' <<<"$SEED_MD")}" >/dev/null
|
||||
log "seed done"
|
||||
|
||||
# ── 驾驶舱 I4 种子:章节探针提案 + 非成员账号(正文经结构 YAML 装载) ──
|
||||
cmd rdm.perm.set "{\"projectId\":\"$PID_MAIN\",\"member\":\"$OWNER\",\"action\":\"doc.write\",\"allow\":true}" >/dev/null
|
||||
# Fixture owner already holds doc.write via owner membership defaults; seed
|
||||
# draft.propose runs as SUPER_ADMIN (project creator) and needs no perm.set.
|
||||
log "seed: cockpit pending proposal probe (non-danger; scenario 4)"
|
||||
COCKPIT_OV=$(cmd rdm.cockpit.overview "{\"projectId\":\"$PID_MAIN\"}")
|
||||
DOC_ID=$(printf '%s' "$COCKPIT_OV" | python3 -c 'import json,sys; d=json.load(sys.stdin); print([i["ref"]["id"] for i in d["documents"] if i["title"]=="验证主体报告"][0])')
|
||||
SEC_JSON=$(cmd rdm.cockpit.tree "{\"projectId\":\"$PID_MAIN\",\"parent\":\"$DOC_ID\"}")
|
||||
SEC_DESIGN=$(printf '%s' "$SEC_JSON" | python3 -c 'import json,sys; d=json.load(sys.stdin); print([i["ref"]["id"] for i in d["items"] if i["title"]=="设计说明"][0])')
|
||||
# 场景 4 驱动用:留一个未采纳提案(浏览器内经 API 采纳后观察投影一致)。
|
||||
cmd rdm.draft.propose "{\"projectId\":\"$PID_MAIN\",\"sectionId\":\"$SEC_DESIGN\",\"content\":\"一致性探针内容。\"}" >/dev/null
|
||||
fcmd rdm.draft.propose "{\"projectId\":\"$PID_MAIN\",\"sectionId\":\"$SEC_DESIGN\",\"content\":\"一致性探针内容。\"}" >/dev/null
|
||||
# 场景 2 驱动:验证主体报告设文档级三权(职责判据与正文版本作者无关;
|
||||
# 结构装载作者为 structure,owner 经文档职责获得正文可读)。
|
||||
log "seed: duty accounts + doc-level duties (scenario 2)"
|
||||
DOC_MAIN=$(printf '%s' "$COCKPIT_OV" | python3 -c 'import json,sys; d=json.load(sys.stdin); print([i["ref"]["id"] for i in d["documents"] if i["title"]=="验证主体报告"][0])')
|
||||
"$OUT/agent-system" user create --data "$DATA" --owner e2e-rev --out-file "$OUT/e2e-rev.bootstrap.key" >/dev/null || die "rev create failed"
|
||||
"$OUT/agent-system" user create --data "$DATA" --owner e2e-app --out-file "$OUT/e2e-app.bootstrap.key" >/dev/null || die "app create failed"
|
||||
admin_create_user e2e-rev 'E2eRev#2026' e2e-rev
|
||||
admin_create_user e2e-app 'E2eApp#2026' e2e-app
|
||||
cmd rdm.project.member.add "{\"projectId\":\"$PID_MAIN\",\"member\":\"e2e-rev\",\"role\":\"member\"}" >/dev/null
|
||||
cmd rdm.project.member.add "{\"projectId\":\"$PID_MAIN\",\"member\":\"e2e-app\",\"role\":\"member\"}" >/dev/null
|
||||
cmd rdm.review.assign.doc "{\"projectId\":\"$PID_MAIN\",\"docId\":\"$DOC_MAIN\",\"author\":\"$OWNER\",\"reviewers\":[\"e2e-rev\"],\"approver\":\"e2e-app\"}" >/dev/null
|
||||
fcmd rdm.review.assign.doc "{\"projectId\":\"$PID_MAIN\",\"docId\":\"$DOC_MAIN\",\"author\":\"$OWNER\",\"reviewers\":[\"e2e-rev\"],\"approver\":\"e2e-app\"}" >/dev/null
|
||||
log "seed: second account (non-member negative case)"
|
||||
"$OUT/agent-system" user create --data "$DATA" --owner e2e-reader --out-file "$OUT/e2e-reader.bootstrap.key" >/dev/null || die "reader create failed"
|
||||
K2=$(cat "$OUT/e2e-reader.bootstrap.key")
|
||||
JAR2="$OUT/reader.jar"
|
||||
curl -sf -c "$JAR2" -X POST "$API_BASE/api/auth/login-key/redeem" -H 'Content-Type: application/json' -d "{\"key\":\"$K2\",\"owner\":\"e2e-reader\"}" >/dev/null || die "reader redeem failed"
|
||||
curl -sf -b "$JAR2" -c "$JAR2" -X POST "$API_BASE/api/auth/password" -H 'Content-Type: application/json' -d "{\"next\":\"E2eReader#2026\"}" >/dev/null || die "reader password setup failed"
|
||||
# cockpit 场景 6 直接以 E2E_READER_PASSWORD 登录(无改密门);种子代完 must-change,
|
||||
# 使最终密码与 Playwright 缺省 E2eReader#2026 一致。
|
||||
READER=${E2E_READER:-e2e-reader}
|
||||
READER_PASSWORD=${E2E_READER_PASSWORD:-E2eReader#2026}
|
||||
admin_create_user "$READER" "$READER_PASSWORD" "$READER"
|
||||
complete_must_change "$READER" "$READER_PASSWORD" "$READER_PASSWORD"
|
||||
|
||||
# ── 3. 验收 ─────────────────────────────────────────────
|
||||
if [ "$SEED_ONLY" = 1 ]; then
|
||||
@@ -281,11 +352,12 @@ log "playwright (install chromium if missing)"
|
||||
log "run specs"
|
||||
cd "$DEV_ROOT/modules/frontend"
|
||||
export PLAYWRIGHT_BASE_URL="http://127.0.0.1:$UI_PORT" E2E_OWNER="$OWNER" \
|
||||
E2E_PASSWORD="${E2E_PASSWORD:-E2eAudit#2026}" E2E_PASSWORD2="$PASSWORD2" \
|
||||
E2E_PASSWORD="$PASSWORD" E2E_PASSWORD2="$PASSWORD2" \
|
||||
E2E_READER="$READER" E2E_READER_PASSWORD="$READER_PASSWORD" \
|
||||
E2E_PROJECT_MAIN="$PROJ_MAIN" E2E_PROJECT_ISOLATED="$PROJ_ISO"
|
||||
set +e
|
||||
# 本脚本的种子只覆盖审计工作区。u7 现场验收用例依赖另一套入口,不在这里跑。
|
||||
# admin-create 场景复用本种子的基座管理员账号(bootstrap-admin)。
|
||||
# admin-create 场景复用本种子的基座超管会话(SUPER_ADMIN_*)。
|
||||
npx playwright test e2e/rdm-audit.playwright.ts e2e/rdm-cockpit.playwright.ts e2e/admin-user-create.playwright.ts --reporter=list >"$OUT/playwright.log" 2>&1
|
||||
PW_EXIT=$?
|
||||
set -e
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# 用法:make facts-verify [ARGS=--update] (--update:把 PASS 项 last_verified 刷为当天并写回)
|
||||
# 表内验证命令约定不含竖线;含 "\|" 转义时按字面还原后执行。
|
||||
# 登记表引用的探针子命令(单独运行时在 stderr 说明判定,不输出凭据):
|
||||
# api-token URL URL 须逐字等于登记端点 https://git-home.u1680832.nyat.app:30319/api/v1/user,
|
||||
# api-token URL URL 须逐字等于登记端点 https://gitea.naitangbaobao.cn/api/v1/user,
|
||||
# 校验通过后才进程内读 ~/.secrets/gitea HOME 段 ACCESS_TOKEN 并 GET(不跟随重定向);
|
||||
# 0=接受 3=缺失 4=格式不符 5=请求失败/被拒/重定向 6=非登记端点
|
||||
# tls-handshake-fails HOST PORT 仅当 TCP 已连通且 TLS 层握手报错时 0;
|
||||
@@ -20,7 +20,7 @@ import sys, urllib.request
|
||||
sys.path.insert(0, sys.argv[1])
|
||||
import task_triage
|
||||
|
||||
ENDPOINT = "https://git-home.u1680832.nyat.app:30319/api/v1/user"
|
||||
ENDPOINT = "https://gitea.naitangbaobao.cn/api/v1/user"
|
||||
if sys.argv[2] != ENDPOINT:
|
||||
print("api-token: URL 不是 policy/facts.md F2 登记端点,拒绝发送凭据", file=sys.stderr)
|
||||
sys.exit(6)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# RDM 制品钉扎。三行都是 40 位 hex。升级走 PR,不在 CI 里执行候选文件。
|
||||
# sha:生命周期套件的目标版本(6-pin 布局)。
|
||||
# sha:生命周期套件的目标版本(6-pin 布局);本次钉扎为增量 D(W11 DHF U2-U6)交付版本,
|
||||
# 即 RDM main 上增量 D 合并 a3c721ae4 之后的 tip。
|
||||
# prev_sha:来回部署身份稳定性测试的起点。选取受两条硬约束——与 sha 的 pins 布局一致,
|
||||
# 且两者之间无跨 schema 迁移(scripts/svc.sh 的 schema 兼容闸会在停旧服务前拒绝不兼容切换)。
|
||||
# 取上一个已钉住的 sha:同为 6-pin 布局,与 sha 之间无 schema/迁移变更,在 RDM 主线上与 sha
|
||||
# 相距 59 个提交,往返部署覆盖的版本跨度大于仅隔一次合并的相邻提交。
|
||||
# 取上一个已钉住的 sha:同为 6-pin 布局,与 sha 之间无 store schema 迁移(W11/W8 变更走事件
|
||||
# 重放而非 schema),往返部署覆盖的正是上一交付版本到增量 D 的真实升级路径。
|
||||
# legacy_sha:承载「无身份」语义(没有 version --json),必然早于 6-pin 布局。
|
||||
# 三者的 pins 布局可能不同;CI 按每个 pin 自身的 gitlink 清单物化并逐个断言,不要求布局统一。
|
||||
# 来回切换的 schema 兼容不由钉扎保证,由真实生命周期套件检验。
|
||||
sha=ee8c2cdf4a0ed88cdc4037f4b43501e1eebc4c5b
|
||||
prev_sha=bc8db886606e88201e7f7210a1357d22dc84124b
|
||||
sha=9176476bf35d23e7886bf6ccb7c3a688067d357f
|
||||
prev_sha=15fdd479a9fe60a59559139305b8d760931cec93
|
||||
legacy_sha=72bb1fec79d64c2af5e49416640712320816c3b4
|
||||
|
||||
+16
-15
@@ -256,7 +256,7 @@ class FactsVerifyTests(unittest.TestCase):
|
||||
self.assert_status(result.stdout, "F2", "PASS")
|
||||
requests = self.stub_events("http")
|
||||
self.assertEqual(len(requests), 1, requests)
|
||||
self.assertEqual(requests[0]["url"], "https://git-home.u1680832.nyat.app:30319/api/v1/user")
|
||||
self.assertEqual(requests[0]["url"], "https://gitea.naitangbaobao.cn/api/v1/user")
|
||||
self.assertEqual(requests[0]["authorization"], "token " + HOME_TOKEN)
|
||||
self.assertEqual(self.dates(), {"F2": self.today, "F8": self.today, "F9": OLD_DATE})
|
||||
self.assertTrue(receipt.exists(), result.stderr)
|
||||
@@ -285,7 +285,7 @@ class FactsVerifyTests(unittest.TestCase):
|
||||
self.assert_status(result.stdout, "F2", "FAIL")
|
||||
self.assertEqual(self.stub_events("http"), [], "no request without a valid HOME token")
|
||||
self.assertEqual(self.dates()["F2"], OLD_DATE)
|
||||
direct = self.run_script("api-token", "https://git-home.u1680832.nyat.app:30319/api/v1/user")
|
||||
direct = self.run_script("api-token", "https://gitea.naitangbaobao.cn/api/v1/user")
|
||||
self.assertNotEqual(direct.returncode, 0)
|
||||
self.assertIn("ACCESS_TOKEN", direct.stderr)
|
||||
self.assert_no_secrets(result.stdout, result.stderr, direct.stdout, direct.stderr,
|
||||
@@ -299,7 +299,7 @@ class FactsVerifyTests(unittest.TestCase):
|
||||
result = self.run_script("--update", http=http)
|
||||
self.assert_status(result.stdout, "F2", "FAIL")
|
||||
self.assertEqual(self.dates(), {"F2": OLD_DATE, "F8": self.today, "F9": OLD_DATE})
|
||||
direct = self.run_script("api-token", "https://git-home.u1680832.nyat.app:30319/api/v1/user",
|
||||
direct = self.run_script("api-token", "https://gitea.naitangbaobao.cn/api/v1/user",
|
||||
http=http)
|
||||
self.assertNotEqual(direct.returncode, 0)
|
||||
self.assertNotIn("Authorization", direct.stderr)
|
||||
@@ -307,17 +307,18 @@ class FactsVerifyTests(unittest.TestCase):
|
||||
|
||||
def test_f2_rejects_unregistered_destination_before_reading_credentials(self):
|
||||
bad_urls = [
|
||||
"http://git-home.u1680832.nyat.app:30319/api/v1/user",
|
||||
"http://gitea.naitangbaobao.cn/api/v1/user",
|
||||
"https://evil.invalid/api/v1/user",
|
||||
"https://git-home.u1680832.nyat.app:30320/api/v1/user",
|
||||
"https://git-home.u1680832.nyat.app/api/v1/user",
|
||||
"https://user:pw@git-home.u1680832.nyat.app:30319/api/v1/user",
|
||||
"https://git-home.u1680832.nyat.app:30319@evil.invalid/api/v1/user",
|
||||
"https://git-home.u1680832.nyat.app:30319/api/v1/user#frag",
|
||||
"https://git-home.u1680832.nyat.app:30319/api/v1/user?next=https://evil.invalid",
|
||||
"https://git-home.u1680832.nyat.app:30319/api/v1/users",
|
||||
"https://git-home.u1680832.nyat.app:30319/api/v1/user/../admin",
|
||||
"https://git-home.u1680832.nyat.app:30319/api/v1/user ",
|
||||
"https://git-home.u1680832.nyat.app:30319/api/v1/user",
|
||||
"https://gitea.naitangbaobao.cn:30320/api/v1/user",
|
||||
"https://gitea.naitangbaobao.cn:443/api/v1/user",
|
||||
"https://user:pw@gitea.naitangbaobao.cn/api/v1/user",
|
||||
"https://gitea.naitangbaobao.cn@evil.invalid/api/v1/user",
|
||||
"https://gitea.naitangbaobao.cn/api/v1/user#frag",
|
||||
"https://gitea.naitangbaobao.cn/api/v1/user?next=https://evil.invalid",
|
||||
"https://gitea.naitangbaobao.cn/api/v1/users",
|
||||
"https://gitea.naitangbaobao.cn/api/v1/user/../admin",
|
||||
"https://gitea.naitangbaobao.cn/api/v1/user ",
|
||||
]
|
||||
for secrets_present in (True, False):
|
||||
if not secrets_present:
|
||||
@@ -337,12 +338,12 @@ class FactsVerifyTests(unittest.TestCase):
|
||||
for code in ("301", "302", "303", "307", "308"):
|
||||
with self.subTest(code):
|
||||
self.stub_log.unlink(missing_ok=True)
|
||||
direct = self.run_script("api-token", "https://git-home.u1680832.nyat.app:30319/api/v1/user",
|
||||
direct = self.run_script("api-token", "https://gitea.naitangbaobao.cn/api/v1/user",
|
||||
http=code)
|
||||
self.assertEqual(direct.returncode, 5, direct.stderr)
|
||||
requests = self.stub_events("http")
|
||||
self.assertEqual([r["url"] for r in requests],
|
||||
["https://git-home.u1680832.nyat.app:30319/api/v1/user"], "no follow-up request")
|
||||
["https://gitea.naitangbaobao.cn/api/v1/user"], "no follow-up request")
|
||||
self.assertNotIn("redirect-target", direct.stdout + direct.stderr)
|
||||
self.assert_no_secrets(direct.stdout, direct.stderr)
|
||||
|
||||
|
||||
+38
-25
@@ -296,12 +296,14 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 1. 存量 exec= DSN:连续启动两次均恢复,进程参数无秘密 ----
|
||||
|
||||
def test_legacy_exec_dsn_survives_consecutive_restarts(self):
|
||||
self.register_pg("pg1", 18890, password="legacy-pw-1")
|
||||
dsn = "postgresql://agent:legacy-pw-1@127.0.0.1:18890/agent"
|
||||
pg_port = free_test_port()
|
||||
self.register_pg("pg1", pg_port, password="legacy-pw-1")
|
||||
dsn = "postgresql://agent:legacy-pw-1@127.0.0.1:%d/agent" % pg_port
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", 18791,
|
||||
"--addr 127.0.0.1:18791 --data %s --plugins %s --auth auto --dsn %s"
|
||||
% (d / "data", d / "plugins", dsn))
|
||||
port = free_test_port()
|
||||
self.seed_agent("dev", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto --dsn %s"
|
||||
% (port, d / "data", d / "plugins", dsn))
|
||||
for _ in range(2):
|
||||
self.clear_serve_record()
|
||||
self.svc("dev", "restart")
|
||||
@@ -322,12 +324,14 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 2. 仅 dbref:裸 restart 与重新部署均保留绑定 ----
|
||||
|
||||
def test_dbref_binding_survives_bare_restart_and_redeploy(self):
|
||||
self.register_pg("pg1", 18891, password="bind-pw-2")
|
||||
dsn = "postgresql://agent:bind-pw-2@127.0.0.1:18891/agent"
|
||||
pg_port = free_test_port()
|
||||
self.register_pg("pg1", pg_port, password="bind-pw-2")
|
||||
dsn = "postgresql://agent:bind-pw-2@127.0.0.1:%d/agent" % pg_port
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", 18792,
|
||||
"--addr 127.0.0.1:18792 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
port = free_test_port()
|
||||
self.seed_agent("dev", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg")])
|
||||
self.clear_serve_record()
|
||||
self.svc("dev", "restart")
|
||||
@@ -413,9 +417,10 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
|
||||
def test_refusals_keep_registration_without_sqlite_fallback(self):
|
||||
d = self.runtime("a1")
|
||||
self.seed_agent("a1", 18796,
|
||||
"--addr 127.0.0.1:18796 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
port = free_test_port()
|
||||
self.seed_agent("a1", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pgmissing/agent"), ("db", "pg")])
|
||||
self.clear_serve_record()
|
||||
result = self.svc("a1", "restart", check=False)
|
||||
@@ -425,33 +430,41 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertEqual(self.kv("a1", "dbref"), "pgmissing/agent") # 可恢复配置保留
|
||||
|
||||
# 实例登记不全(缺 password)→ 拒启。
|
||||
self.register_pg("pginc", 18895, complete=False)
|
||||
self.register_pg("pginc", free_test_port(), complete=False)
|
||||
d = self.runtime("a2")
|
||||
self.seed_agent("a2", 18797,
|
||||
"--addr 127.0.0.1:18797 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
port = free_test_port()
|
||||
self.seed_agent("a2", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pginc/agent"), ("db", "pg")])
|
||||
result = self.svc("a2", "restart", check=False)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("拒绝", result.stdout)
|
||||
|
||||
# dbref 与 dsn= 指向不同库 → 配置冲突拒启。
|
||||
self.register_pg("pg1", 18896, password="clash-pw")
|
||||
pg_port = free_test_port()
|
||||
self.register_pg("pg1", pg_port, password="clash-pw")
|
||||
other_pg_port = free_test_port()
|
||||
while other_pg_port == pg_port:
|
||||
other_pg_port = free_test_port()
|
||||
d = self.runtime("a3")
|
||||
self.seed_agent("a3", 18798,
|
||||
"--addr 127.0.0.1:18798 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
port = free_test_port()
|
||||
self.seed_agent("a3", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg"),
|
||||
("dsn", "postgresql://u:p@127.0.0.1:18999/otherdb")])
|
||||
("dsn", "postgresql://u:p@127.0.0.1:%d/otherdb"
|
||||
% other_pg_port)])
|
||||
result = self.svc("a3", "restart", check=False)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("拒绝", result.stdout)
|
||||
|
||||
# db=pg 但无可恢复 DSN → 拒启。
|
||||
d = self.runtime("a4")
|
||||
self.seed_agent("a4", 18799,
|
||||
"--addr 127.0.0.1:18799 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
port = free_test_port()
|
||||
self.seed_agent("a4", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"),
|
||||
extra_rows=[("db", "pg")])
|
||||
result = self.svc("a4", "restart", check=False)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
|
||||
@@ -2,9 +2,13 @@
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
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]
|
||||
_spec = importlib.util.spec_from_file_location(
|
||||
"tsi", SOURCE / "tests" / "test_svc_identity.py")
|
||||
@@ -46,7 +50,7 @@ class DeployTxnTests(unittest.TestCase):
|
||||
new.chmod(0o755)
|
||||
fx.stub.write_text(old.read_text())
|
||||
fx.stub.chmod(0o755)
|
||||
fx.seed_agent("txn", 9471)
|
||||
fx.seed_agent("txn", free_test_port())
|
||||
fx.start_ok("txn")
|
||||
pid = fx.pid_of("txn")
|
||||
script = fx.root / "scripts" / "svc.sh"
|
||||
@@ -68,7 +72,7 @@ class DeployTxnTests(unittest.TestCase):
|
||||
|
||||
def test_forward_only_requires_confirm_and_backup(self):
|
||||
fx = self.fx
|
||||
fx.seed_agent("fwd", 9472)
|
||||
fx.seed_agent("fwd", free_test_port())
|
||||
fx.start_ok("fwd")
|
||||
iid = json.loads((fx.runtime("fwd") / "manifest.json").read_text())["instance_id"]
|
||||
base = fx.stub.read_text()
|
||||
|
||||
+36
-16
@@ -12,10 +12,14 @@ import os
|
||||
import shutil
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
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"
|
||||
|
||||
@@ -56,7 +60,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- T1 连续裸启停:同一身份与数据根 ----
|
||||
|
||||
def test_T1_bare_restart_stable_identity(self):
|
||||
self.seed_agent("id1", 9431)
|
||||
port = free_test_port()
|
||||
self.seed_agent("id1", port)
|
||||
self.start_ok("id1")
|
||||
m1 = self.manifest("id1")
|
||||
self.assertIsNotNone(m1, "start 应完成 manifest 迁移")
|
||||
@@ -79,7 +84,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- T2 只改端口:数据/DB 锚点不动 ----
|
||||
|
||||
def test_T2_port_change_keeps_anchors(self):
|
||||
self.seed_agent("id2", 9432)
|
||||
port = free_test_port()
|
||||
self.seed_agent("id2", port)
|
||||
self.start_ok("id2")
|
||||
anchor = self.manifest("id2")["identity"]
|
||||
self.svc("id2", "stop")
|
||||
@@ -95,7 +101,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- T3 数据根漂移:拒绝启动,不新建空系统 ----
|
||||
|
||||
def test_T3_data_drift_rejected(self):
|
||||
self.seed_agent("id3", 9434)
|
||||
port = free_test_port()
|
||||
self.seed_agent("id3", port)
|
||||
self.start_ok("id3")
|
||||
self.svc("id3", "stop")
|
||||
other = self.root / "other-data"
|
||||
@@ -111,7 +118,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- T4 迁移窗口与损坏:不猜测 ----
|
||||
|
||||
def test_T4a_leftover_new_file_ignored(self):
|
||||
self.seed_agent("id4", 9435)
|
||||
port = free_test_port()
|
||||
self.seed_agent("id4", port)
|
||||
# 模拟 rename 前崩溃残留:.new 半成品必须被忽略/覆盖,不影响迁移结果
|
||||
(self.runtime("id4") / "manifest.json.new").write_text("{ truncated")
|
||||
self.start_ok("id4")
|
||||
@@ -121,7 +129,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
self.svc("id4", "stop")
|
||||
|
||||
def test_T4b_corrupt_manifest_blocks(self):
|
||||
self.seed_agent("id5", 9436)
|
||||
port = free_test_port()
|
||||
self.seed_agent("id5", port)
|
||||
self.start_ok("id5")
|
||||
self.svc("id5", "stop")
|
||||
(self.runtime("id5") / "manifest.json").write_text("{ not json")
|
||||
@@ -133,7 +142,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- T5 marker/manifest 事件:缺一即阻塞,不自动新建 ----
|
||||
|
||||
def test_T5a_missing_marker_blocks(self):
|
||||
self.seed_agent("id6", 9437)
|
||||
port = free_test_port()
|
||||
self.seed_agent("id6", port)
|
||||
self.start_ok("id6")
|
||||
self.svc("id6", "stop")
|
||||
m = self.manifest("id6")
|
||||
@@ -145,7 +155,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
self.assertFalse(self.running("id6"))
|
||||
|
||||
def test_T5b_foreign_marker_blocks(self):
|
||||
self.seed_agent("id7", 9438)
|
||||
port = free_test_port()
|
||||
self.seed_agent("id7", port)
|
||||
self.start_ok("id7")
|
||||
self.svc("id7", "stop")
|
||||
m = self.manifest("id7")
|
||||
@@ -158,7 +169,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
self.assertFalse(self.running("id7"))
|
||||
|
||||
def test_T5c_lost_manifest_blocks_not_rebuild(self):
|
||||
self.seed_agent("id8", 9439)
|
||||
port = free_test_port()
|
||||
self.seed_agent("id8", port)
|
||||
self.start_ok("id8")
|
||||
m0 = self.manifest("id8")
|
||||
iid = m0["instance_id"]
|
||||
@@ -178,7 +190,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- T6 cwd 无关:从别处调用身份不变 ----
|
||||
|
||||
def test_T6_cwd_independent_identity(self):
|
||||
self.seed_agent("id9", 9440)
|
||||
port = free_test_port()
|
||||
self.seed_agent("id9", port)
|
||||
self.start_ok("id9")
|
||||
iid = self.manifest("id9")["instance_id"]
|
||||
self.svc("id9", "stop")
|
||||
@@ -320,7 +333,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
旧服务继续运行、旧制品与登记不变(2026-10-12 rdm1/rdmcand 反例:停服→
|
||||
覆盖→启动即拒,只剩混合状态)。"""
|
||||
remote, old_sha, new_sha = self.make_rdm_repo(self.noid_stub())
|
||||
self.seed_agent("rdx", 9451)
|
||||
port = free_test_port()
|
||||
self.seed_agent("rdx", port)
|
||||
self.start_ok("rdx") # 已建立身份,有能力制品在运行
|
||||
self.set_kv("rdx", "source", "rdm@%s" % old_sha)
|
||||
pid = self.pid_of("rdx")
|
||||
@@ -344,7 +358,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
"""已建立身份后删行:update 在停服前拒绝,不自动补行,旧进程保持。
|
||||
补行只走 adopt --confirm。"""
|
||||
old_sha, new_sha = self.make_src_repo()
|
||||
self.seed_agent("rowx", 9452)
|
||||
port = free_test_port()
|
||||
self.seed_agent("rowx", port)
|
||||
self.start_ok("rowx")
|
||||
self.set_kv("rowx", "source", "dev@%s" % old_sha)
|
||||
db = Path(self.manifest("rowx")["identity"]["data_root"]) / "agent-system.db"
|
||||
@@ -364,7 +379,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
def test_U1_bare_start_missing_row_is_event_init_recovers(self):
|
||||
"""普通启动只验证不补行:DB 行缺失 → 拒绝且不建空行(缺行即事件);
|
||||
显式 init 补齐后恢复。"""
|
||||
self.seed_agent("rowm", 9453)
|
||||
port = free_test_port()
|
||||
self.seed_agent("rowm", port)
|
||||
self.start_ok("rowm")
|
||||
iid = self.manifest("rowm")["instance_id"]
|
||||
self.svc("rowm", "stop")
|
||||
@@ -416,7 +432,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
def test_U1_adopt_identityless_artifact_reports_pending(self):
|
||||
"""已装制品无 identity 能力:adopt 的 ensure 静默跳过,marker 留在 pending——
|
||||
输出必须如实报 pending,不得宣称 db_marker=established。"""
|
||||
self.seed_agent("adx", 9454)
|
||||
port = free_test_port()
|
||||
self.seed_agent("adx", port)
|
||||
self.start_ok("adx")
|
||||
iid = self.manifest("adx")["instance_id"]
|
||||
self.svc("adx", "stop")
|
||||
@@ -437,7 +454,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
cand.chmod(0o755)
|
||||
remote, old_sha, _ = self._make_track_repo(cand, "agent-system")
|
||||
self.git_ok(self.root, "clone", str(remote), str(self.root / "srccache"))
|
||||
self.seed_agent("scx", 9455)
|
||||
port = free_test_port()
|
||||
self.seed_agent("scx", port)
|
||||
self.start_ok("scx")
|
||||
self.set_kv("scx", "source", "dev@%s" % old_sha)
|
||||
mpath = self.runtime("scx") / "manifest.json"
|
||||
@@ -459,7 +477,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
self.svc("scx", "stop")
|
||||
|
||||
def test_I3_start_ensures_sqlite_identity(self):
|
||||
self.seed_agent("idb1", 9441)
|
||||
port = free_test_port()
|
||||
self.seed_agent("idb1", port)
|
||||
self.start_ok("idb1")
|
||||
iid = self.manifest("idb1")["instance_id"]
|
||||
rows = self.db_identity_row("idb1")
|
||||
@@ -470,7 +489,8 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
self.svc("idb1", "stop")
|
||||
|
||||
def test_I3_wrong_db_identity_blocks_start(self):
|
||||
self.seed_agent("idb2", 9442)
|
||||
port = free_test_port()
|
||||
self.seed_agent("idb2", port)
|
||||
self.start_ok("idb2")
|
||||
iid = self.manifest("idb2")["instance_id"]
|
||||
self.svc("idb2", "stop")
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
"""生命周期隔离:调用方的 AGENT_SYSTEM_* 不得进入 serve,serve.env 为 0600。"""
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
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]
|
||||
_spec = importlib.util.spec_from_file_location("tsr", SOURCE / "tests" / "test_svc_recover.py")
|
||||
tsr = importlib.util.module_from_spec(_spec)
|
||||
@@ -17,7 +22,8 @@ class LifecycleIsolationTests(tsr.SvcAutoRecoverTests):
|
||||
self.root / "scripts" / "svc_manifest.py")
|
||||
|
||||
def test_caller_identity_env_does_not_reach_serve(self):
|
||||
self.seed_agent("iso", 9461)
|
||||
port = free_test_port()
|
||||
self.seed_agent("iso", port)
|
||||
env = dict(self.env)
|
||||
env["AGENT_SYSTEM_DSN"] = "postgres://leak-user:leak-pass@127.0.0.1/leak"
|
||||
env["AGENT_SYSTEM_DATA"] = "/tmp/leak-data"
|
||||
@@ -38,7 +44,8 @@ class LifecycleIsolationTests(tsr.SvcAutoRecoverTests):
|
||||
self.svc("iso", "stop")
|
||||
|
||||
def test_two_bare_restarts_keep_identity(self):
|
||||
self.seed_agent("iso2", 9462)
|
||||
port = free_test_port()
|
||||
self.seed_agent("iso2", port)
|
||||
self.svc("iso2", "start")
|
||||
manifest = json.loads((self.runtime("iso2") / "manifest.json").read_text())
|
||||
iid = manifest["instance_id"]
|
||||
|
||||
+29
-12
@@ -11,12 +11,17 @@
|
||||
夹具复用 tests/test_svc_recover.py(桩不需要运行——台账操作是 SQLite 直读直写)。
|
||||
"""
|
||||
import importlib.util
|
||||
import os
|
||||
import shutil
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
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]
|
||||
|
||||
_spec = importlib.util.spec_from_file_location("tsr", SOURCE / "tests" / "test_svc_recover.py")
|
||||
@@ -64,21 +69,24 @@ class RdmAdminTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- show ----
|
||||
|
||||
def test_show_prints_ledger_and_flags_unknown_owner(self):
|
||||
self.seed_agent("ra1", 9461)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ra1", port)
|
||||
self.seed_dbs("ra1", ledger_owner="u7-admin", base_owners=("weichen",))
|
||||
out = self.svc("ra1", "rdm-admin", "show").stdout
|
||||
self.assertIn("RDM 域管理员 u7-admin", out)
|
||||
self.assertIn("不在基座用户表", out) # u7-admin 不在 users → 残留警示
|
||||
|
||||
def test_show_known_owner_no_warning(self):
|
||||
self.seed_agent("ra2", 9462)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ra2", port)
|
||||
self.seed_dbs("ra2", ledger_owner="weichen")
|
||||
out = self.svc("ra2", "rdm-admin").stdout # 缺省子命令 = show
|
||||
self.assertIn("RDM 域管理员 weichen", out)
|
||||
self.assertNotIn("不在基座用户表", out)
|
||||
|
||||
def test_show_empty_ledger_hint(self):
|
||||
self.seed_agent("ra3", 9463)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ra3", port)
|
||||
self.seed_dbs("ra3", ledger_owner=None)
|
||||
out = self.svc("ra3", "rdm-admin").stdout
|
||||
self.assertIn("台账为空", out)
|
||||
@@ -87,7 +95,8 @@ class RdmAdminTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- transfer ----
|
||||
|
||||
def test_transfer_rewrites_single_row_with_provenance(self):
|
||||
self.seed_agent("ra4", 9464)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ra4", port)
|
||||
self.seed_dbs("ra4", ledger_owner="u7-admin")
|
||||
out = self.svc("ra4", "rdm-admin", "transfer", "--to", "weichen").stdout
|
||||
self.assertIn("受信部署通道", out)
|
||||
@@ -97,13 +106,15 @@ class RdmAdminTests(tsr.SvcAutoRecoverTests):
|
||||
self.assertEqual(added_by, "svc:rdm-admin:u7-admin") # 前任可追溯
|
||||
|
||||
def test_transfer_idempotent_when_already_owner(self):
|
||||
self.seed_agent("ra5", 9465)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ra5", port)
|
||||
self.seed_dbs("ra5", ledger_owner="weichen")
|
||||
out = self.svc("ra5", "rdm-admin", "transfer", "--to", "weichen").stdout
|
||||
self.assertIn("已是 weichen", out)
|
||||
|
||||
def test_transfer_rejects_owner_missing_from_base_users(self):
|
||||
self.seed_agent("ra6", 9466)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ra6", port)
|
||||
self.seed_dbs("ra6", ledger_owner="weichen")
|
||||
proc = self.svc("ra6", "rdm-admin", "transfer", "--to", "nobody", check=False)
|
||||
self.assertEqual(proc.returncode, 1, proc.stdout)
|
||||
@@ -111,7 +122,8 @@ class RdmAdminTests(tsr.SvcAutoRecoverTests):
|
||||
self.assertEqual(self.ledger_owner("ra6")[0], "weichen") # 台账未被动
|
||||
|
||||
def test_transfer_rejects_empty_ledger(self):
|
||||
self.seed_agent("ra7", 9467)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ra7", port)
|
||||
self.seed_dbs("ra7", ledger_owner=None)
|
||||
proc = self.svc("ra7", "rdm-admin", "transfer", "--to", "weichen", check=False)
|
||||
self.assertEqual(proc.returncode, 1, proc.stdout)
|
||||
@@ -120,7 +132,8 @@ class RdmAdminTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- 绑定合同与用法 ----
|
||||
|
||||
def test_refuses_when_rdm_db_missing(self):
|
||||
self.seed_agent("ra8", 9468)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ra8", port)
|
||||
self.seed_dbs("ra8") # 建基座库后删 rdm.db
|
||||
(self.runtime("ra8") / "data" / "rdm.db").unlink()
|
||||
proc = self.svc("ra8", "rdm-admin", check=False)
|
||||
@@ -128,13 +141,15 @@ class RdmAdminTests(tsr.SvcAutoRecoverTests):
|
||||
self.assertIn("无 RDM 域库", proc.stdout)
|
||||
|
||||
def test_refuses_when_base_db_missing(self):
|
||||
self.seed_agent("ra9", 9469)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ra9", port)
|
||||
proc = self.svc("ra9", "rdm-admin", check=False)
|
||||
self.assertEqual(proc.returncode, 1, proc.stdout)
|
||||
self.assertIn("既有库不存在", proc.stdout)
|
||||
|
||||
def test_unknown_subcommand_usage_exit_2(self):
|
||||
self.seed_agent("raa", 9470)
|
||||
port = free_test_port()
|
||||
self.seed_agent("raa", port)
|
||||
proc = self.svc("raa", "rdm-admin", "bogus", check=False)
|
||||
self.assertEqual(proc.returncode, 2, proc.stdout)
|
||||
self.assertIn("用法", proc.stdout)
|
||||
@@ -142,14 +157,16 @@ class RdmAdminTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- status 巡检行 ----
|
||||
|
||||
def test_status_lists_rdm_admin_row_and_warning(self):
|
||||
self.seed_agent("rab", 9471)
|
||||
port = free_test_port()
|
||||
self.seed_agent("rab", port)
|
||||
self.seed_dbs("rab", ledger_owner="u7-admin", base_owners=("weichen",))
|
||||
out = self.svc("rab", "status").stdout
|
||||
self.assertIn("RDM 域管理员 u7-admin", out)
|
||||
self.assertIn("不在基座用户表", out)
|
||||
|
||||
def test_status_silent_without_rdm_db(self):
|
||||
self.seed_agent("rac", 9472)
|
||||
port = free_test_port()
|
||||
self.seed_agent("rac", port)
|
||||
self.seed_dbs("rac")
|
||||
(self.runtime("rac") / "data" / "rdm.db").unlink()
|
||||
out = self.svc("rac", "status").stdout
|
||||
|
||||
+18
-7
@@ -12,9 +12,13 @@ import os
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
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]
|
||||
|
||||
_spec = importlib.util.spec_from_file_location("tsr", SOURCE / "tests" / "test_svc_recover.py")
|
||||
@@ -106,7 +110,8 @@ class U2ReleaseTests(tsi.IdentityTests):
|
||||
clock.chmod(0o755)
|
||||
self.env["PATH"] = str(clock_bin) + os.pathsep + self.env["PATH"]
|
||||
self.make_src_repo() # 预克隆 srccache(本用例走 --from local,仅保环境一致)
|
||||
self.seed_agent("u2a", 9461)
|
||||
port = free_test_port()
|
||||
self.seed_agent("u2a", port)
|
||||
self.start_ok("u2a") # legacy 运行中(期望态=running)
|
||||
self.assertTrue(self.running("u2a"))
|
||||
|
||||
@@ -147,7 +152,8 @@ class U2ReleaseTests(tsi.IdentityTests):
|
||||
# ---- T2:构建失败 → 旧服务保持运行、current 不动 ----
|
||||
|
||||
def test_U2_build_failure_keeps_old_running(self):
|
||||
self.seed_agent("u2b", 9462)
|
||||
port = free_test_port()
|
||||
self.seed_agent("u2b", port)
|
||||
self.set_makefile(self.stub)
|
||||
self.svc("u2b", "deploy", "--from", "local")
|
||||
cur1 = self.cur_release("u2b")
|
||||
@@ -164,7 +170,8 @@ class U2ReleaseTests(tsi.IdentityTests):
|
||||
# ---- T3:中断切换(journal switched)→ start 入口回退并恢复 ----
|
||||
|
||||
def test_U2_journal_interrupt_resolved_on_start(self):
|
||||
self.seed_agent("u2c", 9463)
|
||||
port = free_test_port()
|
||||
self.seed_agent("u2c", port)
|
||||
self.set_makefile(self.stub)
|
||||
self.svc("u2c", "deploy", "--from", "local")
|
||||
v1 = self.cur_release("u2c")
|
||||
@@ -190,7 +197,8 @@ class U2ReleaseTests(tsi.IdentityTests):
|
||||
# ---- T4:schema 前进 → 回退阻断,不触碰业务库 ----
|
||||
|
||||
def test_U2_schema_advance_blocks_rollback(self):
|
||||
self.seed_agent("u2d", 9464)
|
||||
port = free_test_port()
|
||||
self.seed_agent("u2d", port)
|
||||
self.set_makefile(self.stub)
|
||||
self.svc("u2d", "deploy", "--from", "local")
|
||||
v1 = self.cur_release("u2d")
|
||||
@@ -230,7 +238,8 @@ class U2ReleaseTests(tsi.IdentityTests):
|
||||
# ---- T5:legacy 布局未迁移 → 裸 start/restart 仍走原路径(兼容) ----
|
||||
|
||||
def test_U2_fault_after_switch_rolls_back_on_next_start(self):
|
||||
self.seed_agent("u2f", 9466)
|
||||
port = free_test_port()
|
||||
self.seed_agent("u2f", port)
|
||||
self.set_makefile(self.stub)
|
||||
self.svc("u2f", "deploy", "--from", "local")
|
||||
v1 = self.cur_release("u2f")
|
||||
@@ -248,7 +257,8 @@ class U2ReleaseTests(tsi.IdentityTests):
|
||||
self.svc("u2f", "stop")
|
||||
|
||||
def test_U2_health_timeout_rolls_back(self):
|
||||
self.seed_agent("u2g", 9467)
|
||||
port = free_test_port()
|
||||
self.seed_agent("u2g", port)
|
||||
self.set_makefile(self.stub)
|
||||
self.svc("u2g", "deploy", "--from", "local")
|
||||
v1 = self.cur_release("u2g")
|
||||
@@ -277,7 +287,8 @@ class U2ReleaseTests(tsi.IdentityTests):
|
||||
return p
|
||||
|
||||
def test_U2_legacy_layout_bare_lifecycle_still_works(self):
|
||||
self.seed_agent("u2e", 9465)
|
||||
port = free_test_port()
|
||||
self.seed_agent("u2e", port)
|
||||
self.start_ok("u2e")
|
||||
self.assertFalse((self.runtime("u2e") / "current").exists())
|
||||
self.assertTrue((self.runtime("u2e") / "bin" / "agent-system").exists())
|
||||
|
||||
@@ -9,9 +9,13 @@ import importlib.util
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
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]
|
||||
|
||||
_spec = importlib.util.spec_from_file_location("tsr", SOURCE / "tests" / "test_svc_recover.py")
|
||||
@@ -57,7 +61,8 @@ class UserBindingTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- T1 合法 manifest:user list 绑定 data_root + --open-existing ----
|
||||
|
||||
def test_T1_user_list_binds_manifest_data(self):
|
||||
self.seed_agent("ub1", 9441)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ub1", port)
|
||||
self.start_ok("ub1")
|
||||
self.svc("ub1", "stop")
|
||||
root = self.manifest("ub1")["identity"]["data_root"]
|
||||
@@ -74,7 +79,8 @@ class UserBindingTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- T2 禁止覆盖 --data;未绑定默认目录拒绝 ----
|
||||
|
||||
def test_T2_reject_override_data_flag(self):
|
||||
self.seed_agent("ub2", 9442)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ub2", port)
|
||||
self.start_ok("ub2")
|
||||
self.svc("ub2", "stop")
|
||||
self.touch_db("ub2")
|
||||
@@ -106,7 +112,8 @@ class UserBindingTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- T3 删除 data 根:OpenExisting 失败,不建空库 ----
|
||||
|
||||
def test_T3_missing_data_root_rejected(self):
|
||||
self.seed_agent("ub3", 9444)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ub3", port)
|
||||
self.start_ok("ub3")
|
||||
self.svc("ub3", "stop")
|
||||
root = Path(self.manifest("ub3")["identity"]["data_root"])
|
||||
@@ -124,7 +131,8 @@ class UserBindingTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- T4 隐藏 flock:user/deploy 拒绝;已运行服务仍在 ----
|
||||
|
||||
def test_T4_no_flock_rejects_user_and_deploy(self):
|
||||
self.seed_agent("ub4", 9445)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ub4", port)
|
||||
self.start_ok("ub4")
|
||||
self.assertTrue(self.running("ub4"))
|
||||
self.touch_db("ub4")
|
||||
@@ -158,7 +166,8 @@ class UserBindingTests(tsr.SvcAutoRecoverTests):
|
||||
# ---- T5 recovery U1 身份冒烟(完整套件见 test_svc_identity)----
|
||||
|
||||
def test_T5_identity_regression_smoke(self):
|
||||
self.seed_agent("ub5", 9446)
|
||||
port = free_test_port()
|
||||
self.seed_agent("ub5", port)
|
||||
self.start_ok("ub5")
|
||||
iid = self.manifest("ub5")["instance_id"]
|
||||
self.svc("ub5", "stop")
|
||||
|
||||
Reference in New Issue
Block a user