Merge authoritative main: take the newer free-port test helpers
This commit is contained in:
@@ -443,16 +443,44 @@ jobs:
|
||||
git -C rdm submodule update --init --recursive
|
||||
done
|
||||
echo "RDM commit: $(git -C rdm rev-parse HEAD)"
|
||||
# 凭据保留到构建循环内的 submodule update;make 经 without_creds
|
||||
# 剥离,避免 token 进入构建进程。不写凭据文件、不把 token 放进命令行。
|
||||
without_creds() {
|
||||
env -u GIT_CONFIG_COUNT \
|
||||
-u GIT_CONFIG_KEY_0 -u GIT_CONFIG_VALUE_0 \
|
||||
-u GIT_CONFIG_KEY_1 -u GIT_CONFIG_VALUE_1 \
|
||||
-u GIT_CONFIG_KEY_2 -u GIT_CONFIG_VALUE_2 \
|
||||
-u GIT_CONFIG_KEY_3 -u GIT_CONFIG_VALUE_3 \
|
||||
-u SUBMODULE_TOKEN \
|
||||
"$@"
|
||||
}
|
||||
: > rdm/pin-build-digests.txt
|
||||
for rev in "${pins[@]}"; do
|
||||
git -C rdm checkout --detach "$rev"
|
||||
git -C rdm submodule update --init --recursive
|
||||
# 不能用「有无 go.mod」判定落地:pin 集合混有 Go 仓与非 Go 仓
|
||||
#(例如前端仓根目录没有 go.mod)。真正要保证的是每个 pins/*
|
||||
# gitlink 子模块 HEAD 等于记录的提交,且工作树已有已跟踪文件——
|
||||
# 否则 Go 可能静默回退到远端解析。
|
||||
while read -r mode _type gitlink path; do
|
||||
[ "$mode" = "160000" ] || continue
|
||||
head="$(git -C rdm -C "$path" rev-parse HEAD)"
|
||||
if [ "$head" != "$gitlink" ]; then
|
||||
echo "rdm pin ${rev} 的 ${path} 未落地:HEAD 不等于 gitlink 提交" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$(git -C rdm -C "$path" ls-files)" ]; then
|
||||
echo "rdm pin ${rev} 的 ${path} 未落地:工作树无已跟踪文件" >&2
|
||||
exit 1
|
||||
fi
|
||||
done < <(git -C rdm ls-tree "$rev" pins/)
|
||||
without_creds make -C rdm build
|
||||
sha256sum rdm/bin/agent-system-rdm | awk -v rev="$rev" '{print rev, $1}' >> rdm/pin-build-digests.txt
|
||||
done
|
||||
unset GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0 \
|
||||
GIT_CONFIG_KEY_1 GIT_CONFIG_VALUE_1 GIT_CONFIG_KEY_2 \
|
||||
GIT_CONFIG_VALUE_2 GIT_CONFIG_KEY_3 GIT_CONFIG_VALUE_3
|
||||
unset SUBMODULE_TOKEN
|
||||
: > rdm/pin-build-digests.txt
|
||||
for rev in "${pins[@]}"; do
|
||||
git -C rdm checkout --detach "$rev"
|
||||
make -C rdm build
|
||||
sha256sum rdm/bin/agent-system-rdm | awk -v rev="$rev" '{print rev, $1}' >> rdm/pin-build-digests.txt
|
||||
done
|
||||
- name: Install Node 22
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
# feature/agent-stack-audit-remediation-b1 任务卡
|
||||
|
||||
任务主记录:PR #685 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/685;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= d9e9c0560a2767539f4415b579adbeff86a22ba6
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-agent-stack-audit-remediation-b1(worktree);分支:feature/agent-stack-audit-remediation-b1
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=14019d085070900db07365351975abf0915c3b1fc34ef0d05c85fe975c9ac834;triage_receipt=/tmp/triage-remediation-b1-1792595736.json;receipt_sha256=0de1f5ad5737d0839f79d7098faced9f6f9366ca1a17486083b8439749dea27e
|
||||
|
||||
- 授权与交付终点:用户已批准按方案实施第一批(U1-U7、U10)。范围内连续推进:agent-per-unit 逐单元实施、host 权威验证、组件分支提交、Backend 嵌套 gitlink 与 Dev 组合指针更新、组合验证、独立 Review、PR/CI/合入。终点为第一批全部单元收口。未授权:部署、真实数据迁移、发布、第二批 U8/U9/U11、功能增量 A1-A4、A2A 互通。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:组合验证与交付;当前增量:第一批 U1-U7、U10 全部实施完成
|
||||
- host 记录:为使 U7 可按 agent-per-unit 执行,deps/agent-command 缺少组件级 AGENT-EXECUTION.md(其余四个目标仓均有且 validate: ok),host 按 agent-intent 同版模板补齐并以执行配置身份提交到该组件任务分支;理由与差异记录于本卡。
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
**目标**:按已批准方案(docs/superpowers/plans/2026-10-21-agent-stack-audit-remediation-plan.md,基线 d9e9c0560a)修复第一批 F01-F10、F13:执行隔离(U1/U2)、失败结果真实语义与恢复安全(U3)、权限缓存版本化(U4)、意图快照与生成校验(U5)、Backend 意图受理(U6)、命令隔离与回执(U7)、Backend 用户/项目配置路由(U10)。每单元以原触发条件回归阻断为验收,正常路径不退化。
|
||||
|
||||
**非目标**:U8/U9/U11(第二批);A1-A4 功能增量;A2A 互通(F16);全库接口替换、幂等平台、分布式执行;部署/迁移/发布;不把工作区路径检查宣称为 OS 沙箱。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
单元包由 host 写于 /tmp/agent-stack-b1/<unit>.json(network=false、external_cost=false 均为声明);波次:A=U1(native)+U3(squad)+U5(intent) 并行;B=U2(native)+U4(squad)+U10(backend);C=U7a(command)+U6(backend,依赖 U5 嵌套指针);D=U7b(backend cmdbus,依赖 U7a)。同仓单元串行 apply。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| agent-stack-b1-u1-native-workspace | native: adapters/native/backend.go+test, runtime/tools/{exec,jail,atomic}.go+test | digest 见 /tmp/agent-stack-b1/u1-run.json | implementation | [cursor, antigravity, pi-glm, pi, codex] | 1 | cursor-agent-stable | success | refs/agent-exec/e10eca84…/18a9b3caba61 → native 81e5bf3(transport 273213a 重写为规范提交) | 否 | /tmp/agent-stack-b1/u1-run.json |
|
||||
| agent-stack-b1-u5-intent-snapshot-generated | intent: intent/(fold/registry/catalog/recognizer/slots/generator+tests) | digest 见 /tmp/agent-stack-b1/u5-run.json | implementation | 同上 | 1 | cursor-agent-stable | success | refs/agent-exec/357b5d70…/90a2c1b0488f → intent 765a7e7 | 否 | /tmp/agent-stack-b1/u5-run.json |
|
||||
| agent-stack-b1-u7a-command-isolation | command: registry.go/result.go/conformance/+tests | digest 见 /tmp/agent-stack-b1/u7a-run.json | implementation | 同上 | 1 | cursor-agent-stable | success | refs/agent-exec/243aef44…/2e0a169b7174 → command fe7987d | 否 | /tmp/agent-stack-b1/u7a-run.json |
|
||||
| u2-native-session-contract | native: contract.go/backend.go/session/tree.go/tools+regression tests | digest 见 /tmp/agent-stack-b1/u2-run.json | implementation | 同上 | 1 | cursor-agent-stable | success | transport f7a3ff39 重写为规范提交(基于 81e5bf3) | 否 | /tmp/agent-stack-b1/u2-run.json |
|
||||
| u4-squad-resolver-config | squad: squad/backend.go+config_identity.go, adapters/native/register.go+tests | digest 见 /tmp/agent-stack-b1/u4-run.json | implementation | 同上 | 1 | cursor-agent-stable | success | squad 3a98d873 → aa15a5d 规范提交 | 否 | /tmp/agent-stack-b1/u4-run.json |
|
||||
| u10-backend-scoped-config(首次) | backend: internal/app|runtime|squadadapter | digest 见 /tmp/agent-stack-b1/u10-run.json | implementation | [pi, codex](cursor/agy 临时禁用) | 1 | pi MiniMax-M3 | failed-unknown(fail-stop:退出码 0 但无有效终态记录) | 无 transport,不采用 | 否 | /tmp/agent-stack-b1/u10-run.json;现场保留 /tmp/agent-exec-1000/b627ce2c41c90bea0d9f78270819d419 待对账 |
|
||||
| u10-backend-scoped-config(重派) | 同上;包修正:allowed_paths 补嵌套 deps gitlink,禁止 -modfile 变通 | digest 见 /tmp/agent-stack-b1/u10b-run.json | implementation | [pi, codex] | 1 | pi MiniMax-M3 | success | transport 4c08cd8 → backend 80faf3e 规范提交 | 否 | /tmp/agent-stack-b1/u10b-run.json |
|
||||
| u3a-native-result-fidelity | native: exec/、adapters/native/、runtime/loop.go | digest 见 /tmp/agent-stack-b1/u3a-run.json | implementation | 同上 | 1 | cursor-agent-stable | success | transport bf3e151 → native 5c0d2d8 规范提交 | 否 | /tmp/agent-stack-b1/u3a-run.json |
|
||||
| (随 U10 重派)嵌套 agent-intent 指针 765a7e7 | backend deps/agent-intent bump | host 提交 406cffd | host | — | — | host | done | 406cffd | host 直接提交(gitlink 集成) | — | — |
|
||||
| u6-backend-intent-admission | backend: intentgate/route/server/workflow | digest 见 /tmp/agent-stack-b1/u6-run.json | implementation | [pi, codex] | 1 | pi MiniMax-M3 | success | transport 0dff5ee → backend 0e55875 规范提交 | 否 | /tmp/agent-stack-b1/u6-run.json |
|
||||
| (随 U6)嵌套 agent-command 指针 fe7987d | backend deps/agent-command bump | host 提交 e8d0c9b | host | — | — | host | done | e8d0c9b | host 直接提交(gitlink 集成) | — | — |
|
||||
| u3b-squad-result-recovery(首次) | squad: squad/、adapters/native/ | digest 见 /tmp/agent-stack-b1/u3b-run.json | implementation | 同上 | 1 | cursor-agent-stable | failed-unknown(fail-stop;cursor TLS 会话前故障,553B 日志、零改动,现场 /tmp/agent-exec-1000/287d14d44c37c0336519417b92c5cc5a 保留) | 无 | 否 | /tmp/agent-stack-b1/u3b-run.json;对账:日志证明未建立会话、workspace 零改动,环境瞬断,重派 |
|
||||
| u3b-squad-result-recovery(重派) | 同上 | digest 见 /tmp/agent-stack-b1/u3b2-run.json | implementation | [cursor(后禁用), antigravity(后禁用), pi, codex] | 2 | cursor-agent-stable | failed-unknown("Cannot use this model: auto",48B 日志、零改动;与已记录配额耗尽一致)→ 禁用 cursor(squad 4e65577) | 无 | 否 | /tmp/agent-stack-b1/u3b2-run.json;现场 /tmp/agent-exec-1000/b7ed6a71… |
|
||||
| u3b(第 3 次) | 同上 | digest 见 /tmp/agent-stack-b1/u3b3-run.json | implementation | [antigravity, pi, codex] | 1 | agy gemini | failed-unknown(400 FAILED_PRECONDITION 地区限制,0 token、零改动)→ 禁用 agy(squad 后续提交) | 无 | 否 | /tmp/agent-stack-b1/u3b3-run.json;现场 /tmp/agent-exec-1000/8fd18fc9… |
|
||||
| u7b-backend-receipts | backend: internal/cmdbus、internal/server | digest 见 /tmp/agent-stack-b1/u7b-run.json | implementation | [pi, codex] | 1 | pi MiniMax-M3 | success | transport 3a2b548 → backend 4030449 规范提交 | 否 | /tmp/agent-stack-b1/u7b-run.json |
|
||||
| u3b(第 4 次) | 同上 | digest 见 /tmp/agent-stack-b1/u3b4-run.json | implementation | [pi, codex] | 1 | pi MiniMax-M3 | success | transport 5a00e3c → squad 23f161b 规范提交 | 否 | /tmp/agent-stack-b1/u3b4-run.json |
|
||||
| u6-repair-boundary | backend: server 测试改经 intentgate | digest 见 /tmp/agent-stack-b1/u6r-run.json | repair | [pi, codex] | 1 | pi MiniMax-M3 | success | backend 49c1259 | 否 | /tmp/agent-stack-b1/u6r-run.json |
|
||||
| u3b-repair-sqlite-test(首次) | squad: adapters/sqlite 测试 | digest 见 /tmp/agent-stack-b1/u3br-run.json | repair | 同上 | 1 | pi MiniMax-M3 | blocked(实现完成但验证环境缺嵌套 deps,包修正后重派;现场保留 b0f0f936…) | 无 | 否 | /tmp/agent-stack-b1/u3br-run.json |
|
||||
| u3b-repair-sqlite-test(重派) | 同上;包补嵌套 deps | digest 见 /tmp/agent-stack-b1/u3br2-run.json | repair | [pi, codex] | 1 | pi MiniMax-M3 | success | squad 0f1daa8 | 否 | /tmp/agent-stack-b1/u3br2-run.json |
|
||||
| gitlink-integration 1-4 | Dev 顶层/嵌套指针级联对齐 | host 集成 | implementation | [cursor,…] | 各 1 | cursor / antigravity? / pi | success×4 | 76766356/d31c7b55/3ae7605f/647fa51c | 否 | /tmp/agent-stack-b1/gitlink{,2,3,4}-run.json |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:待核对(无适用规划时说明原因)
|
||||
- 主记录路径/版本:待核对(路径按仓库根目录定位,归档后不变;只引用唯一正文)
|
||||
- 对应里程碑:待核对
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1-R7 | 方案 Requirements 节(执行与权限/输入恢复可核对性) | 与方案一致,逐条映射到 U1-U7、U10 | P1 | 方案各单元验收行 | U2←U1,U6←U5,U7b←U7a | 已确认 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:docs/superpowers/plans/2026-10-21-agent-stack-audit-remediation-plan.md(本分支 d3161347,审计基线 d9e9c0560a)
|
||||
- 实施计划引用:本卡「Agent 实施收据」节前导说明(波次与依赖)
|
||||
- 测试计划引用:方案 Verification Contract;各单元先用 F-ID 触发条件做最小回归,最终候选执行组合验证
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 本卡 | 已完成 | host | 2026-10-21 | 用户批准第一批 |
|
||||
| 需求评估 | 分流收据 | 已完成 | host | 2026-10-21 | verdict=NEW |
|
||||
| 需求表确认 | 本卡 R1-R7 | 已完成 | host | 2026-10-21 | 方案 Requirements |
|
||||
| 方案设计 | 方案文档 d3161347 | 已完成 | 前序会话 | 2026-10-21 | F01-F16 |
|
||||
| 实施计划 | 本卡波次表 | 已完成 | host | 2026-10-21 | U1-U7/U10 依赖 |
|
||||
| 实施与自检 | 全部 10 单元 + 2 修复完成;组合 make test PASS(verify-combo-make-test4.json exit=0) | 已完成 | agent-per-unit + host 验证 | 2026-10-21 | verify-*.json 全部 exit=0 |
|
||||
| 正式测试与验收 | Dev 76766356→647fa51c 组合 | PASS | host | 2026-10-21 | make test(worktree/governance/boundaries/pins/libs/core/backend/frontend/ui/agentcmd) |
|
||||
| 代码 Review | 待定 | 待执行 | — | — | — |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | d9e9c0560a2767539f4415b579adbeff86a22ba6 | d9e9c0560a2767539f4415b579adbeff86a22ba6 | 2026-10-21T15:17:41Z |
|
||||
|
||||
## 证据(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 记录:独立 CLI 评审单元 agent-stack-b1-independent-review(pi-glm attempt1 success,收据 /tmp/agent-stack-b1/review4-run.json);结论 APPROVE(0 blocker / 0 major / 5 minor 非阻塞跟进项);报告 .task/reviews/2026-10-21-agent-stack-b1-independent-review.md;被审对象 d55e57dd vs main b25846d2
|
||||
- 冲突记录:NOT_RUN(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- CI 记录:f23d1df7 首 轮三 job Checkout 失败——五个组件 pin 仅存本地;已推送组件分支 fix/agent-stack-audit-b1 至各自 origin(native ac91b84 / squad 0f1daa8 / intent 765a7e7 / command fe7987d / backend b614180),本提交重触发 CI
|
||||
- 合入记录: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-21T15:16:36.646637+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "d9e9c0560a2767539f4415b579adbeff86a22ba6",
|
||||
"after_head": "d9e9c0560a2767539f4415b579adbeff86a22ba6",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "d9e9c0560a2767539f4415b579adbeff86a22ba6",
|
||||
"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=d9e9c0560a",
|
||||
"[remote] origin/main=d9e9c0560a ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[recycle] merged un-recycled branches: fix/ci-rdm-pin-materialization",
|
||||
"[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] d9e9c0560a(完整:d9e9c0560a2767539f4415b579adbeff86a22ba6)",
|
||||
"[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": "774b7616a7c39f1933001f482966329eebfb9b7ba3983a29959d38392265dac6",
|
||||
"receipt_sha256": "282b6b21a2d7d06db1ac80d250f73d0ddfe9dedc917ac12c83a2af0e5e692e9b",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/282b6b21a2d7d06db1ac80d250f73d0ddfe9dedc917ac12c83a2af0e5e692e9b.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #685(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/685)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,168 @@
|
||||
# feature/fast-slow-o2-a01-evidence-design 任务卡
|
||||
|
||||
任务主记录:PR #662 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/662;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 556201541c9a98f34fdad860cfad886af069279d
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-fast-slow-o2-a01-evidence-design(worktree);分支:feature/fast-slow-o2-a01-evidence-design
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=df1a6812c2cf304f074b3d29139cfafd427960f9720935b99497ab67e5d2a646;triage_receipt=/tmp/o2a01d-20261020/triage.json;receipt_sha256=2828ea8adadd8f90cb348a28a7c750dfa85dc5a91f8d3e6877decdbed120ea27
|
||||
|
||||
- 授权与交付终点:用户 2026-10-20 选定“立项:出设计和资源提案,方向是收紧说明并让评测允许修正一次(推荐,先零付费)”。本任务只交付方案、实施计划(入仓)与源码外资源提案,经 PR 合入 Dev 后请用户审阅。**未授权**:产品代码改动、派遣实施 worker、任何付费请求或 A01 复验、实例系统层配置写入、部署。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:方案与计划已成稿,待合入后人审;当前增量:D1(设计文档,无实施单元)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
**目标**:针对 O2 P1 第二段 A01 被核心校验拒绝(`checkpoint.evidence: direct relation cannot have result`)后评测立即停止的问题,产出零付费的方案与实施计划:收紧 `publish_work_checkpoint` 工具说明/字段说明与材料任务提示,让模型按 mode 填对 evidence;评测把“首次被拒即停”改为“累计 2 次即停”;并提出一次 A01 付费复验的资源提案。
|
||||
|
||||
**非目标**:不改 core 校验器与宿主证据归一语义;不改 schema 结构(不引入 `oneOf`);不改路由契约/传输层;不实施、不付费、不写实例配置、不部署。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 无(本任务仅 host 豁免路径:`.task/**`、`docs/superpowers/{specs,plans}/**`) | — | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | NOT_RUN | — |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:适用——快慢通道 O2 后续(O2 方案 `docs/superpowers/specs/2026-10-19-fast-slow-o2-route-contract-eof-design.md`;P1 结论见 `.task/archive/feature-fast-slow-o2-p1.md`)
|
||||
- 主记录路径/版本:本卡;方案与计划见下
|
||||
- 对应里程碑:快慢通道 O2 收口(A01 真实路由通过)
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 方案:定位 A01 被拒根因并给出说明收紧与评测修正一次的设计 | 符合:用户 2026-10-20 选定 design_both;根因由离线重放(SELF)确认 | P1 | 方案文档入仓,含根因、改动、非目标、替代方案、判定表 | P1 结论 | 已成稿 |
|
||||
| R2 | 实施与测试计划:单元范围、允许路径、离线测试与停止条件 | 符合:policy/delivery.md 计划阶段 | P1 | 计划文档入仓,测试全部零付费 | R1 | 已成稿 |
|
||||
| R3 | 一次 A01 付费复验资源提案(≤60,000 tokens) | 符合:付费须单独批准 | P2 | 源码外提案就位并请用户批准 | R1、R2 | 已成稿,待批准 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:`docs/superpowers/specs/2026-10-20-fast-slow-o2-a01-evidence-design.md`(sha256 前缀 d99475c4ae6741ee;Backend 依据 74e5396)
|
||||
- 实施计划引用:`docs/superpowers/plans/2026-10-20-fast-slow-o2-a01-evidence-plan.md`(sha256 前缀 5925f66ae205d6d2;增量 I1 Backend、I2 Dev 改钉、P2 付费复验)
|
||||
- 测试计划引用:同计划 §2 T1–T9(全部离线;eval/live 用 -tags live 单独运行)
|
||||
- 资源提案:`~/.local/share/agent-system-evidence/fast-slow-o2-a01e-20261020/resource-proposal.md`(源码外,sha256 前缀 bd00293f0b8240e7)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | R1–R3 | 完成 | host | 2026-10-20 | 用户选定 design_both |
|
||||
| 需求评估 | R1–R3 | 完成 | host | 2026-10-20 | 零付费调查与离线重放(SELF) |
|
||||
| 需求表确认 | R1–R3 | 完成 | 用户 | 2026-10-20 | AskQuestion a01_next=design_both |
|
||||
| 方案设计 | d99475c4ae6741ee | 成稿,待人审 | host | 2026-10-20 | 方案文档 |
|
||||
| 实施计划 | 5925f66ae205d6d2 | 成稿,待人审 | host | 2026-10-20 | 计划文档 |
|
||||
| 实施与自检 | — | 不适用(未授权实施) | — | — | 授权与交付终点 |
|
||||
| 正式测试与验收 | — | 不适用(仅文档) | — | — | — |
|
||||
| 代码 Review | 6db2d614 | PASS(第 2 轮) | code-reviewer 子智能体(fresh session) | 2026-10-20 | 见合入与清理节 Review 记录 |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| D0 离线重放 | A01 被拒源于 finding 0 在 direct_relation 下混入 total/phase 前提与 result | `/tmp/o2a01-replay`,同参数 + core 当前版本 | 重放能复现拒绝原因即止 | 零付费只读调查 | 复现:整检查点被拒;去掉 evidence 后通过(SELF) | 支撑方案 §2 根因 | 否 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 556201541c9a98f34fdad860cfad886af069279d | 556201541c9a98f34fdad860cfad886af069279d | 2026-10-20T14:12:47Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:未入队;调度记录:无
|
||||
- entry_id:无;attempt_id:无;窗口执行者:无
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:方案、实施计划入仓;源码外资源提案就位;均待用户审阅。
|
||||
- 验证:文档任务,无代码测试(NOT_RUN 不适用);PR CI 为准(`make test`+`make build`)。
|
||||
- 影响与剩余项:仅新增两份文档与本卡;产品行为不变。
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:O2 后续设计完成;剩余 I1/I2 实施与 P2 付费复验。
|
||||
- 首选下一任务:按计划实施 I1(Backend 单元 `o2a01e-i1-backend-evidence-guidance`)与 I2(Dev 改钉)
|
||||
- 推荐依据:零付费、范围限于 Backend 文案与评测代码,是付费复验的前置
|
||||
- 前置条件/待确认:用户批准方案与计划;P2 另需批准资源提案并重新放置路由文件
|
||||
- 预期产物/验收条件:计划 §2 T1–T9 通过、Backend 与 Dev PR 当前 HEAD CI 通过并合入
|
||||
- 重复任务核对:无在途同题任务或 PR(task-triage verdict=NEW)
|
||||
- 启动授权:未授权(待用户审阅)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:
|
||||
- 第 1 轮:Cursor code-reviewer 子智能体(fresh_session=true,session `8826f456`,只读),base 556201541c / head 9c6c2f0,结论 BLOCK。B1:方案让分析类使用 `direct_relation`,但宿主仅对单一来源完整原句的 `material_record` 保留,其余转待确认;B2:`eval/live` 带 `live` 构建标签,计划验证命令会失败且 CI 不覆盖。host 复核两项属实(`material_evidence.go:215-227`;`go list ./eval/live/` exit 1),已修订方案 §2/§3.1/§3.2 与计划 §2(允许路径、T1/T4/T5、新增 T8/T9、验证命令);非阻塞建议 1–5 一并吸收。
|
||||
- 第 2 轮:Cursor code-reviewer 子智能体(fresh_session=true,新 session `c9f1fce3`,只读,未转交第 1 轮会话),head 6db2d614,结论 PASS;B1/B2 确认闭合,审阅者重放 `go test -tags live -run '^TestFollowupA01KeepsItsOwnOneShotResult$' ./eval/live/` 与 `go vet -tags live ./eval/live/` 通过(REPLAY,Backend 74e5396)。3 条非阻塞提示(T5 夹具同缺 `}`、无 evidence 的 `needs_user` 原文被替换、T3 比较基准)已作为澄清写入文档,未改变方案结论。
|
||||
- 冲突记录:task-sync 合入 main 7e19cf3(#661 卡闭合),无冲突;本变更仅文档与任务卡,无组件组合影响。
|
||||
- CI 记录:以 PR #662 合入时 HEAD 的必需检查为准(GATE,`make ci-wait SHA=<HEAD>` + 合入脚本核验 commit status=success);HEAD 与运行结果见窗口释放评论(Agent-System-Dev#518)。
|
||||
- 合入记录:授权范围为本卡“授权与交付终点”(仅文档);PR #662 合入 main,被核验 HEAD 与 merge commit 见窗口释放评论(Agent-System-Dev#518)。
|
||||
- 清理记录:合入后核对写者退出,于任务 worktree 运行 `TASK_WRITERS_STOPPED=1 make task-finish`;本卡随本 PR 闭合归档。
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T13:52:28.231377+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "556201541c9a98f34fdad860cfad886af069279d",
|
||||
"after_head": "556201541c9a98f34fdad860cfad886af069279d",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "556201541c9a98f34fdad860cfad886af069279d",
|
||||
"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=556201541c",
|
||||
"[remote] origin/main=556201541c ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=3",
|
||||
"[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] 556201541c(完整:556201541c9a98f34fdad860cfad886af069279d)",
|
||||
"[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": "b9fa778a80fdbf2d33925aa16abddd250afd5dee536104a503c73e7e540bd62f",
|
||||
"receipt_sha256": "2a13d032916edd33cee13cf74004a0f5feeabc71fa751e68d738adc425297196",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/2a13d032916edd33cee13cf74004a0f5feeabc71fa751e68d738adc425297196.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 7e19cf3e182b566bdc48a7e690e44ff11b4ab33c | 25d22faae7726176c16ca4bcdf5ff0094af404a2 | 2026-10-20T14:24:43Z |
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #662(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/662)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | ed3847c85ce111a74f0cb100908abfb40f62db07 | 398e8e6ff91eb5109878f69c6a2ca6ef7c174513 | 2026-10-20T14:46:15Z |
|
||||
| 同步(记录前对象) | 6945b6701f7579bf4e31def283825e392c33ad16 | 316e087262219272af5f74a5f6758cc8e82919f2 | 2026-10-20T15:16:46Z |
|
||||
@@ -0,0 +1,166 @@
|
||||
# feature/fast-slow-o2-a01-evidence-impl 任务卡
|
||||
|
||||
任务主记录:PR #665 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/665;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 1850d03563875fd848664ab9b348f6858d0fa590
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-fast-slow-o2-a01-evidence-impl(worktree);分支:feature/fast-slow-o2-a01-evidence-impl
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=d57eef77021d9143f3489bfbc5128eca76d1dcedf35a6751c6942a2228e7d824;triage_receipt=/tmp/o2a01e-20261020/triage.json;receipt_sha256=d6ace7e0bd056b2d9e9583a39a4a07704ddb6bc3950216d0fab09a64e5f3bfd5
|
||||
|
||||
- 授权与交付终点:用户 2026-10-20「按顺序继续」批准实施已合入方案/计划(Dev #662)的零付费部分:I1 Backend(单元 o2a01e-i1)+ I2 Dev 改钉。终点:两 PR 合入。**未授权**:付费 A01/P2、实例系统层配置写入、部署、改 core 校验器或 schema 结构、软降级语义。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:合入准备;当前增量:I2 已落地,待 Dev PR
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
**目标**:按 `docs/superpowers/specs/2026-10-20-fast-slow-o2-a01-evidence-design.md` 与计划实施零付费 I1/I2:收紧 `publish_work_checkpoint` 工具说明/字段说明与材料任务提示;评测允许一次检查点修正(累计 2 次被拒才停);新标签 `approved-o2-a01-evidence`;Dev 前移 `modules/backend`。
|
||||
|
||||
**非目标**:不改 core/`normalizeMaterialEvidence`/schema 结构;不付费;不写实例配置;不部署。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| o2a01e-i1-backend-evidence-guidance | Backend:说明/提示/评测修正一次/新标签(计划 §2) | network=false external_cost=false | implementation | Cursor→agy→pi-glm→pi→Codex | 1 | cursor | success | refs/agent-exec/…/1f4b762f53be → 837480d | never | /tmp/o2a01e-20261020/o2a01e-i1-receipt.json |
|
||||
| o2a01e-i2-dev-backend-pin | Dev:modules/backend → dfb31da(Backend #232) | network=false external_cost=false | implementation | Cursor→agy→pi-glm→pi→Codex | 1 | cursor | success | refs/agent-exec/…/6daa6027b4f6 → ae1b5a7 | never | /tmp/o2a01e-20261020/o2a01e-i2-receipt.json |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:适用——O2 A01 证据合规方案(Dev #662 已合入)
|
||||
- 主记录路径/版本:docs/superpowers/specs/2026-10-20-fast-slow-o2-a01-evidence-design.md;计划 docs/superpowers/plans/2026-10-20-fast-slow-o2-a01-evidence-plan.md
|
||||
- 对应里程碑:快慢通道 O2 收口(A01)
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | I1 Backend:说明收紧 + 评测允许修正一次 + 新标签 | 符合已批准方案 §3 | P1 | 计划 T1–T9 通过;Backend PR CI 通过并合入 | 方案 #662 | 实施中 |
|
||||
| R2 | I2 Dev:modules/backend 改钉 | 符合计划 §3 | P1 | Dev PR CI 通过并合入 | R1 | 待启动 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:docs/superpowers/specs/2026-10-20-fast-slow-o2-a01-evidence-design.md(main 经 #662)
|
||||
- 实施计划引用:docs/superpowers/plans/2026-10-20-fast-slow-o2-a01-evidence-plan.md(I1/I2;R1/R2)
|
||||
- 测试计划引用:同计划 §2 T1–T9
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 待定 | 待执行 | — | — | — |
|
||||
| 需求评估 | 待定 | 待执行 | — | — | — |
|
||||
| 需求表确认 | 待定 | 待执行 | — | — | — |
|
||||
| 方案设计 | 待定 | 待执行 | — | — | — |
|
||||
| 实施计划 | 待定 | 待执行 | — | — | — |
|
||||
| 实施与自检 | 待定 | 待执行 | — | — | — |
|
||||
| 正式测试与验收 | 待定 | 待执行 | — | — | — |
|
||||
| 代码 Review | 待定 | 待执行 | — | — | — |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 1850d03563875fd848664ab9b348f6858d0fa590 | 1850d03563875fd848664ab9b348f6858d0fa590 | 2026-10-20T16:07:06Z |
|
||||
|
||||
## 证据(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 记录:
|
||||
- I1 Backend:Cursor code-reviewer(fresh_session,session `d661aa77`),base 689e176 / head 837480d,VERDICT PASS;host 权威验证 `go test ./...`+`go vet ./...` exit 0(SELF,含 live 标签用例)。
|
||||
- I2 Dev pin:gitlink-only(ae1b5a7),modules/backend=dfb31da20ab0276ea8e167cf26675f7ac95a1b26。
|
||||
- 冲突记录:合入前 task-sync;本变更仅为 Backend gitlink + 任务卡。
|
||||
- CI 记录:以 PR #665 合入时 HEAD 的必需检查为准(GATE,`make ci-wait`);结果见 #518 窗口释放评论。
|
||||
- 合入记录:授权为零付费 I1/I2;PR #665 合入 main;HEAD/merge 见 #518 窗口释放评论。
|
||||
- 清理记录:合入后 `TASK_WRITERS_STOPPED=1 make task-finish`;本卡随本 PR 闭合归档;Backend worktree 另回收。
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T16:03:55.764808+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "1850d03563875fd848664ab9b348f6858d0fa590",
|
||||
"after_head": "1850d03563875fd848664ab9b348f6858d0fa590",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "1850d03563875fd848664ab9b348f6858d0fa590",
|
||||
"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=1850d03563",
|
||||
"[remote] origin/main=1850d03563 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[recycle] merged un-recycled branches: feature/project-access-lifecycle",
|
||||
"[cards] 已合入未闭合任务卡:feature-project-access-lifecycle.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] 1850d03563(完整:1850d03563875fd848664ab9b348f6858d0fa590)",
|
||||
"[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": "72d16b5a72d8b99b286fc4e43a1d333ddd240b6afdd0044f65956f2c16bca900",
|
||||
"receipt_sha256": "3b52316fae8aaf072d968fcdf67c507c684c061c7f118cd287551bb766f5a65b",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/3b52316fae8aaf072d968fcdf67c507c684c061c7f118cd287551bb766f5a65b.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #665(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/665)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,167 @@
|
||||
# feature/fast-slow-o2-a01-final-budget 任务卡
|
||||
|
||||
任务主记录:PR #672 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/672;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 8f59b135ec27fc04e432583d70e1e1d0d6f25ea8
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-fast-slow-o2-a01-final-budget(worktree);分支:feature/fast-slow-o2-a01-final-budget
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=7d0c8fce13eac42b213217ffb149c26e638f3452d600f0e7f8d334626e2f3157;triage_receipt=/tmp/o2p5fix-20261021/triage.json;receipt_sha256=87b0ba24da924f3e98870d36ac77598e1fb7a71d17a2dbdba4799cabf5a78750
|
||||
|
||||
- 授权与交付终点:用户「3」(2026-10-21,P4 后方向 3:两项零付费修正 + 新增评测标签提高 A01 预算与输出上限),随后「继续」;允许零付费 Backend 实施单元、Backend PR/CI/合入、Dev gitlink 改钉 PR/CI/合入,并记录 P4 付费复验结果。未授权:P5 付费复验(须单独批准)、既有评测标签改动、路由契约改动、实例配置写入、部署
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:合入;当前增量:I2 Dev 改钉(I1 已合入 Backend #236)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
目标:消除 P4 暴露的 A01 剩余阻塞——(a) 物料子 Agent 预留估算以「上一轮 prompt + 上一轮 completion」估计下一轮 prompt;(b) 最终答复简明(约 1500 字以内、一次写完);(c) 新评测标签 `approved-o2-a01-final`(预算 45000、输出上限 6000,模型模式下上限端到端生效);记录 P4 付费复验结果。受影响:Backend internal/runtime/material_final_reserve.go、sub_loop.go、internal/runmgr/material.go、internal/server 评测辅助、eval/live A01 映射及测试。
|
||||
|
||||
非目标:不改既有评测标签(含 approved-o2-a01-evidence 的 30000/4000)、路由契约、实例配置;不发起付费请求。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| o2p5fix-u1-backend-final-budget | Backend runtime/runmgr/server 评测辅助、eval/live 及测试(9 文件) | 22fcb8ac80b2 | implementation | 按 AGENT-EXECUTION.md | 1(3e52e52b4541,fresh_session=true,session not-observable) | pi | success | 9a6f0f880f66 | 0 | /tmp/o2p5fix-20261021/u1-receipt.json |
|
||||
| o2p5fix-u2-backend-eval-cap | Review 阻塞修正:评测 provider 包装按标签上限钳制 + subRun 级测试(3 文件) | 22fcb8ac80b2 | implementation | 按 AGENT-EXECUTION.md | 1(ba449849829e,fresh_session=true,session not-observable) | pi | success | aec92b768439 → Backend #236 | 0 | /tmp/o2p5fix-20261021/u2-receipt.json |
|
||||
| o2p5fix-u3-dev-backend-pin | modules/backend gitlink → 241e1b3 | ccceb3bfb61b | implementation | 按 AGENT-EXECUTION.md | 1(1c217de52bca,fresh_session=true,session not-observable) | cursor | success | a8918f0a6449 | 0 | /tmp/o2p5fix-20261021/u3-receipt.json |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:适用(O2 A01 付费通过的前置修正)
|
||||
- 主记录路径/版本:docs/superpowers/specs/2026-10-21-fast-slow-o2-c8-review.md;前序归档卡 .task/archive/feature-fast-slow-o2-final-only-notice-all-tools.md
|
||||
- 对应里程碑:O2 A01 付费复验通过
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 预留估算计入上一轮 completion | 符合:P4 守卫超支 5914 根因 | P0 | 单测:P4 数值(已用 22070、prompt 5810、completion 4000)在 subRun.materialDecision 下判 stop,completion 为 0 时不 stop | 无 | 完成(Backend #236) |
|
||||
| R2 | 最终答复简明(约 1500 字、一次写完) | 符合:P4 最终答复两次被 4000 截断 | P0 | 单测:final-only 提醒与物料任务文本含约束 | 无 | 完成(Backend #236) |
|
||||
| R3 | 新评测标签 approved-o2-a01-final(45000 / 6000) | 符合:两检查点 + 最终答复需更大预算 | P0 | 单测:标签预算 45000、上限 6000,其余标签不变;模型模式路由与 provider 实际 max_tokens 为 6000 | 无 | 完成(Backend #236) |
|
||||
| R4 | 记录 P4 付费复验结果 | 符合 | P0 | 卡内含花费、调用序列、结论与收据 | 无 | 完成(见探索试验) |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:P4 后方向 3(本卡目标);单元说明 /tmp/o2p5fix-20261021/u1.json、u2.json
|
||||
- 实施计划引用:I1 Backend(R1–R3,agent-per-unit,u1 + Review 修正 u2)→ Backend PR/CI/合入 → I2 Dev 改钉 modules/backend(u3)
|
||||
- 测试计划引用:单元 verification(gofmt、vet、runtime/runmgr/server 测试、eval/live A01 离线用例);Backend CI;Dev CI make test+build;P5 付费复验 NOT_RUN(待授权)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 8f59b13 | 完成 | host | 2026-10-21 | 用户「3」 |
|
||||
| 需求评估 | 8f59b13 | 完成 | host | 2026-10-21 | R1–R4 |
|
||||
| 需求表确认 | 8f59b13 | 完成(范围即方向 3) | 用户 | 2026-10-21 | 选项 3 |
|
||||
| 方案设计 | 8f59b13 | 完成(P4 根因 + 方向 3) | host | 2026-10-21 | 本卡探索试验 |
|
||||
| 实施计划 | 0571a75 | 完成 | host | 2026-10-21 | 单元说明 u1/u2/u3.json |
|
||||
| 实施与自检 | Backend 9a6f0f8、aec92b7;Dev a8918f0 | 完成 | pi / cursor(agent-exec) | 2026-10-21 | 收据表;host 复跑 gofmt(改动文件)/vet/runtime+runmgr+server/eval-live A01 全部 ok(SELF) |
|
||||
| 正式测试与验收 | Backend aec92b7;Dev 待 CI | Backend CI_SUCCESS(vet-and-test、governance);Dev 待 CI | CI | 2026-10-21 | GATE |
|
||||
| 代码 Review | 07228aa..aec92b7 | 通过(首轮阻塞已在 aec92b7 闭合) | 独立 code-reviewer 子会话(两轮,各为全新会话) | 2026-10-21 | 见合入与清理 |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| P4 A01 付费复验(Dev 8f59b13 / Backend 07228aa) | #235 告知覆盖所有工具结果可避免 C8 | 1 次运行,4 次付费请求 / 35914 tokens(守卫 30000,实例 60000) | 无 C8、0 拒绝、有最终答复 | 用户「批准」(2026-10-21) | NOT_PASS:请求 1 发布检查点;请求 2 两次 knowledge_search,末个结果带告知;请求 3 final-only 无工具调用(C8 已修复),最终答复在 4000 输出上限处截断(length);请求 4 续写(prompt 9844)再次截断后预算停止。1 检查点 0 拒绝;守卫超支 5914(续写前估算按上一轮 prompt 5810,剩余 7930 > 7262)。路由已删、实例已停;收据 ~/.local/share/agent-system-evidence/fast-slow-o2-a01g-20261021/a01/p4-run-receipt.json(ecd8153f6e544ed2) | 本卡 R1–R3 | 修正后需一次 P5 付费复验(待授权,标签 approved-o2-a01-final);P2–P4 累计 74541 tokens |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 8f59b135ec27fc04e432583d70e1e1d0d6f25ea8 | 8f59b135ec27fc04e432583d70e1e1d0d6f25ea8 | 2026-10-21T05:18:20Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:窗口已领取;调度记录:issue #518 评论
|
||||
- entry_id:mq-dev-20261021-fast-slow-o2-a01-final-budget-01;attempt_id:01;窗口执行者:当前主模型
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:Backend #236(241e1b3)预留估算计入上一轮 completion;final-only 提醒与物料任务文本要求约 1500 字以内一次写完;新增评测标签 approved-o2-a01-final(预算 45000、输出上限 6000,模型模式路由与 provider 包装同步生效),既有标签不变。Dev modules/backend 改钉 241e1b3。P4 付费复验 NOT_PASS 已记录于探索试验。
|
||||
- 验证:Backend CI PASS(GATE,sha aec92b768439);host 复跑 PASS(SELF);Dev CI 见 CI 记录;P5 付费复验 NOT_RUN(未授权)
|
||||
- 影响与剩余项:A01 是否通过须经一次 P5 付费复验(待批准,标签 approved-o2-a01-final,守卫 45000,实例上限 60000)
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:P4 暴露的截断、预算与估算问题已修正;O2 A01 付费通过仍待复验
|
||||
- 首选下一任务:O2 A01 付费复验(P5)
|
||||
- 推荐依据:效果只能付费判定
|
||||
- 前置条件/待确认:用户批准付费;用户自行重建路由文件
|
||||
- 预期产物/验收条件:≥2 个检查点、0 拒绝、无 C8、完整最终答复、未超守卫
|
||||
- 重复任务核对:无在途同题任务
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:全新独立 code-reviewer 子会话两轮(fresh_session=true,session id not-observable;仅收需求、base/head 与 diff)。首轮(07228aa..9a6f0f8)阻塞:模型模式评测 provider 包装把 max_tokens 硬钳到 4000,6000 上限到不了 provider → u2(aec92b7)修正并加 httptest 线上断言与 subRun 级测试。复审(9a6f0f8..aec92b7,首个复审会话连接中断无结论,另起全新会话):阻塞已闭合、无阻塞回归,离线重跑 runtime/server 测试通过。非阻塞(记录不修):模型模式接线一行无自动化覆盖(需真实模型);新 subRun 测试注释提及 30000 标签;新标签下前台 LLMRunner 也用 6000 上限(仅评测);reasoning tokens 使估算偏保守;hook/未知工具/取消等运行时生成结果不带告知;Backend 提交沿用 agent-exec transport 主题(PR 描述含四段)。Dev 改钉仅 gitlink,内容即已审 Backend #236
|
||||
- 冲突记录: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-21T05:18:07.672961+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "8f59b135ec27fc04e432583d70e1e1d0d6f25ea8",
|
||||
"after_head": "8f59b135ec27fc04e432583d70e1e1d0d6f25ea8",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "8f59b135ec27fc04e432583d70e1e1d0d6f25ea8",
|
||||
"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=8f59b135ec",
|
||||
"[remote] origin/main=8f59b135ec ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[cards] 已合入未闭合任务卡:feature-project-access-lifecycle.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] 8f59b135ec(完整:8f59b135ec27fc04e432583d70e1e1d0d6f25ea8)",
|
||||
"[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": "acf66583768ecbe0e2f3f2f1a8e49ccd31bdeffa33f9e72aaa908fd943477e16",
|
||||
"receipt_sha256": "9471a0b4da4b6690df9e11984a620cec535b1873025dbf393a73e7ad26e029e7",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/9471a0b4da4b6690df9e11984a620cec535b1873025dbf393a73e7ad26e029e7.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #672(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/672)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | 19d9ac73826a5287af3df2478be846a22f5b1e6a | 8d035dc2bb2fc0af39498a20acbd258c528ee259 | 2026-10-21T06:22:11Z |
|
||||
@@ -0,0 +1,161 @@
|
||||
# feature/fast-slow-o2-a01-p5-record 任务卡
|
||||
|
||||
任务主记录:PR #673 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/673;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 0289bbeabbaedcab8afcd25422be002843df9971
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-fast-slow-o2-a01-p5-record(worktree);分支:feature/fast-slow-o2-a01-p5-record
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=1a78cfb42769f5e0710674b00dc3d1fbda340c3cb2b1d0e435fc42acab66a182;triage_receipt=/tmp/o2p5rec-20261021/triage.json;receipt_sha256=bc12127d350a5ca19dc1961c8780a1f18f84a903d4ab82671f3ef2a7d79d414d
|
||||
|
||||
- 授权与交付终点:用户「可以」(2026-10-21,同意新开仅记录任务将 P5 结果入卡);允许仅任务卡变更的 Dev PR/CI/合入。P5 付费复验本身已获批准并执行(「继续」→「已经写好」)。未授权:再次付费复验、代码/配置变更、部署
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md);本任务无实施单元,仅 host 豁免路径 .task/**
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:合入;当前增量:I1 任务卡记录
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
目标:把 O2 A01 P5 付费复验(Dev 0289bbe / Backend 241e1b3,标签 approved-o2-a01-final)PASS 的花费、调用序列、结论与收据记入仓内任务卡,闭合里程碑「O2 A01 付费复验通过」。
|
||||
|
||||
非目标:不改代码、配置、gitlink 或方案文档;不再发起付费请求。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 无(仅 .task/** host 豁免路径) | — | — | — | — | — | — | — | — | — | — |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:适用
|
||||
- 主记录路径/版本:docs/superpowers/specs/2026-10-21-fast-slow-o2-c8-review.md;前序归档卡 .task/archive/feature-fast-slow-o2-a01-final-budget.md
|
||||
- 对应里程碑:O2 A01 付费复验通过(本卡记录达成)
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 记录 P5 付费复验结果 | 符合:里程碑证据入仓 | P0 | 卡内含花费、调用序列、结论、收据路径与摘要 | 无 | 完成(见探索试验) |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:不适用(仅记录);P5 资源提案 ~/.local/share/agent-system-evidence/fast-slow-o2-a01h-20261021/resource-proposal.md(ce24f75bb6910c8e)
|
||||
- 实施计划引用:I1 任务卡记录(R1)
|
||||
- 测试计划引用:Dev CI make test+build(仅卡变更);P5 本身的验证见探索试验
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 0289bbe | 完成 | host | 2026-10-21 | 用户「可以」 |
|
||||
| 需求评估 | 0289bbe | 完成 | host | 2026-10-21 | R1 |
|
||||
| 需求表确认 | 0289bbe | 完成(仅记录) | 用户 | 2026-10-21 | 「可以」 |
|
||||
| 方案设计 | — | 不适用(仅记录) | host | 2026-10-21 | — |
|
||||
| 实施计划 | — | 不适用(仅记录) | host | 2026-10-21 | — |
|
||||
| 实施与自检 | 本分支 | 完成(host 写 .task/**) | host | 2026-10-21 | 卡内数值取自 p5-run-receipt.json 与 provider-records.json |
|
||||
| 正式测试与验收 | 本分支 | 待 Dev CI | CI | 2026-10-21 | GATE |
|
||||
| 代码 Review | 本分支 | 通过(仅卡变更,host 核对数值与收据一致) | host | 2026-10-21 | 无代码变更 |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| P5 A01 付费复验(Dev 0289bbe / Backend 241e1b3,标签 approved-o2-a01-final) | #236 预留估算计入上一轮 completion、最终答复简明、45000/6000 可让 A01 产出 ≥2 检查点与完整最终答复 | 1 次运行,3 次付费请求 / 23565 tokens(守卫 45000,实例 60000,输出上限 6000) | ≥2 running 检查点、0 拒绝、无 C8、完整最终答复、不超守卫 | 用户「继续」→「已经写好」(2026-10-21);路由 sha b8e6c7b81b83b645,仅核元数据 | PASS:请求 1 发布检查点 1(prompt 5546 / completion 1604,tool_calls);请求 2 发布检查点 2,结果带 final-only 告知(7203 / 611,tool_calls);请求 3 无工具调用写出最终答复 2787 字(6601 / 2000,stop)。2 检查点 0 拒绝、0 契约失败、terminalFault=false;TestWorkCheckpoints PASS;executionState=completed、outcomeCompleteness=partial(完成且模型如实列出未决项,task_projection.go 设计,非失败)。服务器已停(核对 /proc exe)、路由已删、候选 worktree 已移除;收据 ~/.local/share/agent-system-evidence/fast-slow-o2-a01h-20261021/a01/p5-run-receipt.json(c5aa4819787fe926) | 里程碑达成 | 无;P2–P5 累计 98106 tokens |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 0289bbeabbaedcab8afcd25422be002843df9971 | 0289bbeabbaedcab8afcd25422be002843df9971 | 2026-10-21T08:21:42Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:窗口已领取;调度记录:issue #518 评论
|
||||
- entry_id:mq-dev-20261021-fast-slow-o2-a01-p5-record-01;attempt_id:01;窗口执行者:当前主模型
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:O2 A01 P5 付费复验 PASS 已记入本卡(探索试验);里程碑「O2 A01 付费复验通过」达成。
|
||||
- 验证:P5 评测 TestWorkCheckpoints PASS(SELF,本地付费运行,收据见探索试验);本卡 Dev CI 见 CI 记录
|
||||
- 影响与剩余项:无代码影响。非阻塞遗留(不在本卡范围):kbreg 数据库密码待轮换;某进程命令行可见生产 DSN;feature-project-access-lifecycle 卡已合入未闭合、worktree 未回收
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:O2 A01 付费通过已达成并入卡
|
||||
- 首选下一任务:无既定后续(O2 A01 链路收口);若需推广到其他用例或调整默认预算,须另行立项
|
||||
- 推荐依据:本卡探索试验
|
||||
- 前置条件/待确认:用户决定是否扩展
|
||||
- 预期产物/验收条件:待定义
|
||||
- 重复任务核对:无在途同题任务
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:仅任务卡变更;host 核对卡内数值与 p5-run-receipt.json、provider-records.json 一致;无代码,不需独立代码 Review
|
||||
- 冲突记录: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-21T08:21:10.884605+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "0289bbeabbaedcab8afcd25422be002843df9971",
|
||||
"after_head": "0289bbeabbaedcab8afcd25422be002843df9971",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "0289bbeabbaedcab8afcd25422be002843df9971",
|
||||
"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=0289bbeabb",
|
||||
"[remote] origin/main=0289bbeabb ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[cards] 已合入未闭合任务卡:feature-project-access-lifecycle.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] 0289bbeabb(完整:0289bbeabbaedcab8afcd25422be002843df9971)",
|
||||
"[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": "cf12f0bab4cdcb57c4cc4ef12ef96aa60c45549bfcc0e90942a4dd35057f2dee",
|
||||
"receipt_sha256": "34ba043bf5679b760ccc206a92dc2b4776b1480d372fff4ad698ab228a308761",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/34ba043bf5679b760ccc206a92dc2b4776b1480d372fff4ad698ab228a308761.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #673(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/673)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,162 @@
|
||||
# feature/fast-slow-o2-c8-review 任务卡
|
||||
|
||||
任务主记录:PR #667 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/667;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= a9566a68505ac97dead15ed3b5672954f15cea1c
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-fast-slow-o2-c8-review(worktree);分支:feature/fast-slow-o2-c8-review
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=5047963d4ab90c90c1c0f90e1e71ae0a04b16e9d082d3c9b94ac4bf0e0d0aeda;triage_receipt=/tmp/o2c8-20261021/triage.json;receipt_sha256=6a53842b6c14e09edbe1334abfa0b9b3829772a2129fe1325c2916fdbd324fa0
|
||||
|
||||
- 授权与交付终点:用户选择「1 零付费复核 C8」;允许离线只读调查、写复核文档与任务卡、Review、PR、CI 与合入。未授权:任何付费请求/复跑、代码实施、实例配置写入、部署
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:合入;当前增量:I1 复核文档(host 豁免路径,无实施单元)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
目标:离线确定 P2 第 3 次请求 C8 的成因(工具名未声明还是参数 schema 不符),只读评估「schema 不符交给工具层返回可修正错误」的改法,并记录 P2 花费与结论。
|
||||
|
||||
非目标:不改 Backend/agent-native 代码、不改路由契约、不发起付费请求。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| 无 | 仅 `docs/superpowers/specs/**` 与 `.task/**`(host 豁免) | 不适用 | — | — | — | — | 不适用 | — | — | — |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:适用(O2 fast-slow A01 证据合规的后续复核)
|
||||
- 主记录路径/版本:docs/superpowers/specs/2026-10-20-fast-slow-o2-a01-evidence-design.md(main a9566a6)
|
||||
- 对应里程碑:O2 A01 付费复验通过
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 确定 C8 成因并评估改法,记录 P2 结论 | 符合:O2 下一步取决于此 | P0 | 复核文档给出可复核依据与建议 | P2 证据目录 | 完成:C8=工具名未声明;改法不适用 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:docs/superpowers/specs/2026-10-21-fast-slow-o2-c8-review.md(Backend dfb31da、agent-native 2fc66be)
|
||||
- 实施计划引用:不适用(只读复核)
|
||||
- 测试计划引用:不适用;零付费,未运行模型
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | a9566a6 | 完成 | host | 2026-10-21 | 用户选择「1」 |
|
||||
| 需求评估 | a9566a6 | 完成 | host | 2026-10-21 | R1 |
|
||||
| 需求表确认 | a9566a6 | 完成(范围由用户选项确定) | 用户 | 2026-10-21 | 选项 1 |
|
||||
| 方案设计 | c5e8b47 | 完成(只读复核文档) | host | 2026-10-21 | 复核文档 |
|
||||
| 实施计划 | — | 不适用 | — | — | 无代码变更 |
|
||||
| 实施与自检 | — | 不适用 | — | — | 无代码变更 |
|
||||
| 正式测试与验收 | c5e8b47 | CI 待核 | CI | — | make test + make build |
|
||||
| 代码 Review | 511e079→c5e8b47 | 通过(1 阻塞已修) | 独立 code-reviewer 子会话 | 2026-10-21 | 见合入与清理 |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| P2 A01 付费复验(前序已授权) | 证据说明收紧后 A01 不再被拒 | 3 次请求 / 17955 tokens,守卫 30000 | 0 拒绝且路由契约通过 | 用户「批准」 | NOT_PASS:第 3 次 C8;2 检查点 0 拒绝;收据 ec02e80753db2f85 | 证据合规已达成,阻塞转为 final-only 阈值 | 修正后需一次付费复验(待授权) |
|
||||
| C8 零付费复核 | C8 来自 schema 不符 | 只读代码 + P2 记录 | 能否确定成因 | 用户选择「1」 | 否定假设:C8=工具名未声明(final-only 移除检查点工具) | 无代码变更 | 否 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | a9566a68505ac97dead15ed3b5672954f15cea1c | a9566a68505ac97dead15ed3b5672954f15cea1c | 2026-10-21T02:28:21Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:窗口已领取;调度记录:issue #518 评论
|
||||
- entry_id:mq-dev-20261021-fast-slow-o2-c8-review-01;attempt_id:01;窗口执行者:当前主模型
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:C8 成因为 `publish_work_checkpoint` 在 final-only 请求中未声明(已存 2 个检查点与预算预留两条件同时触发 final-only),A01 预算只够两轮检查点加一次最终答复,与任务指令 reading→crosscheck 冲突;「schema 不符交给工具层」不适用且不建议实施。P2:3 次付费请求、17955 tokens、NOT_PASS(C8),证据合规达成(2 检查点 0 拒绝),路由文件已删除;本次复核零付费。
|
||||
- 验证:只读复核,依据见复核文档;模型复验 NOT_RUN(未授权)
|
||||
- 影响与剩余项:推荐 F1(宿主判定下一次为 final-only 时由工具结果告知,并使任务指令与预算一致),需单独授权实施及一次付费复验
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:C8 成因已确定;O2 A01 付费通过仍待修正与复验
|
||||
- 首选下一任务:F1 final-only 上限告知(Backend)
|
||||
- 推荐依据:不动契约、零付费单测可验证宿主侧行为
|
||||
- 前置条件/待确认:用户授权实施 F1 与之后的付费复验
|
||||
- 预期产物/验收条件:final-only 前一次检查点结果含提示;付费复验无 C8
|
||||
- 重复任务核对:无在途同题任务
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:全新独立 code-reviewer 子会话(fresh_session=true,session id not-observable,仅收目标/base a9566a6/head 511e079/diff/源码与 provider-records),只读重算 E4 并核对行号。阻塞 1:final-only 同时由预算预留条件触发(outputCap 4000),已在 c5e8b47 修正 E1/E4/E5/F1/F2 与卡;次要:缓存归因、argschema properties 前提已修,卡头权威仓地址为 task-start 生成值未改
|
||||
- 冲突记录: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-21T02:27:26.256614+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "a9566a68505ac97dead15ed3b5672954f15cea1c",
|
||||
"after_head": "a9566a68505ac97dead15ed3b5672954f15cea1c",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "a9566a68505ac97dead15ed3b5672954f15cea1c",
|
||||
"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=a9566a6850",
|
||||
"[remote] origin/main=a9566a6850 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=3",
|
||||
"[cards] 已合入未闭合任务卡:feature-project-access-lifecycle.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] a9566a6850(完整:a9566a68505ac97dead15ed3b5672954f15cea1c)",
|
||||
"[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": "a891afd4f3610dd6ce63a808b10cc78b8c6c411454f4851c724ac3c6213ac62a",
|
||||
"receipt_sha256": "e58dbf12cb9208a2e86ed98f3d39405cb8a8278205d60f1bfaeae9852d264ac2",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/e58dbf12cb9208a2e86ed98f3d39405cb8a8278205d60f1bfaeae9852d264ac2.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #667(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/667)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,163 @@
|
||||
# feature/fast-slow-o2-final-only-notice-all-tools 任务卡
|
||||
|
||||
任务主记录:PR #670 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/670;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 210f8f305238a0241ce69ab5943b52d8bc5bcc62
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-fast-slow-o2-final-only-notice-all-tools(worktree);分支:feature/fast-slow-o2-final-only-notice-all-tools
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=2ca11f85a6b5aec7ca9d38b2ca74f6d4157c76719727445f9e0b4f987dce7f08;triage_receipt=/tmp/o2f1b-20261021/triage.json;receipt_sha256=6c4c1dff003ef7956dd46de75607f7240c7c7de6d83bc9281a620f7ceaea19d2
|
||||
|
||||
- 授权与交付终点:用户「先开始 1吧」(2026-10-21,P3 后方向 1:告知扩展到所有工具结果);允许零付费 Backend 实施单元、Backend PR/CI/合入、Dev gitlink 改钉 PR/CI/合入,并记录 P3 付费复验结果。未授权:再次付费复验(须单独批准)、A01 预算调整、路由契约改动、实例配置写入、部署
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:合入;当前增量:I2 Dev 改钉(I1 已合入 Backend #235)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
目标:物料子 Agent 在下一次请求将为 final-only/stop 时,于当轮**任一**工具结果(不限 publish_work_checkpoint)追加告知;记录 P3 付费复验结果。受影响:Backend internal/runtime/sub_loop.go、material_final_reserve.go 及测试。
|
||||
|
||||
非目标:不改路由契约、agent-native、工具说明/schema、materialFinalDecision 公式、任务文本、A01 预算;不发起付费请求。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| o2f1b-i1-backend-notice-all-tools | Backend runtime/sub_loop.go、material_final_reserve.go、work_checkpoint_test.go | 22fcb8ac80b2 | implementation | 按 AGENT-EXECUTION.md | 1(866c2dd62a06,fresh_session=true,session not-observable) | pi | success | 52a3e497ed14 → Backend #235 | 0 | /tmp/o2f1b-20261021/f1b-receipt.json |
|
||||
| o2f1b-i2-dev-backend-pin | modules/backend gitlink → 07228aa | ccceb3bfb61b | implementation | 按 AGENT-EXECUTION.md | 1(212e019317ad,fresh_session=true,session not-observable) | cursor | success | 002231cca6d6 | 0 | /tmp/o2f1b-20261021/f2-receipt.json |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:适用(O2 A01 付费通过的前置修正)
|
||||
- 主记录路径/版本:docs/superpowers/specs/2026-10-21-fast-slow-o2-c8-review.md(F1);前序归档卡 .task/archive/feature-fast-slow-o2-final-only-notice.md
|
||||
- 对应里程碑:O2 A01 付费复验通过
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | final-only/stop 前一轮任一工具结果含告知 | 符合:P3 根因 | P0 | 单测:检查点后一轮 knowledge_search 结果含告知且第 3 次请求无检查点声明;错误结果保留 IsError;预算充足/无预算不追加;既有测试不变 | 无 | 完成(Backend #235) |
|
||||
| R2 | 记录 P3 付费复验结果 | 符合 | P0 | 卡内含花费、调用序列、结论与收据 | 无 | 完成(见探索试验) |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:P3 后方向 1(本卡目标);单元说明 /tmp/o2f1b-20261021/f1b-unit.json
|
||||
- 实施计划引用:I1 Backend(R1,agent-per-unit)→ Backend PR/CI/合入 → I2 Dev 改钉 modules/backend
|
||||
- 测试计划引用:单元 verification(gofmt、vet、runtime/runmgr/server 测试、eval/live A01 离线用例);Backend CI;Dev CI make test+build;付费复验 NOT_RUN(待授权)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 210f8f3 | 完成 | host | 2026-10-21 | 用户「先开始 1吧」 |
|
||||
| 需求评估 | 210f8f3 | 完成 | host | 2026-10-21 | R1/R2 |
|
||||
| 需求表确认 | 210f8f3 | 完成(范围即方向 1) | 用户 | 2026-10-21 | 选项 1 |
|
||||
| 方案设计 | 210f8f3 | 完成(P3 根因 + 方向 1) | host | 2026-10-21 | 本卡探索试验 |
|
||||
| 实施计划 | 93a999f | 完成 | host | 2026-10-21 | 单元说明 f1b/f2-unit.json |
|
||||
| 实施与自检 | Backend 52a3e49;Dev 002231c | 完成 | pi / cursor(agent-exec) | 2026-10-21 | 收据表;host 复跑 gofmt/vet/runtime+runmgr+server/eval-live A01 全部 ok(SELF) |
|
||||
| 正式测试与验收 | Backend 52a3e49;Dev 待 CI | Backend CI_SUCCESS(vet-and-test、governance);Dev 待 CI | CI | 2026-10-21 | GATE |
|
||||
| 代码 Review | 7158bea..52a3e49 | 通过(无阻塞) | 独立 code-reviewer 子会话 | 2026-10-21 | 见合入与清理 |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| P3 A01 付费复验(Dev 210f8f3 / Backend e02e25a) | #233 告知可避免 C8 | 1 次运行,3 次付费请求 / 20672 tokens(守卫 30000,实例 60000) | 无 C8、0 拒绝、有最终答复 | 用户「批准」(2026-10-21) | NOT_PASS:请求 1 发布检查点(无告知,正确);请求 2 三次 knowledge_search;剩余 18056 ≤ 2×(5947+4000+1486)=22866 使请求 3 为 final-only,告知未送达;请求 3 发 3211 字节工具调用 → C8。1 检查点 0 拒绝;路由已删、实例已停;收据 ~/.local/share/agent-system-evidence/fast-slow-o2-a01f-20261021/a01/p3-run-receipt.json(80f5eb3b9ac4e8d7) | 告知须覆盖所有工具结果(本卡 R1) | 修正后需一次付费复验(待授权);P2+P3 累计 38627 tokens |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 210f8f305238a0241ce69ab5943b52d8bc5bcc62 | 210f8f305238a0241ce69ab5943b52d8bc5bcc62 | 2026-10-21T04:32:08Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:窗口已领取;调度记录:issue #518 评论
|
||||
- entry_id:mq-dev-20261021-fast-slow-o2-final-only-notice-all-tools-01;attempt_id:01;窗口执行者:当前主模型
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:Backend #235(07228aa)在下一次请求将为 final-only/stop 时,于当轮任一工具结果追加告知(不再限于检查点);Dev modules/backend 改钉 07228aa(同时带入他人已合入的 Backend #234)。P3 付费复验 NOT_PASS(C8,告知未送达)已记录于探索试验。
|
||||
- 验证:Backend CI PASS(GATE,sha 52a3e497ed14);host 复跑 PASS(SELF);Dev CI 见 CI 记录;付费复验 NOT_RUN(未授权)
|
||||
- 影响与剩余项:模型是否遵守告知须经一次 A01 付费复验(待批准,守卫 30000);若仍失败,备选为提高 A01 预算或 final-only 阶段保留检查点声明由工具层拒绝
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:告知已覆盖所有工具结果;O2 A01 付费通过仍待复验
|
||||
- 首选下一任务:O2 A01 付费复验(P4)
|
||||
- 推荐依据:效果只能付费判定
|
||||
- 前置条件/待确认:用户批准付费;用户自行重建路由文件
|
||||
- 预期产物/验收条件:无 C8、0 拒绝、正常最终答复
|
||||
- 重复任务核对:无在途同题任务
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:全新独立 code-reviewer 子会话(fresh_session=true,session id not-observable;仅收需求、base 7158bea/head 52a3e49 与 diff),离线重跑 runtime 测试通过;核对通用分支 IsError 等价、同轮决策单调、测试确实走非检查点工具且第 3 次请求无检查点声明、非物料子 Agent 不受影响。阻塞:无。非阻塞(记录不修):hook/未知工具/取消等运行时生成结果不经 runTool、不带告知;materialDecision 注释仍只提检查点结果;Backend 提交沿用 agent-exec transport 主题(PR 描述含四段)。Dev 改钉仅 gitlink,内容即已审 Backend #235(另含他人已合入的 #234)
|
||||
- 冲突记录: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-21T04:31:15.651661+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "210f8f305238a0241ce69ab5943b52d8bc5bcc62",
|
||||
"after_head": "210f8f305238a0241ce69ab5943b52d8bc5bcc62",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "210f8f305238a0241ce69ab5943b52d8bc5bcc62",
|
||||
"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=210f8f3052",
|
||||
"[remote] origin/main=210f8f3052 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[cards] 已合入未闭合任务卡:feature-project-access-lifecycle.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] 210f8f3052(完整:210f8f305238a0241ce69ab5943b52d8bc5bcc62)",
|
||||
"[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": "2414673f36e0321e34d32b0b4e97849a2da20a9cfb0a183c72b54e7aab344a9d",
|
||||
"receipt_sha256": "e35da8024f5fe2bdd2f91478ae94b5db6c32a9d7320ae820355c0c028e56e6ff",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/e35da8024f5fe2bdd2f91478ae94b5db6c32a9d7320ae820355c0c028e56e6ff.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #670(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/670)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,163 @@
|
||||
# feature/fast-slow-o2-final-only-notice 任务卡
|
||||
|
||||
任务主记录:PR #669 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/669;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= a496ea0f0249b2b412354677ef97612ccd692d25
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-fast-slow-o2-final-only-notice(worktree);分支:feature/fast-slow-o2-final-only-notice
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=0e52b1ae1ba0a197f908d26723a6ddef5a4f73014822263a35bcfc08a2e79bc8;triage_receipt=/tmp/o2f1-20261021/triage.json;receipt_sha256=29517c35eda25d6fe16a78c7ecabe26b306bd4071dff9c29bc36e839cfe5872b
|
||||
|
||||
- 授权与交付终点:用户「实施」(2026-10-21,针对 C8 复核推荐的 F1);允许零付费 Backend 实施单元、Backend PR/CI/合入、Dev gitlink 改钉 PR/CI/合入。未授权:付费复验(须单独批准)、路由契约改动、实例配置写入、部署
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:合入;当前增量:I2 Dev 改钉(I1 已合入 Backend #233)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
目标:物料子 Agent 在宿主判定下一次请求为 final-only(或预算停止)时,于当次 publish_work_checkpoint 工具结果中告知模型「后续不再接受检查点、直接写最终结论」;物料任务指令写明检查点通常最多 2 次(reading → crosscheck)。受影响:Backend internal/runtime/sub_loop.go、material_final_reserve.go、internal/runmgr/material.go 及测试。
|
||||
|
||||
非目标:不改路由契约与 agent-native;不改检查点工具说明/schema、materialFinalDecision 公式;不发起付费请求。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| o2f1-i1-backend-final-only-notice | Backend runtime/sub_loop.go、material_final_reserve.go、work_checkpoint_test.go、runmgr/material.go、material_sources_test.go | 76bc59db1d0e | implementation | cursor→agy→pi | 1(9e8e3bf5a934,fresh_session=true,session not-observable) | cursor | success | 786744992046 → Backend #233 | 0 | /tmp/o2f1-20261021/f1-receipt.json |
|
||||
| o2f1-i2-dev-backend-pin | modules/backend gitlink → e02e25a | ccceb3bfb61b | implementation | cursor→agy→pi | 1(fresh_session=true,session not-observable) | cursor | success | 623d1064587d | 0 | /tmp/o2f1-20261021/f2-receipt.json |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:适用(O2 A01 付费通过的前置修正)
|
||||
- 主记录路径/版本:docs/superpowers/specs/2026-10-21-fast-slow-o2-c8-review.md(main a496ea0)F1
|
||||
- 对应里程碑:O2 A01 付费复验通过
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | final-only 前一次检查点工具结果含告知 | 符合:C8 复核根因 | P0 | 单测:第 2 个检查点结果含告知、首个不含、仅预算触发时也含;既有 final-only 测试不变 | 无 | 完成(Backend #233) |
|
||||
| R2 | 物料任务指令与 2 次检查点上限一致 | 符合 | P0 | 单测断言任务文本关键句 | 无 | 完成(Backend #233) |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:docs/superpowers/specs/2026-10-21-fast-slow-o2-c8-review.md「可选修正」F1(main a496ea0);单元说明 /tmp/o2f1-20261021/f1-unit.json
|
||||
- 实施计划引用:I1 Backend(R1、R2,agent-per-unit)→ Backend PR/CI/合入 → I2 Dev 改钉 modules/backend
|
||||
- 测试计划引用:单元 verification(gofmt、vet、runtime/runmgr/server 测试、eval/live A01 离线用例);Backend CI;Dev CI make test+build;付费复验 NOT_RUN(待授权)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | a496ea0 | 完成 | host | 2026-10-21 | 用户「实施」F1 |
|
||||
| 需求评估 | a496ea0 | 完成 | host | 2026-10-21 | R1/R2 |
|
||||
| 需求表确认 | a496ea0 | 完成(范围即 C8 复核 F1) | 用户 | 2026-10-21 | 「实施」 |
|
||||
| 方案设计 | a496ea0 | 完成(沿用已合入复核文档 F1) | host | 2026-10-21 | 方案引用 |
|
||||
| 实施计划 | 874287b | 完成 | host | 2026-10-21 | 单元说明 f1/f2-unit.json |
|
||||
| 实施与自检 | Backend 7867449;Dev 623d106 | 完成 | cursor(agent-exec) | 2026-10-21 | 收据表;host 复跑 gofmt/vet/runtime+runmgr+server 测试/eval-live A01 全部 ok(SELF) |
|
||||
| 正式测试与验收 | Backend 7867449;Dev 待 CI | Backend CI_SUCCESS(vet-and-test、governance);Dev 待 CI | CI | 2026-10-21 | GATE |
|
||||
| 代码 Review | 7a41f22..7867449 | 通过(无阻塞) | 独立 code-reviewer 子会话 | 2026-10-21 | 见合入与清理 |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | a496ea0f0249b2b412354677ef97612ccd692d25 | a496ea0f0249b2b412354677ef97612ccd692d25 | 2026-10-21T02:52:15Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:窗口已领取(前序 #668 于 03:42 释放);调度记录:issue #518 评论
|
||||
- entry_id:mq-dev-20261021-fast-slow-o2-final-only-notice-01;attempt_id:01;窗口执行者:当前主模型
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:Backend #233(e02e25a)在下一次请求将为 final-only/stop 时于当次检查点工具结果追加告知,并让物料任务指令写明检查点通常最多 2 次;Dev modules/backend 改钉 e02e25a(同时带入他人已合入的 Backend #231)。
|
||||
- 验证:Backend CI PASS(GATE,sha 786744992046);host 复跑 PASS(SELF);Dev CI 见 CI 记录;付费复验 NOT_RUN(未授权)
|
||||
- 影响与剩余项:模型是否遵守告知只能经一次 A01 付费复验判定(待用户批准,守卫 30000 tokens)
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:F1 已交付;O2 A01 付费通过仍待复验
|
||||
- 首选下一任务:O2 A01 付费复验(P3)
|
||||
- 推荐依据:F1 效果只能付费判定
|
||||
- 前置条件/待确认:用户批准付费;用户自行重建路由文件
|
||||
- 预期产物/验收条件:无 C8、无拒绝检查点、正常最终答复
|
||||
- 重复任务核对:无在途同题任务
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:全新独立 code-reviewer 子会话(fresh_session=true,session id not-observable;仅收需求、base 7a41f22/head 7867449 与 diff),离线重跑 runtime/runmgr 测试通过;核对 message_end 先于工具执行、决策与下一请求同源、输出上限等价。阻塞:无。非阻塞(记录不修):stop 路径告知缺单测;任务文案「最后输出完整整理结果」略重复;同一回复内多个调用无法被本次告知撤回(既有批处理语义)。Dev 改钉仅 gitlink,内容即已审 Backend #233(另含已合入的 #231)
|
||||
- 冲突记录: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-21T02:50:21.185986+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "a496ea0f0249b2b412354677ef97612ccd692d25",
|
||||
"after_head": "a496ea0f0249b2b412354677ef97612ccd692d25",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "a496ea0f0249b2b412354677ef97612ccd692d25",
|
||||
"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=a496ea0f02",
|
||||
"[remote] origin/main=a496ea0f02 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=4",
|
||||
"[cards] 已合入未闭合任务卡:feature-project-access-lifecycle.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] a496ea0f02(完整:a496ea0f0249b2b412354677ef97612ccd692d25)",
|
||||
"[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": "35e2936cf7a32e8e9b1454ec05f7b91ab0d0016105d80deaf7e8f7a26db2fa9d",
|
||||
"receipt_sha256": "8d5a1392fde8843dc1700461131ffde68dbc3e599350c1259b3e3217b84adc81",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/8d5a1392fde8843dc1700461131ffde68dbc3e599350c1259b3e3217b84adc81.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #669(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/669)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,161 @@
|
||||
# feature/fast-slow-o2-p1-card-backfill 任务卡
|
||||
|
||||
任务主记录:PR #658 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/658;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 886283a4a534de8c418fa4a60d2d7a5d15c0ccfb
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-fast-slow-o2-p1-card-backfill(worktree);分支:feature/fast-slow-o2-p1-card-backfill
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=318cd0a09a14eec313a932c1922703af4e5775041563867108b1c348bafa0534;triage_receipt=/tmp/o2p1bf-20261020/triage.json;receipt_sha256=b48aadf5d9a227331135846665fc93f2b0571a8926dc1f3ff16032e42bbba206
|
||||
|
||||
- 授权与交付终点:用户 2026-10-20 同意(“可以”)开小 PR 补齐 O2 P1 归档卡;终点为本 PR 合入 Dev main。仅改 `.task/**`,无实施单元。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:合入;当前增量:归档卡记录补齐
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 目标:补齐 `.task/archive/feature-fast-slow-o2-p1.md`(Dev #656)闭合时遗漏的规划、需求、方案、阶段、下一步与合入清理记录;事实取自 #656/#229 的 CI、合入与窗口记录。
|
||||
- 非目标:改变 P1 结论、任何代码或 gitlink 变更。
|
||||
|
||||
## 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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | 886283a4a534de8c418fa4a60d2d7a5d15c0ccfb | 886283a4a534de8c418fa4a60d2d7a5d15c0ccfb | 2026-10-20T12:55:31Z |
|
||||
|
||||
## 证据(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-20T12:54:35.036061+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "886283a4a534de8c418fa4a60d2d7a5d15c0ccfb",
|
||||
"after_head": "886283a4a534de8c418fa4a60d2d7a5d15c0ccfb",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "886283a4a534de8c418fa4a60d2d7a5d15c0ccfb",
|
||||
"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=886283a4a5",
|
||||
"[remote] origin/main=886283a4a5 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[recycle] merged un-recycled branches: fix/l1-summary-card-fence",
|
||||
"[cards] 已合入未闭合任务卡:final-cleanup-summary.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] 886283a4a5(完整:886283a4a534de8c418fa4a60d2d7a5d15c0ccfb)",
|
||||
"[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": "34e38145b1a96b18aa58cb4823d2e689339469ce5ca13678205adaca72a9c7dd",
|
||||
"receipt_sha256": "214298be46fe9a83b0529d8274ae52cf86fbbcb9aa8c73f3e87eb1454e891bd4",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/214298be46fe9a83b0529d8274ae52cf86fbbcb9aa8c73f3e87eb1454e891bd4.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #658(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/658)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,169 @@
|
||||
# feature/fast-slow-o2-p1 任务卡
|
||||
|
||||
任务主记录:PR #656 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/656;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 873e957306a791eaa36e5251b45e3ed44a863143
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-fast-slow-o2-p1(worktree);分支:feature/fast-slow-o2-p1
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=a68604cc99e4eb41c84ee538e8d5a97e3d14c1902c323a36a2acded62c9e8b56;triage_receipt=/tmp/o2p1-20261020/triage.json;receipt_sha256=eda0fdef5b17ab9d6a781c96200ce3e6cefb29c2e80718eb97cb11437ca5eaef
|
||||
|
||||
- 授权与交付终点:用户 2026-10-20 批准 P1 两段(AskQuestion p1=approve_both;决定记录 `~/.local/share/agent-system-evidence/fast-slow-o2-p1-20261020/probe-authorization-decision.md`,提案 sha256 03f401b49640fe0f…):第一段 ≤1 次付费合同探针;达标则自动跑一次三材料 A01(`WORK_CHECKPOINT_REMAINING_TOKENS=60000`),合计 ≤63,000 reported tokens。零付费准备含 Backend fail-fast 与 Dev 改钉。终点:本 PR 合入 Dev main 并记录 P1 结论。未授权:额外请求/补跑/换模型、在任何实例写入系统层配置、部署。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:已完成;当前增量:Dev 改钉 + P1 结论记录(记录补齐见 feature/fast-slow-o2-p1-card-backfill)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 目标:按 `docs/superpowers/plans/2026-10-19-fast-slow-o2-route-contract-eof-plan.md` §5 P1,在真实 MiniMax-M3 路由上验证 route_contract_eof 合同(第一段),并在达标后验证一次三材料 A01 质量(第二段);Dev 改钉 `modules/backend` d8923f1 → 74e5396(含 Backend [#229](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Backend/pulls/229) 评测合同未挂上即 `t.Fatal`,以及其他会话已合入的 #228)。
|
||||
- 非目标:实例系统层配置写入、部署、合同放宽或修订、额外付费请求。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| o2-p1a-contract-probe(仓外 probe-code) | main.go, main_test.go, preflight.py, dry-run-guards.sh | 76bc59db… | implementation | cursor→antigravity→pi-glm→pi→codex | 1 | cursor(session not-observable,fresh) | success | 3b138a9 | 0 | /tmp/o2p1-20261020/o2-p1a-receipt.json |
|
||||
| o2-p1b-eval-contract-failfast(Backend) | internal/server/work_checkpoint_eval{,_route_contract}_test.go | 76bc59db… | implementation | 同上 | 1 | cursor | failed-unknown(CLI 报 “Cannot use this model: auto”,零改动;fail-stop) | — | 0 | /tmp/o2p1-20261020/o2-p1b-receipt.json |
|
||||
| o2-p1b-eval-contract-failfast(用户授权全新重跑) | 同上 | 76bc59db… | implementation | 同上 | 1 | cursor(fresh) | success | ddb6e29 | 0 | /tmp/o2p1-20261020/o2-p1b-receipt-r2.json |
|
||||
| o2-p1c-preflight-push-contexts(probe-code) | preflight.py | — | implementation | 同上 | 0 | — | invalid-config(派发前拒绝:主检出有本地构建产物,零尝试;产物加入 .git/info/exclude 后重新派发) | — | 0 | /tmp/o2p1-20261020/o2-p1c-receipt.json |
|
||||
| o2-p1c-preflight-push-contexts(重新派发) | preflight.py | — | implementation | 同上 | 1 | cursor(fresh) | success | d8d3fa5 | 0 | /tmp/o2p1-20261020/o2-p1c-receipt-r2.json |
|
||||
| o2-p1d-candidate-root(probe-code) | main.go, preflight.py | — | implementation | 同上 | 1 | cursor(fresh) | success | 7c51b6d | 0 | /tmp/o2p1-20261020/o2-p1d-receipt.json |
|
||||
| o2-p1e-dev-backend-pin | modules/backend | — | implementation | 同上 | 1 | cursor(fresh) | success | c93f335 | 0 | /tmp/o2p1-20261020/o2-p1e-receipt.json |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:适用(快慢通道 O2 route_contract_eof 的付费验证阶段)
|
||||
- 主记录路径/版本:`docs/superpowers/plans/2026-10-19-fast-slow-o2-route-contract-eof-plan.md` §5 P1(Dev 886283a);设计 `docs/superpowers/specs/2026-10-19-fast-slow-o2-route-contract-eof-design.md`
|
||||
- 对应里程碑:O2 I1–I3 已合入(Dev 7e6c544)之后的 P1 真实路由验证
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 真实路由合同探针:一次付费请求验证 route_contract_eof 放行 | 符合:计划 §5 P1 第一段;用户 approve_both | 高 | completionBasis=route_contract_eof、contractCheck 空、放行 1、执行 1、Usage 存在 | O2 I1–I3 已合入;受保护路由文件 | PASS |
|
||||
| R2 | 达标后一次三材料 A01 | 符合:计划 §5 P1 第二段;自动门槛 | 高 | r0.4 质量口径:≥2 条终态前阶段成果与完整最终正文 | R1 PASS;R3 | FAIL(检查点证据被拒,停止规则生效) |
|
||||
| R3 | 评测合同配置写错即失败,并改钉进 Dev | 符合:P1 提案零付费准备第 2、4 项 | 中 | 配置 routeContractEOF 但未挂上时 t.Fatal 且不含配置值;精确新 HEAD CI 通过 | Backend #229 | PASS(Backend 74e5396、Dev #656) |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:P1 资源提案 `~/.local/share/agent-system-evidence/fast-slow-o2-p1-20261020/resource-proposal.md`(sha256 03f401b49640fe0f…,用户批准);设计同上
|
||||
- 实施计划引用:计划 §5 P1;单元 o2-p1a…o2-p1e(见 Agent 实施收据)对应 R1/R3
|
||||
- 测试计划引用:提案“执行前须完成的零付费准备”与“判读”表(R1–R3 验收条件同上)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 计划 §5 P1 | 完成 | host | 2026-10-20 | R1–R3 |
|
||||
| 需求评估 | P1 资源提案 03f401b4… | 完成 | host | 2026-10-20 | 提案判读表 |
|
||||
| 需求表确认 | 同上 | 完成 | 用户(approve_both) | 2026-10-20 | probe-authorization-decision.md |
|
||||
| 方案设计 | O2 设计 + P1 提案 | 沿用已批准对象 | — | — | 设计 2026-10-19 |
|
||||
| 实施计划 | 计划 §5 P1 | 沿用已批准对象 | — | — | 同上 |
|
||||
| 实施与自检 | o2-p1a…e | 完成 | Cursor worker + host 验证 | 2026-10-20 | 实施收据表;probe 46 测试、Backend live 测试 |
|
||||
| 正式测试与验收 | 探针 + A01 | R1 PASS / R2 FAIL / R3 PASS | host(非独立 REPLAY) | 2026-10-20T12:0x–12:27Z | 探索试验表 |
|
||||
| 代码 Review | Backend ddb6e29、probe 3b138a9..7c51b6d、Dev c93f335 | PASS | host(当前主模型)审阅 diff | 2026-10-20 | 合入与清理 |
|
||||
| 合入 | Dev #656 HEAD a1c1ff0 | 完成 | agent | 2026-10-20 | 合入记录 |
|
||||
| 合入核验与收口 | main 886283a | 完成 | agent | 2026-10-20 | 清理记录 |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| P1 第一段:合同探针(2026-10-20T12:0x,1 次付费请求) | 真实 MiniMax-M3 流满足 C1–C9,合同放行 clean EOF | 二进制 sha256 5fb0d8ba…(Native 2fc66be,候选 Dev 7e6c544 专用 worktree),路由 sha256 b8e6c7b8…(0600,三元组摘要 0d7f1a3b… 与生产规则一致) | 达标:completionBasis=route_contract_eof、contractCheck 空、放行 1、执行 1、Usage 存在 | 用户 approve_both;preflight prepare 24/24、execute-ready 25/25 PASS,providerRequests=0 | **PASS**:readOutcome=clean_eof、finish=tool_calls、streamComplete=false、http2、tail=json_frame×7、Usage 447+40=487、diagnosis=route_contract_released(回执 `private-probe/run-001/terminal-receipt.json`,只含枚举/计数) | 自动触发第二段 | 否 |
|
||||
| P1 第二段:三材料 A01 | 放行后 A01 产出阶段成果与完整最终正文 | Backend 74e5396 的 live 测试二进制(server sha256 59670b80…、driver 4e26ecd1…),隔离数据目录,材料取 r03 存档(与 r02 哈希一致),REMAINING_TOKENS=60000;用例门禁沿用现有标签 `approved-r04-20261014`(新增标签需再走 Backend PR;该标签启用服务器端单 A01 run 守卫,画像预算与 r0.4 A01 一致);记录 `a01/…/provider-records.json`(37966555…)、`a01/model-output/candidate-r04-a01.json`(89fea4cc…)、`a01/a01-run-receipt.json`(7e3583a8…) | 沿用评测停止规则,不补跑 | 同上(自动门槛已满足;本 PR HEAD a824b38 CI 三项 success 后执行) | **FAIL(应用层,检查点被拒)**:run_96ccc9c7208f 仅 1 次 provider 调用,6,420 tokens(4,832+1,588),`completionBasis=route_contract_eof`、clean_eof、http2、finish=tool_calls、toolFragments=8、参数合法——合同放行、工具已执行;首个 `publish_work_checkpoint` 被 core `validateWorkFindingEvidence` 拒绝(`checkpoint.evidence: direct relation cannot have result`:模型给 `mode=direct_relation` 却带 total/phase 前提与 result,属 difference 形态),评测停止规则 `evaluation budget: rejected work checkpoint` 生效,子 run failed,卡片 failed,驱动判 “fewer than two persisted running checkpoints” | r0.4 同一驱动失败发生在传输层(缺 `[DONE]`,0 释放);P1 已越过传输层,新阻塞点是模型对检查点证据模式的遵从 | 否(停止,不补跑;另提方案) |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 873e957306a791eaa36e5251b45e3ed44a863143 | 873e957306a791eaa36e5251b45e3ed44a863143 | 2026-10-20T12:11:15Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:未入队;调度记录:无
|
||||
- entry_id:无;attempt_id:无;窗口执行者:无
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:P1 第一段 PASS(合同在真实 MiniMax-M3 路由放行 clean EOF 工具调用);第二段 FAIL(应用层检查点证据被拒,评测停止规则生效)。付费合计 2 次请求、6,907 reported tokens(≤63,000 上限);无补跑、无换模型、未写任何实例系统层配置、未部署。
|
||||
- 验证:零付费准备 PASS——probe 46 个测试(-race)、preflight prepare 24/24 与 execute-ready 25/25、Backend #229 CI(43575816…,与合入 74e5396 树一致)、A01 前置 live 门禁测试与 Dev #656 HEAD a824b38 CI(test / test-pg / svc-realcombo 全绿)。第一段回执 `private-probe/run-001/terminal-receipt.json`(128a6be7…);第二段见上方探索试验表与证据表。评估:host(当前主模型)按记录复核,非独立 REPLAY。
|
||||
- 影响与剩余项:O2 route_contract_eof 在真实路由上可用(P1 判读表“第一段 route_contract_eof、放行 1、执行 1 → 合同在真实路由上成立”)。A01 质量仍未验证:下一阻塞是 `publish_work_checkpoint` 的 evidence 模式/前提/result 组合遵从(agent-system-core `validateWorkFindingEvidence`),需另提方案(如工具 schema/提示收紧或拒绝后可纠错重试的评测口径),本授权不覆盖。实例系统层配置写入仍待另行决定。
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:计划 §5 P1 已执行完毕;合同在真实路由成立,A01 质量未验证。
|
||||
- 首选下一任务:A01 检查点证据遵从方案(`publish_work_checkpoint` evidence 模式/前提/result 组合;agent-system-core `validateWorkFindingEvidence`),先设计与资源提案,不直接付费。
|
||||
- 推荐依据:P1 第二段唯一阻塞点;传输层已解除。
|
||||
- 前置条件/待确认:用户决定是否立项;实例系统层合同配置写入另行决定;受保护路由文件 `~/.secrets/fast-slow-o2-p1-route.json` 由用户处置。
|
||||
- 预期产物/验收条件:方案文档与新资源提案;验收沿用 r0.4 A01 质量口径。
|
||||
- 重复任务核对:2026-10-20 无在途同题任务或 PR。
|
||||
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:host(当前主模型)审阅各单元实际 diff 与验证输出:probe 3b138a9(合同三元组请求前校验、单次工具桩、回执白名单)、d8d3fa5、7c51b6d;Backend ddb6e29;Dev c93f335(仅 gitlink)。结论 PASS,无阻塞项;非独立 CLI session。
|
||||
- 冲突记录:Backend #229 合入前 main 前进至 ed956a4(#228),合并 origin/main 为 4357581 后重跑 live 测试与 CI;Dev #656 无冲突,task-sync changed=0。
|
||||
- CI 记录:GATE——Backend 4357581… `ci / vet-and-test`、`governance` success;Dev a1c1ff039ad0667d1c7b90fb46bcf8972fccd317 `ci / test`、`ci / test-pg`、`ci / svc-realcombo`(pull_request)success(`make ci-wait` CI_SUCCESS);前一 HEAD a824b38 同三项 success。
|
||||
- 合入记录:Backend #229 → 74e5396861f1cdd5bed1c07206c9f28620f7d16a(树与 4357581 一致);Dev #656 HEAD a1c1ff0 → main 886283a4a534de8c418fa4a60d2d7a5d15c0ccfb(树一致,modules/backend=74e5396,归档卡存在);合入窗口 mq-dev-20261020-fast-slow-o2-p1-01 已领取并释放。
|
||||
- 清理记录:评测服务器 SIGTERM 正常退出、探针/驱动无残留进程;`TASK_WRITERS_STOPPED=1 make task-finish` 回收 worktree 与本地/远端分支;Backend fail-fast worktree 与分支、候选 Dev worktree(7e6c544,detached)确认干净后移除;仓外证据目录保留。
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T12:10:16.654753+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "873e957306a791eaa36e5251b45e3ed44a863143",
|
||||
"after_head": "873e957306a791eaa36e5251b45e3ed44a863143",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "873e957306a791eaa36e5251b45e3ed44a863143",
|
||||
"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=873e957306",
|
||||
"[remote] origin/main=873e957306 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[cards] 已合入未闭合任务卡:final-cleanup-summary.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] 873e957306(完整:873e957306a791eaa36e5251b45e3ed44a863143)",
|
||||
"[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": "43fd450fdf40fd114863abbf096d540a28ad89fb95c98575e1c199c2f62ac925",
|
||||
"receipt_sha256": "ae3bc540a8acf624a1c093784b1d2b0612ce4dd2c0bfe8f09932f166378c4e53",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/ae3bc540a8acf624a1c093784b1d2b0612ce4dd2c0bfe8f09932f166378c4e53.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #656(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/656)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,163 @@
|
||||
# feature/genui-prod-ops-docsync 任务卡
|
||||
|
||||
任务主记录:PR #677 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/677;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 444fb1dee2a72452be23b97cbaaad9e28c42dffd
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-genui-prod-ops-docsync(worktree);分支:feature/genui-prod-ops-docsync
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=19d3cdc801db7908de195f7e5ee2b8461c25c3ffcda23bc58aa2d95fb1c156bc;triage_receipt=/tmp/genui-prod-docsync/triage.json;receipt_sha256=e638023ee64c99d4ae8fe90a87ff285d91fcdd60ddb4a19cd252f3c0261a0d03
|
||||
|
||||
- 授权与交付终点:用户 2026-10-21「做」(迁 svc 管理 + 收口稿小修);方式裁定「只迁 pg1」。终点:Documents #118 与本 PR 合入、任务收口。未授权:prod 升级/schema 迁移。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:合入;当前增量:I1(文档同步 + gitlink)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 目标:GenUI 收口稿(Documents `docs/proposals/2026-10-12-generative-ui-usage-and-evaluation.md`)「部署与运行验收」条目与现状一致;Dev 锁定该 Documents 版本。
|
||||
- 仓外运维(不入仓,本卡只记事实):2026-10-21 `pg1` 由保留目录迁入主检出 `runtime/pg1`,经 `svc_manifest.py bulk-set` 显式重锚数据根后 `scripts/svc.sh pg1 start` 接管;prod(`dev@b4649d7`,无 identity 能力,svc 启动门禁拒绝)留保留目录,新增仓外 `prodctl.sh`(`env -i` + `AGENT_SYSTEM_DSN`)手工启停。
|
||||
- 非目标:升级 prod / schema v25→v51 迁移(用户选「只迁 pg1」);svc.sh 代码改动。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| documents-genui-prod-ops-docsync | Documents 收口稿单条目 | AGENT-EXECUTION.md | implementation | cursor→antigravity→pi-glm→pi→codex | 1 | cursor / auto(fresh process) | success | applied → Documents 244477e(#118) | 无 | /tmp/genui-prod-docsync/docs-run.json |
|
||||
| dev-genui-prod-ops-docsync-repin | modules/documents gitlink | AGENT-EXECUTION.md | implementation | cursor→antigravity→pi-glm→pi→codex | 1 | cursor / auto(fresh process) | success | applied → 3289c81 | 无 | /tmp/genui-prod-docsync/dev-run.json |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:待核对(无适用规划时说明原因)
|
||||
- 主记录路径/版本:待核对(路径按仓库根目录定位,归档后不变;只引用唯一正文)
|
||||
- 对应里程碑:待核对
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 待填写 | 待评估 | 待定 | 待定义 | 待确认 | 待评审 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:待填写(含对象版本)
|
||||
- 实施计划引用:待填写(含增量与需求 ID)
|
||||
- 测试计划引用:待填写(含需求 ID 与验收条件)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 待定 | 待执行 | — | — | — |
|
||||
| 需求评估 | 待定 | 待执行 | — | — | — |
|
||||
| 需求表确认 | 待定 | 待执行 | — | — | — |
|
||||
| 方案设计 | 待定 | 待执行 | — | — | — |
|
||||
| 实施计划 | 待定 | 待执行 | — | — | — |
|
||||
| 实施与自检 | 待定 | 待执行 | — | — | — |
|
||||
| 正式测试与验收 | 待定 | 待执行 | — | — | — |
|
||||
| 代码 Review | 待定 | 待执行 | — | — | — |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 444fb1dee2a72452be23b97cbaaad9e28c42dffd | 444fb1dee2a72452be23b97cbaaad9e28c42dffd | 2026-10-21T10:27:03Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:未入队;调度记录:无
|
||||
- entry_id:无;attempt_id:无;窗口执行者:无
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:Documents #118 合入(merge fcea466,条目改为 `dev@b4649d7`、prodctl.sh 手工启停、pg1 由 svc 管理、升级须另行授权);`modules/documents` 82b4619→fcea466(同时带入已合入的 Documents #117 IAM 记录)。
|
||||
- 验证:Documents #118 head 244477e CI governance=success(GATE,ci_wait CI_SUCCESS);Dev 以本 PR 当前 HEAD 的 CI 为准。仓外运维 SELF:`svc.sh list` 显示 pg1 运行中;prod `/readyz`=200、argv 无 DSN、进程环境仅 HOME/PATH/LANG/AGENT_SYSTEM_DSN。
|
||||
- 影响与剩余项:仅文档与 gitlink。切换停机约 2 分钟(首启继承调用方 `PGOPTIONS` 只读护栏失败,prodctl 改 `env -i` 后恢复)。prod 升级非本卡范围。
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:待核对(依据实际成果更新适用方案/规划)
|
||||
- 首选下一任务:待核对(无适用规划、已完成或受阻时如实说明,不虚构任务)
|
||||
- 推荐依据:待填写
|
||||
- 前置条件/待确认:待核对
|
||||
- 预期产物/验收条件:待定义
|
||||
- 重复任务核对:待核对(已有/在途任务、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-21T10:26:25.848135+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "444fb1dee2a72452be23b97cbaaad9e28c42dffd",
|
||||
"after_head": "444fb1dee2a72452be23b97cbaaad9e28c42dffd",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "444fb1dee2a72452be23b97cbaaad9e28c42dffd",
|
||||
"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=444fb1dee2",
|
||||
"[remote] origin/main=444fb1dee2 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=3",
|
||||
"[recycle] merged un-recycled branches: feature/card-close-20261021-085838Z",
|
||||
"[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] 444fb1dee2(完整:444fb1dee2a72452be23b97cbaaad9e28c42dffd)",
|
||||
"[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": "ea54f9121123c1a453cf9e1c66c4a733828a7166ed7fc26cdd9a02eb23e8c2e3",
|
||||
"receipt_sha256": "825a20b8f4d5d920996bbaa0d30eccc9a4718c7ae9b5f1400b244d398e90495d",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/825a20b8f4d5d920996bbaa0d30eccc9a4718c7ae9b5f1400b244d398e90495d.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #677(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/677)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | 096b74b4775e81cfb2864f98cae2e9fc9d9c1a44 | da2492de25ad6dcbf265a55a4246f088642489cf | 2026-10-21T11:40:43Z |
|
||||
@@ -0,0 +1,161 @@
|
||||
# feature/genui-prod-upgrade-docsync 任务卡
|
||||
|
||||
任务主记录:PR #679 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/679;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= e634608d97333354c3ead85d47c9e9e368a992a0
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-genui-prod-upgrade-docsync(worktree);分支:feature/genui-prod-upgrade-docsync
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=6f68e1ece9fe3a765dde7e98955e816bcb1e6c8f776739169191da3353604790;triage_receipt=/tmp/genui-prod-upgrade-doc/triage.json;receipt_sha256=52f7deacbc7f523892a5c6bdae42a3add141712558df706c9d93134ed7d32516
|
||||
|
||||
- 授权与交付终点:用户 2026-10-21 选择「升级生产并交给 svc 完整管理」。终点:prod 升级运行 + Documents #119 与本 PR 合入、任务收口。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:合入;当前增量:I1(升级运维 + 文档同步)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 目标:本机 GenUI `prod`(127.0.0.1:8787,`pg1/prod`)由 `dev@b4649d7`(schema v25,无 identity 能力,svc 不能接管)升级至 `dev@5360884`(v51)并由主检出 `scripts/svc.sh` 完整管理;收口稿部署条目同步。
|
||||
- 仓外运维(不入仓,本卡记事实):备份 `~/.local/share/agent-system-backups/genui-prod-upgrade-20261021T123748Z/`(prod.dump、prod-final.dump、prod-runtime.tar、保留目录整包 tar.gz);演练实例 `prodrh`(库副本)完整走 adopt → migrate → deploy 并验证后清除;正式:停服 12:41:39 → 迁入 `runtime/prod` 并 bulk-set 重锚 → `adopt --confirm svc-prod-f29fb8e57420` → 候选二进制 `agent-system migrate` → `svc.sh prod deploy --from dev@5360884` → 12:42:39 UI 就绪。新增 `superadmin`(凭据 `runtime/prod/super-admin.env` 0600,经 `AGENT_SYSTEM_ENV_FILE` 传入);原 `admin` 保留。保留目录打包后删除。
|
||||
- 非目标:GenUI 功能验收清单重跑(NOT_RUN);svc.sh 改动。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| documents-genui-prod-upgrade-docsync | Documents 收口稿单条目 | AGENT-EXECUTION.md | implementation | cursor→antigravity→pi-glm→pi→codex | 1 | cursor / auto(fresh process) | success | applied → Documents 518e381(#119) | 无 | /tmp/genui-prod-upgrade-doc/docs-run.json |
|
||||
| dev-genui-prod-upgrade-docsync-repin | modules/documents gitlink | AGENT-EXECUTION.md | implementation | cursor→antigravity→pi-glm→pi→codex | 1 | cursor / auto(fresh process) | success | applied → 137a18c | 无 | /tmp/genui-prod-upgrade-doc/dev-run.json |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:待核对(无适用规划时说明原因)
|
||||
- 主记录路径/版本:待核对(路径按仓库根目录定位,归档后不变;只引用唯一正文)
|
||||
- 对应里程碑:待核对
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 待填写 | 待评估 | 待定 | 待定义 | 待确认 | 待评审 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:待填写(含对象版本)
|
||||
- 实施计划引用:待填写(含增量与需求 ID)
|
||||
- 测试计划引用:待填写(含需求 ID 与验收条件)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 待定 | 待执行 | — | — | — |
|
||||
| 需求评估 | 待定 | 待执行 | — | — | — |
|
||||
| 需求表确认 | 待定 | 待执行 | — | — | — |
|
||||
| 方案设计 | 待定 | 待执行 | — | — | — |
|
||||
| 实施计划 | 待定 | 待执行 | — | — | — |
|
||||
| 实施与自检 | 待定 | 待执行 | — | — | — |
|
||||
| 正式测试与验收 | 待定 | 待执行 | — | — | — |
|
||||
| 代码 Review | 待定 | 待执行 | — | — | — |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | e634608d97333354c3ead85d47c9e9e368a992a0 | e634608d97333354c3ead85d47c9e9e368a992a0 | 2026-10-21T12:44:13Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:未入队;调度记录:无
|
||||
- entry_id:无;attempt_id:无;窗口执行者:无
|
||||
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
|
||||
- 调度依据:policy/delivery.md#合入调度
|
||||
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:prod 运行 `dev@5360884e`(svc 管理,desired=running,identity established,schema v51);Documents #119 合入(merge 2e9c3bc);`modules/documents` fcea466→2e9c3bc。
|
||||
- 验证:Documents #119 head 518e381 CI governance=success(GATE);Dev 以本 PR 当前 HEAD 的 CI 为准。仓外运维 SELF:healthz/readyz/UI=200;superadmin 登录 200、isSuperAdmin=true;未登录 /api/me/profile=401;argv 无 DSN;users(admin、superadmin)与 3 个会话保留。GenUI 功能验收清单 NOT_RUN。
|
||||
- 影响与剩余项:停机约 1 分钟。svc 不持久化超级管理员凭据:`scripts/svc.sh prod start/restart`(含 recover/watchdog)须带 `AGENT_SYSTEM_ENV_FILE=runtime/prod/super-admin.env`,否则服务拒绝启动(与 dev 同)。发现(非阻塞,未修):v27 前旧库经 svc 部署时身份门禁 ensure 报 identity table missing,须先手工 `agent-system migrate`;`svc.sh adopt` 在旧制品下实际保持 pending 却提示 established。
|
||||
|
||||
## 规划进展与下一步
|
||||
|
||||
- 本次推进/剩余:待核对(依据实际成果更新适用方案/规划)
|
||||
- 首选下一任务:待核对(无适用规划、已完成或受阻时如实说明,不虚构任务)
|
||||
- 推荐依据:待填写
|
||||
- 前置条件/待确认:待核对
|
||||
- 预期产物/验收条件:待定义
|
||||
- 重复任务核对:待核对(已有/在途任务、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-21T12:43:34.416643+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "e634608d97333354c3ead85d47c9e9e368a992a0",
|
||||
"after_head": "e634608d97333354c3ead85d47c9e9e368a992a0",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "e634608d97333354c3ead85d47c9e9e368a992a0",
|
||||
"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=e634608d97",
|
||||
"[remote] origin/main=e634608d97 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=3",
|
||||
"[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] e634608d97(完整:e634608d97333354c3ead85d47c9e9e368a992a0)",
|
||||
"[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": "780774c4e44bd6f550b9d22ee2db80a7ae56d999c3f130cec70d8845b21aff16",
|
||||
"receipt_sha256": "da371db8c87cef44543025eedbc4f3112b08d6fd0478b4072a1590b844eb9618",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/da371db8c87cef44543025eedbc4f3112b08d6fd0478b4072a1590b844eb9618.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #679(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/679)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,244 @@
|
||||
# feature/project-access-lifecycle-u1-role-project-access 任务卡
|
||||
|
||||
任务主记录:PR #637 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/637;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 3e70d76a20a6caf6f2edfd738a86f501c31064b6
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u1-role-project-access(worktree);分支:feature/project-access-lifecycle-u1-role-project-access
|
||||
- 分支类型:child;initiative_id:feature-project-access-lifecycle;root_target:main;immediate_target:feature/project-access-lifecycle;accepting_requirements:false;parent_branch:feature/project-access-lifecycle;parent_head_sha:a8557f27ed8a1708f814539d7161364daea15dd5;parent_pr:无;parent_source:origin
|
||||
- 需求分流:免(父 initiative 子任务登记已列出 feature/project-access-lifecycle-u1-role-project-access;parent_head_sha=a8557f27ed8a1708f814539d7161364daea15dd5)
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式: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 | — |
|
||||
| pal-u1a Backend | 角色权限收窄为项目访问布尔 + R13 门禁拆分 + 管理员去旁路 + 归一化 | AGENT-EXECUTION digest(见各收据) | implementation | cursor→agy→pi | 4 次运行:scope-expansion(测试文件未授权)、scope-expansion(仓内 scratch)、failed-unknown(cursor 网络中断)、success | cursor(success 次为 cursor,session not-observable) | success | 已应用 cf9c35a(c8b1b7e→cf9c35a) | never | /tmp/pal-u1a-run.json、run2、run3、run4.json |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 3e70d76a20a6caf6f2edfd738a86f501c31064b6 | a8557f27ed8a1708f814539d7161364daea15dd5 | 2026-10-19T14:59:41Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
| pal-u1b Frontend | 角色编辑收窄为单项目访问布尔 + R13 门禁(roles.manage)+ 旧模型测试重写 | AGENT-EXECUTION digest | implementation | cursor→agy→pi | 1 | cursor(session not-observable) | success | 已应用 2c19439(dab1605→2c19439) | never | /tmp/pal-u1b-run.json |
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:独立审查(全新 cursor 会话,无 resume,未参与实现;输出存 /tmp/pal-u1-review-output.txt)结论 BLOCKED(1 项):缺计划 U1 第 9 条测试(provider 缺失/不兼容拒绝创建项目);五项重点全部成立。阻塞项已由 repair 修复(transport 03646b5,测试 internal/server/role_authorization_test.go 新增场景并 PASS),闭环依据:修复后过滤套件 ok + make test-affected PASS(/tmp/pal-u1-affected3.json)。另:agent-exec review 路由两次 pi 尝试均未产出报告文件(failed-unknown,收据 /tmp/pal-u1-review-run.json、review2-run.json,工作区已清理),改用上述全新会话方式。(审阅智能体/平台身份、base/head、结论、问题闭合及重放依据)
|
||||
- 冲突记录:task-sync 于合入前执行;组合 pin 期间发生一次误配(pins2 将前端回退 dab1605),已由 pins3 纠正为 backend 03646b5 + frontend 2c19439(7519672),组合门禁以终态对象通过。(最新目标同步、冲突处置、组合兼容核验及复审/补测)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-19T14:58:45.588024+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle",
|
||||
"before_head": "a8557f27ed8a1708f814539d7161364daea15dd5",
|
||||
"after_head": "a8557f27ed8a1708f814539d7161364daea15dd5",
|
||||
"branch": "feature/project-access-lifecycle",
|
||||
"target_branch": "main",
|
||||
"target_head": "3e70d76a20a6caf6f2edfd738a86f501c31064b6",
|
||||
"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-wt-feature-project-access-lifecycle'",
|
||||
"[repo] branch=feature/project-access-lifecycle head=a8557f27ed",
|
||||
"[remote] origin/main=3e70d76a20 ahead=3 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[recycle] merged un-recycled branches: feature/layered-python-env",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 3e70d76a20(完整:3e70d76a20a6caf6f2edfd738a86f501c31064b6)",
|
||||
"[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-wt-feature-project-access-lifecycle'"
|
||||
],
|
||||
"output_sha256": "4dbe19daf89259d609baf4cb3df0a993029edc91d8b1f0889987bd6d74a59761",
|
||||
"receipt_sha256": "7e8ed3a646ae25996e75763ca1d199723ce86193f84ebea48ed622f7e036d0ee",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/7e8ed3a646ae25996e75763ca1d199723ce86193f84ebea48ed622f7e036d0ee.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-19T15:41:10.347615+00:00",
|
||||
"command": "sh -c 'cd modules/backend && GOWORK=off go build ./... && GOWORK=off go test ./internal/authz/... ./internal/store ./internal/server -run \"Role|Policy|Catalog|Access|Super\" -count=1 && GOWORK=off go vet ./internal/authz/... ./internal/store ./internal/server && git status --porcelain | wc -l'",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u1-role-project-access",
|
||||
"before_head": "a455919908d8949988aafc7b984c8eb870dcba6d",
|
||||
"after_head": "a455919908d8949988aafc7b984c8eb870dcba6d",
|
||||
"branch": "feature/project-access-lifecycle-u1-role-project-access",
|
||||
"target_branch": "main",
|
||||
"target_head": "2b7fdc39a1f44163f4766701429d61ffd6926f1a",
|
||||
"root_target": "main",
|
||||
"immediate_target": "feature/project-access-lifecycle",
|
||||
"before_tracked_diff_sha256": "59bad28b181e892816c6c432ef14deb2e178ab05190f808e8dd292fabc33e74f",
|
||||
"after_tracked_diff_sha256": "59bad28b181e892816c6c432ef14deb2e178ab05190f808e8dd292fabc33e74f",
|
||||
"output_tail": [
|
||||
"? \tagent-system-backend/internal/authz\t[no test files]",
|
||||
"ok \tagent-system-backend/internal/authz/catalog\t0.002s",
|
||||
"ok \tagent-system-backend/internal/store\t4.029s",
|
||||
"ok \tagent-system-backend/internal/server\t3.266s",
|
||||
"0"
|
||||
],
|
||||
"output_sha256": "3546e0a0a2c49bf4b92853a0b97e746823398a5f19c3f4f028d0ff8f015a4c4d",
|
||||
"receipt_sha256": "80c009bd4f0750a8be9fc3e6bcf71ed3631645e88f0b7b8210c70a35dff62e81",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/80c009bd4f0750a8be9fc3e6bcf71ed3631645e88f0b7b8210c70a35dff62e81.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-19T15:48:12.454853+00:00",
|
||||
"command": "sh -c 'cd modules/frontend && npx vitest run test/role-permissions.test.ts test/role-permissions-ui.test.ts test/account-users-panel.test.ts test/design-guard.test.ts 2>&1 | tail -3 && npx tsc -b && echo TSC_OK'",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u1-role-project-access",
|
||||
"before_head": "d2e78d53be5d7a9eb549ee87180685feed385fa9",
|
||||
"after_head": "d2e78d53be5d7a9eb549ee87180685feed385fa9",
|
||||
"branch": "feature/project-access-lifecycle-u1-role-project-access",
|
||||
"target_branch": "main",
|
||||
"target_head": "2b7fdc39a1f44163f4766701429d61ffd6926f1a",
|
||||
"root_target": "main",
|
||||
"immediate_target": "feature/project-access-lifecycle",
|
||||
"before_tracked_diff_sha256": "79fc127e44f0bcb60eca463a19384c65e9f3a70c781dc2ff72d3395affe9dfae",
|
||||
"after_tracked_diff_sha256": "79fc127e44f0bcb60eca463a19384c65e9f3a70c781dc2ff72d3395affe9dfae",
|
||||
"output_tail": [
|
||||
" Start at 15:48:12",
|
||||
" Duration 385ms (transform 439ms, setup 0ms, import 634ms, tests 27ms, environment 0ms)",
|
||||
"",
|
||||
"TSC_OK"
|
||||
],
|
||||
"output_sha256": "9241d526c3f2dac26fbd1d7745047cacdf505c4ddd9f525884f1e5f7aca5d8a9",
|
||||
"receipt_sha256": "998b331751b5e987ae229d179674ae99af97ac850ed87a496bdc011b8f175495",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/998b331751b5e987ae229d179674ae99af97ac850ed87a496bdc011b8f175495.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-19T15:49:58.382245+00:00",
|
||||
"command": "sh -c 'umask 0022 && make test-affected'",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u1-role-project-access",
|
||||
"before_head": "ef8d142a8a56984d3d674edf3465e6165152961f",
|
||||
"after_head": "ef8d142a8a56984d3d674edf3465e6165152961f",
|
||||
"branch": "feature/project-access-lifecycle-u1-role-project-access",
|
||||
"target_branch": "main",
|
||||
"target_head": "2b7fdc39a1f44163f4766701429d61ffd6926f1a",
|
||||
"root_target": "main",
|
||||
"immediate_target": "feature/project-access-lifecycle",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"(node:4185712) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.",
|
||||
"(Use `node --trace-warnings ...` to show where the warning was created)",
|
||||
"(node:4185778) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.",
|
||||
"(Use `node --trace-warnings ...` to show where the warning was created)",
|
||||
"(node:4185713) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.",
|
||||
"(Use `node --trace-warnings ...` to show where the warning was created)",
|
||||
"",
|
||||
" Test Files 49 passed (49)",
|
||||
" Tests 588 passed (588)",
|
||||
" Start at 15:50:33",
|
||||
" Duration 797ms (transform 6.87s, setup 0ms, import 11.31s, tests 1.10s, environment 3ms)",
|
||||
"",
|
||||
"knip unused exports 57 <= 58",
|
||||
"make[2]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u1-role-project-access'",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u1-role-project-access'"
|
||||
],
|
||||
"output_sha256": "a8a060112b3e15dd817d7b66314a5ca89ce0ef1f5cc17edba7a939f1278d79fb",
|
||||
"receipt_sha256": "e60210ccc7eda17e55ca0c72d4ab7b635e1f45650b5d7eb80ffcaea0e1e3a24f",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/e60210ccc7eda17e55ca0c72d4ab7b635e1f45650b5d7eb80ffcaea0e1e3a24f.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-19T16:08:30.705187+00:00",
|
||||
"command": "sh -c 'umask 0022 && make test-affected'",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u1-role-project-access",
|
||||
"before_head": "75196728eb462b1e18579e4657ffab84ac54dde2",
|
||||
"after_head": "75196728eb462b1e18579e4657ffab84ac54dde2",
|
||||
"branch": "feature/project-access-lifecycle-u1-role-project-access",
|
||||
"target_branch": "main",
|
||||
"target_head": "2b7fdc39a1f44163f4766701429d61ffd6926f1a",
|
||||
"root_target": "main",
|
||||
"immediate_target": "feature/project-access-lifecycle",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"(node:414845) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.",
|
||||
"(Use `node --trace-warnings ...` to show where the warning was created)",
|
||||
"(node:414911) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.",
|
||||
"(Use `node --trace-warnings ...` to show where the warning was created)",
|
||||
"(node:414846) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.",
|
||||
"(Use `node --trace-warnings ...` to show where the warning was created)",
|
||||
"",
|
||||
" Test Files 49 passed (49)",
|
||||
" Tests 588 passed (588)",
|
||||
" Start at 16:09:04",
|
||||
" Duration 806ms (transform 7.07s, setup 0ms, import 11.27s, tests 1.17s, environment 4ms)",
|
||||
"",
|
||||
"knip unused exports 57 <= 58",
|
||||
"make[2]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u1-role-project-access'",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u1-role-project-access'"
|
||||
],
|
||||
"output_sha256": "6575ab5b5ba1b87a87736b5dec8f2be390bee0f9a6fe499825332e5d2027c53b",
|
||||
"receipt_sha256": "43973a45aae37e8b5aec60342ac1ddc013b8e292f9044a413272f8d983972ae1",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/43973a45aae37e8b5aec60342ac1ddc013b8e292f9044a413272f8d983972ae1.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #637(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/637)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,132 @@
|
||||
# feature/project-access-lifecycle-u2-creator-management 任务卡
|
||||
|
||||
任务主记录:PR #640 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/640;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 2b7fdc39a1f44163f4766701429d61ffd6926f1a
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u2-creator-management(worktree);分支:feature/project-access-lifecycle-u2-creator-management
|
||||
- 分支类型:child;initiative_id:feature-project-access-lifecycle;root_target:main;immediate_target:feature/project-access-lifecycle;accepting_requirements:false;parent_branch:feature/project-access-lifecycle;parent_head_sha:a32e2227b0d4f77c15581fdc475a22edc4d10823;parent_pr:无;parent_source:origin
|
||||
- 需求分流:免(父 initiative 子任务登记已列出 feature/project-access-lifecycle-u2-creator-management;parent_head_sha=a32e2227b0d4f77c15581fdc475a22edc4d10823)
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式: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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | 2b7fdc39a1f44163f4766701429d61ffd6926f1a | a32e2227b0d4f77c15581fdc475a22edc4d10823 | 2026-10-19T16:34:33Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
| pal-u2 RDM | creator 管理权威收敛(有效项目访问+精确 createdBy;override 不增不减)+ 旧语义测试重写 | AGENT-EXECUTION digest | implementation | cursor→agy→pi | 16 次运行:2 次 cursor 网络 TLS 失败、2 次合理 scope-expansion(perms_api.go/测试文件)、2 次 local_config(worker 跑 submodule update 触发监管)、2 次 blocked(pins 嵌套依赖缺失)、1 次终态记录失足、其余为逐轮修正重派;末次 success | cursor(R10 配方:-modfile 覆盖 + pin 自愈;session not-observable) | success | 已应用 853121d(5a27d78→853121d,RDM 仓分支 feature/project-access-lifecycle-u2-creator-management 已推送) | never | /tmp/pal-u2-run*.json(run16 为成功收据) |
|
||||
|
||||
| pal-u2 Review 修复 | fail-open 旁路修复(legacy 模式 fail-closed)+ 13 个 Skip 回归测试恢复 | AGENT-EXECUTION digest | repair | cursor→agy→pi | 2 次运行(首次终态记录失足,第二次 success) | cursor(session not-observable) | success | 已应用 aa1a7d2(853121d→aa1a7d2) | never | /tmp/pal-u2-fix2-run.json、fix2b-run.json |
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:独立审查(全新 cursor 会话,未参与实现;输出 /tmp/pal-u2-review-output.txt)结论 BLOCKED(2):①legacy readiness 模式管理判定 fail-open;②13 个 host_projection 回归测试被 Skip。两项已由 repair(aa1a7d2)修复:fail-closed 语义 + 全部恢复并按新语义通过;rdmapp 全量 3 包 ok、Skip=0。非阻塞建议 3 条(canManageProject 死参数收敛、成员入口重复调用清理、场景映射注释)留后续增量。(审阅智能体/平台身份、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-19T16:33:45.887202+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle",
|
||||
"before_head": "5eb8591957a89a269326d52d53a982fbc157dba1",
|
||||
"after_head": "5eb8591957a89a269326d52d53a982fbc157dba1",
|
||||
"branch": "feature/project-access-lifecycle",
|
||||
"target_branch": "main",
|
||||
"target_head": "2b7fdc39a1f44163f4766701429d61ffd6926f1a",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle'",
|
||||
"[repo] branch=feature/project-access-lifecycle head=5eb8591957",
|
||||
"[remote] origin/main=2b7fdc39a1 ahead=5 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=6",
|
||||
"[recycle] merged un-recycled branches: feature/layered-python-env",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 2b7fdc39a1(完整:2b7fdc39a1f44163f4766701429d61ffd6926f1a)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle'"
|
||||
],
|
||||
"output_sha256": "f4c03343235843eaf5f99905576d9e5c3133939f633b4fcaf227aeb70fdadbdb",
|
||||
"receipt_sha256": "2bfa8f889300bce9ad9f556e8abb6e23b5730a252a76d286f02f4a103a7f13ca",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/2bfa8f889300bce9ad9f556e8abb6e23b5730a252a76d286f02f4a103a7f13ca.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-19T20:33:08.272772+00:00",
|
||||
"command": "sh -c 'cd /home/weichen/projects/Agent-System-RDM-wt-pal-u2 && GOWORK=off go build ./internal/projects/... ./internal/rdmapp/... ./internal/perms/... ./internal/members/... && GOWORK=off go test ./internal/perms/... ./internal/members/... ./internal/projects/... -count=1 2>&1 | tail -1 && GOWORK=off go test ./internal/rdmapp/... -count=1 2>&1 | grep -cE \"^ok\" && GOWORK=off go vet ./internal/perms ./internal/members ./internal/rdmapp && echo VET_OK && git status --porcelain | wc -l'",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u2-creator-management",
|
||||
"before_head": "4e3d5240e759ab80f2a481c0f4d6465119ee0c37",
|
||||
"after_head": "4e3d5240e759ab80f2a481c0f4d6465119ee0c37",
|
||||
"branch": "feature/project-access-lifecycle-u2-creator-management",
|
||||
"target_branch": "main",
|
||||
"target_head": "f07a53d86aa10d35be0d3519fcf3c493b3391fec",
|
||||
"root_target": "main",
|
||||
"immediate_target": "feature/project-access-lifecycle",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"ok \tagent-system-rdm/internal/projects\t0.145s",
|
||||
"3",
|
||||
"VET_OK",
|
||||
"0"
|
||||
],
|
||||
"output_sha256": "f2e306cfce2ea88f542f0bbb1671e5d70931120990c6936b3a9b80bc82db49cd",
|
||||
"receipt_sha256": "8d6760af9d8f734b5c7212ea1f18d25835e46864a8603e0ce1aaba4ae5be7a91",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/8d6760af9d8f734b5c7212ea1f18d25835e46864a8603e0ce1aaba4ae5be7a91.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #640(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/640)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,131 @@
|
||||
# feature/project-access-lifecycle-u3-member-history 任务卡
|
||||
|
||||
任务主记录:PR #646 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/646;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= f07a53d86aa10d35be0d3519fcf3c493b3391fec
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u3-member-history(worktree);分支:feature/project-access-lifecycle-u3-member-history
|
||||
- 分支类型:child;initiative_id:feature-project-access-lifecycle;root_target:main;immediate_target:feature/project-access-lifecycle;accepting_requirements:false;parent_branch:feature/project-access-lifecycle;parent_head_sha:c9ca59b195c17abfbcb29ed7a7b958bc69fb22ab;parent_pr:无;parent_source:origin
|
||||
- 需求分流:免(父 initiative 子任务登记已列出 feature/project-access-lifecycle-u3-member-history;parent_head_sha=c9ca59b195c17abfbcb29ed7a7b958bc69fb22ab)
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式: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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | f07a53d86aa10d35be0d3519fcf3c493b3391fec | c9ca59b195c17abfbcb29ed7a7b958bc69fb22ab | 2026-10-20T01:17:12Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
| pal-u3a RDM(部分交付) | 成员状态机(active/removed/absent + removed_at + 迁移回填);Remove 保留行;re-add 恢复 active;IsMember 只认 active;被移除成员共享域只读放行(doc.read 前置解耦);写全拒与隐私负控 | AGENT-EXECUTION digest | implementation | cursor→agy→pi | 3 次运行(2 次网络/中断、1 次 blocked 未用 -modfile 配方)后 success | cursor(session not-observable) | success | 已应用 62c77ec(aa1a7d2→62c77ec,RDM 仓分支已推送) | never | /tmp/pal-u3a-run3.json |
|
||||
|
||||
| pal-u3b Backend | 被移除成员本人会话只读路径(经唯一资源授权面)+ 写门全拒负控 | AGENT-EXECUTION digest | implementation | cursor(配额禁用)→antigravity(端点禁用)→pi | 6 次运行(详见下);末次 pi success | pi(session not-observable) | success | 已应用 d3380de(03646b5→d3380de,Backend 仓分支已推送) | never | /tmp/pal-u3b-run*.json(run12 为成功收据) |
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:U3a(RDM 侧)+ U3b(Backend 侧)均已交付;U3 整体待独立 Review 与合入。
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:U3a 待独立 Review(RDM 分支 62c77ec)。U3b:host 主模型 Review(实现由 pi 完成,三个 CLI 候选当前不可用:cursor 配额禁用、agy 端点 EOF×3 禁用、pi 为本单元实现者)——结论 PASS:读决策唯一化(projectRead 经域内资源授权面,removed+共享域放行)、写全走 projectWork、无授权器时行为不变、fail-closed;依据 server.go:876/1063/1459/1558/1618/1647 与新增 250 行测试。临时路由变更记录:Backend AGENT-EXECUTION.md 于 001ba38(cursor 配额禁用)、5543a44(agy 端点禁用),随本 PR 可审计,配额/端点恢复后应改回。(审阅智能体/平台身份、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-20T01:16:15.775522+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle",
|
||||
"before_head": "c9ca59b195c17abfbcb29ed7a7b958bc69fb22ab",
|
||||
"after_head": "c9ca59b195c17abfbcb29ed7a7b958bc69fb22ab",
|
||||
"branch": "feature/project-access-lifecycle",
|
||||
"target_branch": "main",
|
||||
"target_head": "f07a53d86aa10d35be0d3519fcf3c493b3391fec",
|
||||
"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-wt-feature-project-access-lifecycle'",
|
||||
"[repo] branch=feature/project-access-lifecycle head=c9ca59b195",
|
||||
"[remote] origin/main=f07a53d86a ahead=23 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] f07a53d86a(完整:f07a53d86aa10d35be0d3519fcf3c493b3391fec)",
|
||||
"[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-wt-feature-project-access-lifecycle'"
|
||||
],
|
||||
"output_sha256": "584a369ff09522a79ecca05db2987a558d00d82789b6055b44b7f6beb6a4a33a",
|
||||
"receipt_sha256": "87a4a678d7471e64c1497f0e9b584e8b3114296fe3a935fe61376209923e3ffe",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/87a4a678d7471e64c1497f0e9b584e8b3114296fe3a935fe61376209923e3ffe.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T05:00:39.146940+00:00",
|
||||
"command": "sh -c 'cd modules/backend && GOWORK=off go build ./internal/... 2>/dev/null; GOWORK=off go test ./internal/server -run \"Project|Conversation|Message|Member\" -count=1 2>&1 | tail -1 && GOWORK=off go test ./internal/authz/... -count=1 2>&1 | tail -1 && GOWORK=off go vet ./internal/server ./internal/authz && echo VET_OK && git status --porcelain | wc -l'",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u3-member-history",
|
||||
"before_head": "bd7ae9a892c32f158fb60cbd705c0fd781c5fa94",
|
||||
"after_head": "bd7ae9a892c32f158fb60cbd705c0fd781c5fa94",
|
||||
"branch": "feature/project-access-lifecycle-u3-member-history",
|
||||
"target_branch": "main",
|
||||
"target_head": "d2035c76dc1cb15e01d2c184af5155d9d773cd9f",
|
||||
"root_target": "main",
|
||||
"immediate_target": "feature/project-access-lifecycle",
|
||||
"before_tracked_diff_sha256": "d16db70897f470c2a2d69a393757ded896703b26562be1c19e29c63d2da49b1d",
|
||||
"after_tracked_diff_sha256": "d16db70897f470c2a2d69a393757ded896703b26562be1c19e29c63d2da49b1d",
|
||||
"output_tail": [
|
||||
"ok \tagent-system-backend/internal/server\t12.943s",
|
||||
"ok \tagent-system-backend/internal/authz/catalog\t0.001s",
|
||||
"VET_OK",
|
||||
"0"
|
||||
],
|
||||
"output_sha256": "606658b1f5cbf647603a229c72d25dd53ca7f5dcf84ff1dafdf2fd0eb822e069",
|
||||
"receipt_sha256": "1a1f139555b65e7b82dd9ccbf5a827510d89651f2d5abb87484eb71d930257b1",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/1a1f139555b65e7b82dd9ccbf5a827510d89651f2d5abb87484eb71d930257b1.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #646(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/646)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,129 @@
|
||||
# feature/project-access-lifecycle-u4-work-states 任务卡
|
||||
|
||||
任务主记录:PR #654 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/654;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= d2035c76dc1cb15e01d2c184af5155d9d773cd9f
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u4-work-states(worktree);分支:feature/project-access-lifecycle-u4-work-states
|
||||
- 分支类型:child;initiative_id:feature-project-access-lifecycle;root_target:main;immediate_target:feature/project-access-lifecycle;accepting_requirements:false;parent_branch:feature/project-access-lifecycle;parent_head_sha:028c36d13fb67d62c9884306ecb94135d26a8d1b;parent_pr:无;parent_source:origin
|
||||
- 需求分流:免(父 initiative 子任务登记已列出 feature/project-access-lifecycle-u4-work-states;parent_head_sha=028c36d13fb67d62c9884306ecb94135d26a8d1b)
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式: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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | d2035c76dc1cb15e01d2c184af5155d9d773cd9f | 028c36d13fb67d62c9884306ecb94135d26a8d1b | 2026-10-20T05:50:13Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
| pal-u4 RDM | 项目/成员工作状态机(work_state、suspended)+ 工作判定 + 管理命令入口 + 审计 + removed/suspended 只读边界的可见性修复 | AGENT-EXECUTION digest | implementation | cursor(配额误报禁用后恢复)→agy(区域/EOF 禁用)→pi(限流)→cursor(--model auto 修复后) | 多轮(详见收据) | cursor(--model auto)+pi | success×2(f54c769 状态机、7f43da6 review 修复) | 已应用 f54c769、7f43da6 | never | /tmp/pal-u4-run2.json、/tmp/pal-u4-fix3b-run.json |
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:U4 已交付(RDM 分支 feature/project-access-lifecycle-u4-work-states @ 7f43da6,已推送):work_state/suspended 状态机与迁移、工作判定落点、creator 的 close/reopen/suspend/reactivate 命令入口与审计、时间比较与 doc-read 检查并行的只读边界(修掉 Review 指出的“新增可见性”缺陷)。
|
||||
- 验证:<结论摘要 PASS/FAIL/SKIP/NOT_RUN 逐项;逐条证据由 make evidence 写入上方证据表>
|
||||
- 影响与剩余项:
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:独立审查(全新 cursor 会话,--model auto 修复后可用)结论 BLOCKED(2):①removed/suspended 共享读取跳过 doc.read 三查且无时间比较(新增可见性,源自 U3a 基线、U4 扩大);②状态转换缺命令入口与审计。两项已由 repair(7f43da6)修复并有新增负控测试(TestFormerMemberSliceReadExcludesLaterPublication 等),rdmapp 全量绿、Skip=0。报告 /tmp/pal-u4-review-output.txt。(审阅智能体/平台身份、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-20T05:49:21.114317+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle",
|
||||
"before_head": "028c36d13fb67d62c9884306ecb94135d26a8d1b",
|
||||
"after_head": "028c36d13fb67d62c9884306ecb94135d26a8d1b",
|
||||
"branch": "feature/project-access-lifecycle",
|
||||
"target_branch": "main",
|
||||
"target_head": "d2035c76dc1cb15e01d2c184af5155d9d773cd9f",
|
||||
"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-wt-feature-project-access-lifecycle'",
|
||||
"[repo] branch=feature/project-access-lifecycle head=028c36d13f",
|
||||
"[remote] origin/main=d2035c76dc ahead=30 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] d2035c76dc(完整:d2035c76dc1cb15e01d2c184af5155d9d773cd9f)",
|
||||
"[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-wt-feature-project-access-lifecycle'"
|
||||
],
|
||||
"output_sha256": "056f1cc21e09910b01081fae0ec088394c2f522d8e4de15620c6f687876c76d0",
|
||||
"receipt_sha256": "8dd3dfce03454527c8f4f5c127f50a70bbcefd1ec1cfa912bee25a1a1fed7bb3",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/8dd3dfce03454527c8f4f5c127f50a70bbcefd1ec1cfa912bee25a1a1fed7bb3.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T06:27:50.962524+00:00",
|
||||
"command": "sh -c 'cd /home/weichen/projects/Agent-System-RDM-wt-pal-u2 && GOWORK=off GOFLAGS=-modfile=/tmp/pal-u3.mod go test ./internal/members/... ./internal/projects/... ./internal/perms/... -count=1 2>&1 | tail -1 && GOWORK=off GOFLAGS=-modfile=/tmp/pal-u3.mod go test ./internal/rdmapp/... -count=1 2>&1 | grep -cE \"^ok\" && GOWORK=off go vet ./internal/members ./internal/projects ./internal/rdmapp && echo VET_OK && git status --porcelain | wc -l'",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle-u4-work-states",
|
||||
"before_head": "607c3c209fd68903086adb868580d175dcbec0d0",
|
||||
"after_head": "607c3c209fd68903086adb868580d175dcbec0d0",
|
||||
"branch": "feature/project-access-lifecycle-u4-work-states",
|
||||
"target_branch": "main",
|
||||
"target_head": "65e4aa39592ed8f11b5385d6665aab6a883ae345",
|
||||
"root_target": "main",
|
||||
"immediate_target": "feature/project-access-lifecycle",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"ok \tagent-system-rdm/internal/perms\t0.019s",
|
||||
"3",
|
||||
"VET_OK",
|
||||
"0"
|
||||
],
|
||||
"output_sha256": "0ea9c6e9427f885022bfb5c62cdb746ac8a178a81febea8da3931b995ff9cc58",
|
||||
"receipt_sha256": "28d77a6df794aecb5cb43d8bb8c62195af3d54feb6b8c58f98cf8fea0f9cdb2d",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/28d77a6df794aecb5cb43d8bb8c62195af3d54feb6b8c58f98cf8fea0f9cdb2d.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #654(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/654)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,231 @@
|
||||
# feature/project-access-lifecycle 任务卡
|
||||
|
||||
任务主记录:PR #634 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/634(merge 1850d03563);状态:已完成(回填);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 3e70d76a20a6caf6f2edfd738a86f501c31064b6
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle(worktree);分支:feature/project-access-lifecycle
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=72ce129300a749b532d1d2806e247cdcd412bb19e927a50d5b16be827b398f5e;triage_receipt=/tmp/access-lifecycle-triage.json;receipt_sha256=399267dd5f0e1d3e1ba1bf1ccf52532b386535fef618d03985b46d36e8f6c800
|
||||
|
||||
- 授权与交付终点:按计划 docs/superpowers/plans/2026-10-19-1440-feat-project-access-lifecycle-plan.md 实施 U1–U6:角色权限收窄为单一项目访问布尔(含 R13 门禁修正与旧数据兼容归一)、creator 管理权威、成员历史与只读移除、项目/成员工作状态、固定模板库、界面呈现。终点:L1 分支经组合验证与 CI 后合入 main;不含部署、生产数据迁移、发布/打标与线上权限改写(计划 Verification Contract 明确排除)。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:U3 实施前须确认「移除成员后的历史可见范围」(计划 Deferred Planning Questions,当前按 KTD6 保守默认);其余按既有边界
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
|
||||
- 当前阶段:需求拆解;当前增量:I1(待定义)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 章程(charter)
|
||||
|
||||
**用途与目标**:按计划实现「项目访问与生命周期管理」——角色配置收窄为单一「项目访问」布尔(旧 allowed/capAllowed 按原有效结果 allowed&&capAllowed 兼容归一,写时双写两列);角色管理者凭 system.roles.manage 即可配置该权限(R13,系统授权与平台上限仍由 system.permissions.manage 保护);系统管理员不再默认绕过项目创建检查;项目管理 = 有效项目访问 + 精确 creator 身份;成员/项目工作状态(active/suspended/removed、active/closed)可逆且在每个执行边界重验;固定模板库全量入册、按需实例化;不含文档职责分配。
|
||||
|
||||
**接纳范围**:计划 R1–R13;单元 U1–U6(U1 角色收窄+门禁修正;U2 creator 管理权威;U3 成员历史与只读移除;U4 工作状态;U5 模板库;U6 界面呈现);AE1–AE11。
|
||||
|
||||
**非目标**:文档职责分配的新增/接线;归档/恢复行为变更;项目转移创建者;项目删除;全局同名唯一性变更;部署、生产迁移、发布/打标、线上权限改写;私人内容对系统管理员开放。
|
||||
|
||||
**分级测试策略**:单元→L2:worker 自检 + host 最小直接检查(make test-affected)+ 全新会话独立 Review;轻量 CI。L1→main:task-sync + 完整组合验证 + 完整 CI(test/test-pg/svc-realcombo)+ 三段证据 + 独立 Review。
|
||||
|
||||
**验收条件**:计划 Success Criteria 全部成立;AE1–AE11 在真实组合通过;Definition of Done 逐项满足(不含部署项)。
|
||||
|
||||
**追加领域规则**:
|
||||
- 角色授权判定只消费规范化后的项目访问布尔;旧 system/user 行对角色授权无效且不得出现在角色能力中。
|
||||
- system.grants 与平台上限写入门禁保持 system.permissions.manage,不得因 R13 放宽。
|
||||
- 创建者身份只能来自服务端认证与不可变 createdBy;任何 owner 标签、调用方字段、RDM 域管理员、系统管理员都不得绕过。
|
||||
- 成员移除/停用与项目关闭在每个副作用边界重验;移除历史保留且可审计。
|
||||
|
||||
**生命周期**:建立即开本 PR(base main,[WIP])作分流发现;子任务按登记推进;最终一次性合入 main 后归档回收。
|
||||
|
||||
**关联方向与规划**:主记录 docs/superpowers/plans/2026-10-19-1440-feat-project-access-lifecycle-plan.md(本分支提交);关联 #628(其角色模型由本计划取代,衔接由 L1 owner 处理)。
|
||||
|
||||
## 子任务登记
|
||||
|
||||
| 子任务分支 | 必需 | 状态 | PR | 单元 | 范围摘要 |
|
||||
|---|---|---|---|---|---|
|
||||
| feature/project-access-lifecycle-u1-role-project-access | 是 | 已登记,待派发 | — | U1 | Backend catalog/service/role_policy/migrate/roles.go + Frontend rolePermissions.ts/RolePermissionsPanel.tsx:单一项目访问布尔、R13 门禁拆分、旧数据归一;R1/R2/R9/R11/R13 |
|
||||
| feature/project-access-lifecycle-u2-creator-management | 是 | 待启动(依赖 U1) | — | U2 | RDM projects_api/system_projects/perms:有效项目访问+精确 creator 的管理权威 |
|
||||
| feature/project-access-lifecycle-u3-member-history | 是 | 待启动(依赖 U2;实施前须确认历史可见范围) | — | U3 | RDM members/projects_api/system_projects/private_doc_views + Backend server.go:移除/停用即时停工、只读历史 |
|
||||
| feature/project-access-lifecycle-u4-work-states | 是 | 待启动(依赖 U2/U3) | — | U4 | 项目 active/closed 与成员 active/suspended 状态、迁移与全边界重验 |
|
||||
| feature/project-access-lifecycle-u5-template-catalog | 是 | 已交付待合入(RDM 分支 @ 0f97deb 已推送:inventory.json 414 文件全量盘点、130 项差距诚实列出、catalog 44 条已核验覆盖、项目选用接线测试;rdmapp 全量绿) | — | U5 | RDM 模板清单刷新(44 项 DHF+)、目录连接项目浏览/选择、来源/版本溯源 |
|
||||
| feature/project-access-lifecycle-u6-ui-presentation | 是 | 待启动(依赖 U1–U5 合同) | — | U6 | Frontend 角色/成员/项目/模板界面呈现与只读态 |
|
||||
|
||||
## 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 | — |
|
||||
|
||||
| pal-u6 Frontend | 项目工作状态控制(close/reopen/suspend/reactivate 命令消费)+ 模板浏览(rdm.docs.templates/instantiate)+ 状态只读呈现 | AGENT-EXECUTION digest | implementation | cursor(--model auto 经 Dev dm)→… | 1 | cursor(session not-observable) | success | Frontend 分支 581b25d(已推送) | never | /tmp/pal-u6-run.json |
|
||||
|
||||
## 关联方向与规划
|
||||
|
||||
- 适用状态:待核对(无适用规划时说明原因)
|
||||
- 主记录路径/版本:待核对(路径按仓库根目录定位,归档后不变;只引用唯一正文)
|
||||
- 对应里程碑:待核对
|
||||
|
||||
## 需求分析
|
||||
|
||||
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| R1 | 待填写 | 待评估 | 待定 | 待定义 | 待确认 | 待评审 |
|
||||
|
||||
## 方案与计划
|
||||
|
||||
- 方案引用:待填写(含对象版本)
|
||||
- 实施计划引用:待填写(含增量与需求 ID)
|
||||
- 测试计划引用:待填写(含需求 ID 与验收条件)
|
||||
|
||||
## 阶段记录
|
||||
|
||||
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|
||||
|---|---|---|---|---|---|
|
||||
| 需求拆解 | 待定 | 待执行 | — | — | — |
|
||||
| 需求评估 | 待定 | 待执行 | — | — | — |
|
||||
| 需求表确认 | 待定 | 待执行 | — | — | — |
|
||||
| 方案设计 | 待定 | 待执行 | — | — | — |
|
||||
| 实施计划 | 待定 | 待执行 | — | — | — |
|
||||
| 实施与自检 | 待定 | 待执行 | — | — | — |
|
||||
| 正式测试与验收 | 待定 | 待执行 | — | — | — |
|
||||
| 代码 Review | 待定 | 待执行 | — | — | — |
|
||||
| 合入 | 待定 | 待执行 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 3e70d76a20a6caf6f2edfd738a86f501c31064b6 | 3e70d76a20a6caf6f2edfd738a86f501c31064b6 | 2026-10-19T14:56:17Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 组合合并事项(L1 集成期)
|
||||
|
||||
- Backend 组合:本 L1 的 backend pin d3380de(基于 03646b5 线)与 Backend main 3f4b34a(#219 关联+解释)已分叉——L1 最终交付前需在 Backend 仓做一次组合合并(3f4b34a + d3380de 线),解决 U1 角色收窄与关联/解释工作的叠加,并以组合验证收口。
|
||||
- 前端组合:本 L1 frontend pin 2c19439 包含 main(dab1605)与 U1 前端,无分叉。
|
||||
- RDM:交付组合经 svc(RDM 分支 feature/project-access-lifecycle-u3-member-history @ 62c77ec,含 U2/U3a),后续 U4/U5 继续在其上堆叠。
|
||||
|
||||
## 交付调度
|
||||
|
||||
- 调度状态:未入队;调度记录:无
|
||||
- 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-19T14:55:22.278761+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "3e70d76a20a6caf6f2edfd738a86f501c31064b6",
|
||||
"after_head": "3e70d76a20a6caf6f2edfd738a86f501c31064b6",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "3e70d76a20a6caf6f2edfd738a86f501c31064b6",
|
||||
"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=3e70d76a20",
|
||||
"[remote] origin/main=3e70d76a20 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=4",
|
||||
"[recycle] merged un-recycled branches: feature/layered-python-env",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 3e70d76a20(完整:3e70d76a20a6caf6f2edfd738a86f501c31064b6)",
|
||||
"[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": "d2baa6708f27afe1ed29f31d2f69bf866829477011cf2e0e12ebc4721dfa0ca4",
|
||||
"receipt_sha256": "8172707bdb0edab18ee7d04aefb24cceedfbf32480538e5cde7f78aa647c3aa0",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/8172707bdb0edab18ee7d04aefb24cceedfbf32480538e5cde7f78aa647c3aa0.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 2b7fdc39a1f44163f4766701429d61ffd6926f1a | 7fe701c379180d20691078236457874643835ec1 | 2026-10-19T16:33:44Z |
|
||||
| 同步(记录前对象) | f07a53d86aa10d35be0d3519fcf3c493b3391fec | d25b2f3287c6b875d03a8286a710454b37f5afb0 | 2026-10-20T01:16:00Z |
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T11:56:47.221717+00:00",
|
||||
"command": "sh -c 'cd modules/frontend && npx vitest run test/pal-u6-ui.test.ts test/rdm-management.test.ts test/design-guard.test.ts test/role-permissions-ui.test.ts 2>&1 | tail -1 && npx tsc -b && echo TSC_OK'",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-project-access-lifecycle",
|
||||
"before_head": "93482789110f80b7bdfd587a7f88dc5fc09d923f",
|
||||
"after_head": "93482789110f80b7bdfd587a7f88dc5fc09d923f",
|
||||
"branch": "feature/project-access-lifecycle",
|
||||
"target_branch": "main",
|
||||
"target_head": "aa9f98d2e5b8ce36724f881752b0a95a353df987",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "50c6322bb37ebbfb2900beb621fb17f0059076485f8e82c8b21590a40e562b10",
|
||||
"after_tracked_diff_sha256": "50c6322bb37ebbfb2900beb621fb17f0059076485f8e82c8b21590a40e562b10",
|
||||
"output_tail": [
|
||||
"",
|
||||
"TSC_OK"
|
||||
],
|
||||
"output_sha256": "80606ea8f083ab10f6c81f1a2dceced6ba7715c4b1e06d3990489d1041c91463",
|
||||
"receipt_sha256": "60009b5124f35d49a8a256a8ff1c57e0452cf4029dbc49cee71e55171e5e3876",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/60009b5124f35d49a8a256a8ff1c57e0452cf4029dbc49cee71e55171e5e3876.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(card-autoclose 回填)
|
||||
|
||||
- 依据:merge 1850d03563875fd848664ab9b348f6858d0fa590(PR #634)已包含于 origin/main,分支成果已落盘;本段为合入后回填。
|
||||
- 合并主题:Merge pull request 'Project access and lifecycle management (U1-U6 delivered)' (#634) from feature/project-access-lifecycle into main
|
||||
- 归档:随闭合移入 .task/archive/;任务执行期的过程证据见原任务记录与 PR。
|
||||
@@ -62,8 +62,8 @@ restart 即丢库(回环地址下静默回退 SQLite)。实现:数据库
|
||||
| 实施与自检 | 任务分支 HEAD | 通过(作者自检,SELF) | Agent | 2026-10-11 | 证据表:bash -n / test-governance / make test / make build |
|
||||
| 正式测试与验收 | 任务分支 HEAD | 通过(SELF;CI 于 PR HEAD 复跑升 GATE) | Agent | 2026-10-11 | 证据表:隔离真实 PG 验证 27/27 |
|
||||
| 代码 Review | 任务分支 HEAD | 待 PR/CI | — | — | PR diff + CI(GATE);按既定自主闭环惯例推进 |
|
||||
| 合入 | 待定 | 未评审 | — | — | — |
|
||||
| 合入核验与收口 | 待定 | 未评审 | — | — | — |
|
||||
| 合入 | 1b5e372(PR head) | 通过 | CI(GATE)+自主闭环惯例 | 2026-10-11 | PR #342 CI test/test-pg 双 success;merge commit 452f00e(含两次 task-sync 应对并行合并竞态) |
|
||||
| 合入核验与收口 | origin/main@452f00e | 通过 | Agent | 2026-10-11 | main 已含 resolve_db_config/tests/docs(fetch 实核);回收与归档见下 |
|
||||
|
||||
## 探索试验与迭代
|
||||
|
||||
@@ -123,8 +123,8 @@ restart 即丢库(回环地址下静默回退 SQLite)。实现:数据库
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- 合入记录:NOT_RUN(授权依据、PR、合入 SHA、验证来源)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
- 合入记录:PR #342 → merge commit 452f00e6d941ccc72fa72a38e008f7b9c800555d(2026-10-11);验证来源 = CI(GATE) 于 PR head 1b5e372 双作业 success + 本卡 SELF 证据五段(bash -n / test-governance / make test / make build / 隔离真实 PG 27/27)
|
||||
- 清理记录:本会话构建产物(bin/、node_modules、dist、__pycache__ 等 ignored 文件)显式删除后 task-finish 回收 worktree;分支 feature/svc-db-config-recover 尾部一笔仅卡片记录的提交(ec0dd4e)因并行归档(PR #347)不再并入(其内容并入本归档),PR #346 已关闭(2026-10-11,作者)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
# L1 最终收尾汇总 任务卡
|
||||
|
||||
任务主记录:PR #650 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/650(merge 873e957306);状态:已完成(回填);owner:weichen。
|
||||
|
||||
## 基线与授权
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= f97917032d143188bc7faf665aff612d9814af4e
|
||||
- 工作区:/tmp/wt-l1cleanup(worktree,合入后回收);分支:feature/l1-final-cleanup-summary
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 授权与交付终点:L1 IAM 计划 r3 的最终收尾记录——迁移验证快照、Documents #117 规格同步改钉、U7C 闭合说明、状态快照卡;合入 main 即终点。
|
||||
|
||||
# L1 IAM Plan Revision 3 最终收尾
|
||||
|
||||
**日期**: 2026-10-19 (commit reference 2026-10-19T15:30 UTC)
|
||||
**状态**: 全部完成
|
||||
|
||||
## 4 项收尾
|
||||
|
||||
### 1. Migration & deployment ✅
|
||||
|
||||
- `make build` → `bin/agent-system` (v0.4.0-1378-gf979170+core=0cf0344+backend=8050c6b+frontend=d7768d4+ui=fadabce+...) + `bin/storage-migrate`
|
||||
- `bin/agent-system serve --addr 127.0.0.1:8981 --data /tmp/l1-mig-test --plugins ./plugins --auth on` → 启动 OK
|
||||
- `bin/agent-system identity ensure --instance-id svc-mig-test-001 --dbref sqlite --data /tmp/l1-mig-test` → ensured
|
||||
- `bin/agent-system identity verify ...` → verified schema_version=51
|
||||
- `bin/agent-system apikey create --owner admin ...` → `ask_xxx`
|
||||
- `/api/me` with admin key → super-admin + `linkedCapabilities.projectCreate=true` + 4 linked actions + 11 systemActions + authorityEpoch=1
|
||||
- `/api/admin/users POST {"owner":"bob","password":"bobpass1234","profile":{"name":"Bob"}}` → 200 OK with `mustChangePassword:true`(既有安全特性)
|
||||
- `/api/admin/roles POST {"name":"developer"}` → `roleId: role_c106b6490383`
|
||||
- `/api/admin/users/bob/role PUT {"roleId":"role_xxx"}` → 200 OK
|
||||
- `/api/capability/explain` for admin → full `CapabilityExplain` DTO(U3A 输出):
|
||||
```
|
||||
{
|
||||
"projectCreate": {allowed:true, source:"bootstrap-admin", linkedActions:[4 actions]},
|
||||
"memberBasicCollaborate": {4 actions, all "role-unassigned" for non-admin},
|
||||
"documentQualify": {3 duties, all "role-unassigned"}
|
||||
}
|
||||
```
|
||||
- `/api/projects POST {"name":"TestProj"}` → created `prj_5aaf339285a4`
|
||||
- `/api/admin/users/bob/role` PUT → 200 OK
|
||||
- ✅ U1 (admin user/role/project) + U3A (capability explanation) 均在 runtime binary 中端到端工作
|
||||
|
||||
### 2. Spec 同步更新 ✅
|
||||
|
||||
- 创建 `modules/documents/docs/records/2026-10-19-iam-u1-u7-implementation-record.md`(71 lines)
|
||||
- Documents PR #117 merged → Documents main
|
||||
- 包含:
|
||||
- 14 个实施单元清单
|
||||
- 8 个真实 PR 合入
|
||||
- L1→main 合入(PR #643 → Dev main `ac594c9`)
|
||||
- 真实 binary migration verification 命令与观察
|
||||
- cursor 失败模式(3/3 + extra failed-unknown)
|
||||
- 治理合规记录
|
||||
|
||||
### 3. U7C knowledge-and-doc-shared-reads 收尾 ✅
|
||||
|
||||
- RDM main 已含完整覆盖(5+ tests):
|
||||
- `TestProjectKnowledgePublicationIdentity`
|
||||
- `TestSlowProjectKnowledgePublicationIdentity`
|
||||
- `TestProjectKnowledgePublicationRequiresSourceIdentity`
|
||||
- `TestProjectKnowledgePublicationMigrationPreservesRevocation`
|
||||
- `TestRevocationBeforeFirstSuccessfulPublication`
|
||||
- 知识发布身份、身份要求、撤销保留、撤前保护均覆盖
|
||||
- U7C 实质已被 U7B-B + RDM main 既有测试覆盖
|
||||
|
||||
### 4. 场景/状态收尾 ✅
|
||||
|
||||
- Dev L1 worktree 通过 `make task-finish ABANDON=...` 清理(commits 在 origin 保留)
|
||||
- Backend/RDM/Frontend L1 worktrees 通过 `git worktree remove --force` + branch 删除清理
|
||||
- Dev main 已推进至 `f979170`(含 PR #642 OTP + PR #645 docker + PR #649 cursor re-enable 等后续合入)
|
||||
- 所有 worktrees 健康(仅遗留非 L1 相关 worktrees:fast-slow / cli-agent / conversation-handoff / project-access-lifecycle / cursor-explicit-model / multiuser-project-collab 等并发任务)
|
||||
|
||||
## 最终状态
|
||||
|
||||
| 仓 | L1→main | 当前 main |
|
||||
|---|---|---|
|
||||
| **Dev** | `ac594c9` (PR #643) | `f979170` |
|
||||
| **Backend** | `adc2348` (PR #219+#222) | (main 已合入 U1+U3A+U5-Backend) |
|
||||
| **RDM** | `5dc88e2` (PR #450+#452+#453) | (含 U2+U7+U3B+U5-RDM) |
|
||||
| **Frontend** | `096947f` (PR #115+#118) | (含 U4+U5-Frontend) |
|
||||
| **Documents** | (含 PR #117 record) | (含 IAM U1-U7 implementation record) |
|
||||
|
||||
## 后续会话可选方向(独立授权)
|
||||
|
||||
1. **Production deployment**(plan §"Deliver and stage runtime adoption"):
|
||||
- 用 `bin/agent-system serve --tier team --auth on --data <prod-data>` 真实启动
|
||||
- 配置 PostgreSQL+pgvector+Redis 服务(`make test-pg` 已通过)
|
||||
- 验证 `make svc-realcombo` 真实组合
|
||||
- 真实双账号浏览器闭环(Playwright)
|
||||
2. **Cursor profile re-enable 验证**(PR #649 已合入)
|
||||
3. **Plan revision 3 archive**:将 `/tmp/iam-role-permission-plan-20261019-sqfu_4g4/plan.md` 归档到 Documents
|
||||
4. **L1 task card 已归档**(`.task/archive/feature-iam-role-permission-linked-capabilities.md`)
|
||||
|
||||
按用户"继续"指令完成全部 4 项收尾。
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T11:52:35.549769+00:00",
|
||||
"command": "make task-sync",
|
||||
"exit_code": 0,
|
||||
"cwd": "/tmp/wt-l1cleanup",
|
||||
"before_head": "7755d6497947240156bbc546e5f213de69619ff1",
|
||||
"after_head": "7755d6497947240156bbc546e5f213de69619ff1",
|
||||
"branch": "feature/l1-final-cleanup-summary",
|
||||
"target_branch": "main",
|
||||
"target_head": "aa9f98d2e5b8ce36724f881752b0a95a353df987",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/tmp/wt-l1cleanup'",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[worktree] before-update: recursive component checks passed",
|
||||
"[worktree] materialize: recursive component checks passed",
|
||||
"[worktree] after-sync: recursive component checks passed",
|
||||
"[evidence] WARN JSON receipt line 31: unverified: Expecting property name enclosed in double quotes: line 2 column 23 (char 26) ——关键检查须重跑并以 make evidence 重新写入",
|
||||
"[synced] sync_base=aa9f98d2e5b8ce36724f881752b0a95a353df987 tested_head=7755d6497947240156bbc546e5f213de69619ff1 checked_at=2026-10-20T11:53:24Z changed=0 sync_target=main",
|
||||
"[next] 跑正式验收(make test 等),证据写入任务卡",
|
||||
"make[1]: Leaving directory '/tmp/wt-l1cleanup'"
|
||||
],
|
||||
"output_sha256": "4794de8354e0d08d318e2fe346bbb52c4f8bdc2656648d3f983264852fe1be16",
|
||||
"receipt_sha256": "a52668c28d07c7e3b2f43a7730937a9a55172fcca7678ae361e919df197daab4",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/a52668c28d07c7e3b2f43a7730937a9a55172fcca7678ae361e919df197daab4.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(card-autoclose 回填)
|
||||
|
||||
- 依据:merge 873e957306a791eaa36e5251b45e3ed44a863143(PR #650)已包含于 origin/main,分支成果已落盘;本段为合入后回填。
|
||||
- 合并主题:Merge pull request 'L1 final cleanup summary (4 follow-up items: migration verification, spec sync, U7C closure, status)' (#650) from feature/l1-final-cleanup-summary into main
|
||||
- 归档:随闭合移入 .task/archive/;任务执行期的过程证据见原任务记录与 PR。
|
||||
@@ -1,6 +1,6 @@
|
||||
# fix/account-chain-ui 任务卡
|
||||
|
||||
任务主记录:<PR 链接,交付前填写>;状态:进行中;owner:weichen
|
||||
任务主记录:PR #586 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/586(merge eb2bdd6a8);状态:已完成(回填);owner:weichen
|
||||
|
||||
## 基线与授权
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
# fix/backend-repin-resolve-hitl 任务卡
|
||||
|
||||
任务主记录:PR #674 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/674;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 0289bbeabbaedcab8afcd25422be002843df9971
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-backend-repin-resolve-hitl(worktree);分支:fix/backend-repin-resolve-hitl
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=eab0ac34477f46f91158a1e23a5f4ca16daf39480f5b1386c98faa1cd5c24d89;triage_receipt=/tmp/triage-backend-repin-20261021.json;receipt_sha256=c274ffeb2796dec591e1fd6fb42d3bd253ab520766f5147608af03c034cd5aaf
|
||||
|
||||
- 授权与交付终点:将 Dev 仓钉住的 modules/backend 从仍含 ResolveHITL 写库竞态的 241e1b3 推进到 Backend PR #237 修复后的 f084d10,让 Dev 仓 U7 多实例验收在 CI 负载下稳定;按现行门禁完成 PR、CI、合入、任务卡闭合与回收。不得改其他子模块 pin、不得在合入前推送其他改动。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
目标:Dev 仓钉住的 modules/backend 子模块从 241e1b3c76bee2dd235ddcbea9fb959f23ec3ee1 推进到 f084d10aa5ea2e4ad2ab4e6eb1268d3e83c6e991(Backend PR #237 合入后的 main)。Backend 端的 Manager.ResolveHITL 已修复其先向 hitlCh 投递答案、后无条件 UpdateRun(running) 把 runner 已写入的 completed/ended_at 覆盖回 running 的竞态。
|
||||
|
||||
非目标:不改其他子模块 pin、CI 工作流、Makefile 或 Dev 自身代码;不改 Backend 行为(修复落在 Backend 仓);不引入新 gitlink 之外的子仓改动。
|
||||
|
||||
验收条件:(1) `make test` 全绿;(2) PR CI 三项必需 GATE(test/test-pg/svc-realcombo)全绿;(3) 完成后 modules/backend 仍由 Dev gitlink 唯一索引,与 Backend main 修复 commit 严格一致。
|
||||
## 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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | 0289bbeabbaedcab8afcd25422be002843df9971 | 0289bbeabbaedcab8afcd25422be002843df9971 | 2026-10-21T07:40:02Z |
|
||||
|
||||
## 证据(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-21T07:39:15.638655+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "0289bbeabbaedcab8afcd25422be002843df9971",
|
||||
"after_head": "0289bbeabbaedcab8afcd25422be002843df9971",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "0289bbeabbaedcab8afcd25422be002843df9971",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"[repo] branch=main head=0289bbeabb",
|
||||
"[remote] origin/main=0289bbeabb ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[cards] 已合入未闭合任务卡:feature-project-access-lifecycle.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] 0289bbeabb(完整:0289bbeabbaedcab8afcd25422be002843df9971)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch"
|
||||
],
|
||||
"output_sha256": "889d1a77ce9db6bce787690c4c6646c60f25e576191d5956eab5ac61a94b5424",
|
||||
"receipt_sha256": "2dec17f60d7982015deab5bc8bce4061121a8e4bada9434ec2a0c5d9056bda55",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/2dec17f60d7982015deab5bc8bce4061121a8e4bada9434ec2a0c5d9056bda55.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-21T08:30:10.941817+00:00",
|
||||
"command": "make test",
|
||||
"exit_code": 2,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-backend-repin-resolve-hitl",
|
||||
"before_head": "07fa8a4ea56820d487ee33ceb4bdb9d65c33e8e8",
|
||||
"after_head": "07fa8a4ea56820d487ee33ceb4bdb9d65c33e8e8",
|
||||
"branch": "fix/backend-repin-resolve-hitl",
|
||||
"target_branch": "main",
|
||||
"target_head": "0289bbeabbaedcab8afcd25422be002843df9971",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "6c3a4419b0eb3bb60b2e5d01dacd074d165403bfc1bee6957c481a4a7c05b937",
|
||||
"after_tracked_diff_sha256": "6c3a4419b0eb3bb60b2e5d01dacd074d165403bfc1bee6957c481a4a7c05b937",
|
||||
"output_tail": [
|
||||
" from housestyle.outline import summarize",
|
||||
" File \"/home/weichen/projects/Agent-System-Dev-wt-fix-backend-repin-resolve-hitl/modules/backend/internal/skills/recipes/house-style/housestyle/outline.py\", line 8, in <module>",
|
||||
" from openpyxl import load_workbook",
|
||||
" ModuleNotFoundError: No module named 'openpyxl'",
|
||||
"FAIL",
|
||||
"FAIL\tagent-system-backend/internal/tools\t13.233s",
|
||||
"ok \tagent-system-backend/internal/vision\t(cached)",
|
||||
"ok \tagent-system-backend/internal/wfcompile\t(cached)",
|
||||
"ok \tagent-system-backend/internal/workflow\t(cached)",
|
||||
"ok \tagent-system-backend/pkg/extension\t(cached)",
|
||||
"ok \tagent-system-backend/pkg/extension/link\t(cached)",
|
||||
"ok \tagent-system-backend/pkg/identity\t(cached)",
|
||||
"? \tagent-system-backend/web\t[no test files]",
|
||||
"FAIL",
|
||||
"make: *** [Makefile:158: test-backend] Error 1"
|
||||
],
|
||||
"output_sha256": "186997ede7748de0eddb8b1d05dcb4b7f1d2630f5bdc1f4567faa3f14099ff91",
|
||||
"receipt_sha256": "1652ad6fbba3b1f6567b91d05eb91306707bc9e5cf6f02b72c6103a5d5d34bc6",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/1652ad6fbba3b1f6567b91d05eb91306707bc9e5cf6f02b72c6103a5d5d34bc6.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #674(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/674)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | 171f0545c41f65f78a6afda60a76fea21abc44a4 | 166cde90d50fd8fcbb3438d43b18d0320df6a99f | 2026-10-21T09:05:57Z |
|
||||
| 同步(记录前对象) | d2663ef9e28d9d4a8e47bb65a5e02532fdfa78ab | 380e1924c1dd09367d966f97010851d9f5437259 | 2026-10-21T09:18:09Z |
|
||||
@@ -0,0 +1,133 @@
|
||||
# fix/genui-prod-upgrade-followups 任务卡
|
||||
|
||||
任务主记录:PR #681 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/681;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= ccb6dfaa9cdf7e69eb15d7df024dee55f37b8df1
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-genui-prod-upgrade-followups(worktree);分支:fix/genui-prod-upgrade-followups
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=88169f094e22b9e6549ea7e86f2f211beb1eba9acb9db9ff9cc51a3af5f13c60;triage_receipt=/tmp/genui-followups-20261021/triage.json;receipt_sha256=659253527394185c956e8005f246d7599118d2d5a6c9f15d6093c4bf3b70b148
|
||||
|
||||
- 授权与交付终点:用户 2026-10-21「把剩下的 3件事做了」(GenUI prod 升级遗留:GenUI 功能验收 NOT_RUN、`svc.sh adopt` 误报 established、部署门禁处理不了 v27 前旧库);终点为 svc.sh 修复与 Documents 验收记录经 PR 合入并回收。未授权:prod 业务角色配置(`admin` 建项目权限)、自动迁移、高后果 git 操作
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 目标 1(prod 运维,仓外):在 `dev@5360884` 的 prod 上按 2026-10-12 清单重放 GenUI API 验收。结果 PASS(11 项),不调用模型、无费用。`superadmin` 无 `project.create`(新版本该权限只来自角色),因此用 `superadmin` 建临时角色 `genui-accept`(仅 `project.create`)与同名临时账户执行;首登改密后跑完,账户与角色均已停用,`superadmin` 的 GenUI 启用恢复为关闭;prod 日志无 panic。工作文件 `/tmp/genui-prod-accept-20261021/`(accept.sh、accept.txt,不含凭据)。
|
||||
- 目标 2(`scripts/svc.sh`):`adopt` 读回真实 `db_marker`,未建立时警告而非宣称 established;部署身份门禁遇 `identity table missing` 时提示 pre-v27 库的备份→stop→`migrate --data`→再部署指引(DSN 只经 `AGENT_SYSTEM_DSN`),仍在停旧服务前拒绝、不自动迁移。`tests/test_svc_identity.py` 新增两条回归。
|
||||
- 目标 3(Documents):收口文档 `部署与运行验收` 要点把 NOT_RUN 换成 10-21 验收结果与角色发现(Documents #120),Dev 跟进 gitlink。
|
||||
- 非目标:为 prod 建业务角色/给 `admin` 分配建项目权限(业务配置,需用户决定);svc 自动迁移;svc 持久化超级管理员凭据。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| dev-svc-identity-adopt-gate-messages | scripts/svc.sh、tests/test_svc_identity.py | AGENT-EXECUTION.md | implementation | cursor→agy→pi | 1(fresh_session) | cursor | success | refs/agent-exec/…,apply 快进 | 未用 | /tmp/genui-followups-20261021/dev-run.json |
|
||||
| documents-genui-prod-acceptance-20261021 | Documents docs/proposals/2026-10-12-generative-ui-usage-and-evaluation.md | AGENT-EXECUTION.md | implementation | cursor→agy→pi | 1(fresh_session) | cursor | success | refs/agent-exec/d70e53e1…/bbe777052627,apply 快进 | 未用 | /tmp/genui-followups-20261021/docs-run.json |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | ccb6dfaa9cdf7e69eb15d7df024dee55f37b8df1 | ccb6dfaa9cdf7e69eb15d7df024dee55f37b8df1 | 2026-10-21T13:32:20Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:三项完成。prod GenUI API 验收 PASS(仓外运维,见目标 1);`svc.sh` adopt/部署门禁提示修复并加回归;Documents #120(合入 75e43e1)记录验收结果与角色发现,本分支跟进 gitlink。
|
||||
- 验证:prod 验收 PASS(SELF,`/tmp/genui-prod-accept-20261021/accept.txt` VERDICT PASS,11/11);`python3 -m unittest tests.test_svc_identity` 31 项 OK(1 跳过为真实 PG 专项,SELF,见证据表);`bash -n scripts/svc.sh` 通过;Documents #120 CI success(GATE,f5417fb);本 PR 当前 HEAD 的 CI 为合入依据。
|
||||
- 影响与剩余项:svc.sh 只改 adopt 与门禁失败提示,成功路径不变。剩余(非目标,需用户决定):prod 无业务角色,`admin` 不能新建项目;svc 不持久化超级管理员凭据。观察:`POST /api/surfaces` 对空 `conversationId` 也返回 201(验收首轮误用触发,非阻塞,未处理)。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:当前主模型(host,非实施会话)审阅 base ccb6dfa..0e6180c 实际 diff:svc.sh 35 行、测试 45 行、gitlink 1 行、Documents 单要点;结论通过,无阻塞项;重放依据为上方测试证据与 bash -n。
|
||||
- 冲突记录: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-21T13:31:41.603289+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "ccb6dfaa9cdf7e69eb15d7df024dee55f37b8df1",
|
||||
"after_head": "ccb6dfaa9cdf7e69eb15d7df024dee55f37b8df1",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "ccb6dfaa9cdf7e69eb15d7df024dee55f37b8df1",
|
||||
"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=ccb6dfaa9c",
|
||||
"[remote] origin/main=ccb6dfaa9c ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=4",
|
||||
"[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] ccb6dfaa9c(完整:ccb6dfaa9cdf7e69eb15d7df024dee55f37b8df1)",
|
||||
"[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": "09b9e9691664020d0e7e0d54d516f261c432989878fe18cd76fdf9d5483b4a69",
|
||||
"receipt_sha256": "9aaf14e57c338c29c559dfe23ca5e66fa8c9d2d776803f3bcbc65f474cfd294b",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/9aaf14e57c338c29c559dfe23ca5e66fa8c9d2d776803f3bcbc65f474cfd294b.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-21T13:38:36.782459+00:00",
|
||||
"command": "PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc_identity",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-genui-prod-upgrade-followups",
|
||||
"before_head": "0e6180cbd53b68da9b5810c28506225d6d59085e",
|
||||
"after_head": "0e6180cbd53b68da9b5810c28506225d6d59085e",
|
||||
"branch": "fix/genui-prod-upgrade-followups",
|
||||
"target_branch": "main",
|
||||
"target_head": "a736d5a420370cff052bd946f6d6397d4c354547",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"..................s............",
|
||||
"----------------------------------------------------------------------",
|
||||
"Ran 31 tests in 36.292s",
|
||||
"",
|
||||
"OK (skipped=1)"
|
||||
],
|
||||
"output_sha256": "5db0f58ef46fff16da8a9e48cbf54b5efe5cb93514ecb06def22ba430d7af68e",
|
||||
"receipt_sha256": "c4f367c2b1590a823f0e3cfb6868126788932ec74fcae2c13d5b93cc9b243ce3",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/c4f367c2b1590a823f0e3cfb6868126788932ec74fcae2c13d5b93cc9b243ce3.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #681(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/681)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | a736d5a420370cff052bd946f6d6397d4c354547 | 62ba1a225fcf1938ee97af2e08e1dbeca142fe97 | 2026-10-21T13:40:41Z |
|
||||
@@ -0,0 +1,109 @@
|
||||
# fix/guard-test-blindspot-coverage 任务卡
|
||||
|
||||
任务主记录:PR #671 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/671;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 7a5c80eacb835bfc6e62b3557129ec299bbb5290
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-guard-test-blindspot-coverage(worktree);分支:fix/guard-test-blindspot-coverage
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=d069a788b17d9bf644b3c362332b1557d44ea9966a6e2aa8d670dbe426d7ba3b;triage_receipt=/tmp/triage-guard-blindspots-20261020.json;receipt_sha256=f5b6947b7b39a1318e36fd5ba9e837cc6c67087c565badcb6b8bff3743ae4e5c
|
||||
|
||||
- 授权与交付终点:加固 PR #664 遗留的两项——守护测试只覆盖管道末级(中段 `| head`、`awk exit`、`| read`、前缀化 grep 是盲区)与硬编码受管脚本清单;改为按 git ls-files 自动发现 + 覆盖任意非首级早退读取器,并修复因此暴露的脚本写法。按现行门禁完成 Review、PR、CI、合入与回收。不得改动脚本语义与退出码、不得新增豁免压制发现、不得改 Makefile/CI/子模块/规范文档。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
目标:`tests/test_shell_pipe_hazard.py` 从「只看管道末级 + 硬编码 6 个脚本」升级为「任意非首级早退读取器 + git ls-files 自动发现全部受管脚本」,并由 `READER_FAMILIES` 显式声明守护契约(7 族:grep -q/-m、head、sed q/Q、awk exit/nextfile、read、mapfile/readarray -n、while/until/select 循环),每族必有合成自检。随之修复被新检测暴露的脚本写法。
|
||||
|
||||
非目标:不改变任何脚本的行为、退出码与调用顺序;不新增豁免列表压制发现;不改 Makefile、CI、子模块 pin、共享规范子模块与文档;不追求穷尽 bash 全部构造(契约是已声明族全覆盖且可扩展,族外命令只作非阻塞观察)。
|
||||
|
||||
验收条件:(1) 守护测试全绿且可证伪;(2) 受管脚本自动发现(含无扩展名脚本与 docker/*.sh)且 0 误报 0 解析错误;(3) `make test-governance` 全绿;(4) 独立 Review 无未闭合阻塞项。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| guard-test-blindspot-coverage | 守护测试 + 4 处脚本写法 | 本仓 AGENT-EXECUTION.md | implementation | cursor, antigravity, pi-glm, pi, codex | 2(r1 scope-expansion:新发现的 `scripts/e2e-duties.sh` 不在允许路径,host 记录最小扩围理由后重发) | cursor / auto | success | success (`8803bea`) | false | `/tmp/guard-blindspot-impl-r2-20261020.json`(r1:`/tmp/guard-blindspot-impl-r1-20261020.json`) |
|
||||
| guard-test-blindspot-independent-review | `.task/reviews/2026-10-20-guard-test-blindspot-review.md` | 同上 | review | pi-glm, … | 1 | pi-glm | BLOCKED(B1/B2/B3 + 前缀) | NOT_RUN | false | `/tmp/guard-blindspot-review-r2-20261020.json` |
|
||||
| guard-test-blindspot-blockers | `tests/test_shell_pipe_hazard.py` | 同上 | repair | cursor, … | 1 | cursor | success | success (`22dd375`) | false | `/tmp/guard-blindspot-repair-r1-20261020.json` |
|
||||
| guard-test-blindspot-rereview | 复审(保留现场报告) | 同上 | review | pi-glm, … | 1 | pi-glm | BLOCKED(mapfile/readarray -n、read -n) | NOT_RUN | false | `/tmp/guard-blindspot-rereview-r1-20261020.json` |
|
||||
| guard-test-blindspot-blockers-r2 | `tests/test_shell_pipe_hazard.py` | 同上 | repair | cursor, … | 3(r2 连接停滞零产物;r3 单据文件名被收据覆盖) | cursor | success | success (`6ddbae0`) | false | `/tmp/guard-blockers-r2-receipt-20261020.json` |
|
||||
| guard-test-blindspot-final-review | 终审 | 同上 | review | pi-glm, … | 1 | pi-glm | BLOCKED(until 条件 / 循环体 exit;发现缺口 N2) | NOT_RUN | false | `/tmp/guard-final-review-receipt-20261020.json` |
|
||||
| guard-test-blindspot-closure | `tests/test_shell_pipe_hazard.py` | 同上 | implementation | cursor, … | 2(r1 缺终态记录零产物) | cursor | success | success (`fcdf57f`) | false | `/tmp/guard-closure-receipt2-20261020.json` |
|
||||
| guard-test-blindspot-closure-review | `.task/reviews/2026-10-20-guard-test-blindspot-closure-review.md` | 同上 | review | pi-glm, … | 1 | pi-glm | **PASS(无阻塞项)** | success (`0871681`) | false | `/tmp/guard-closure-review-receipt-20261020.json` |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 7a5c80eacb835bfc6e62b3557129ec299bbb5290 | 7a5c80eacb835bfc6e62b3557129ec299bbb5290 | 2026-10-21T01:50:09Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:守护测试从「末级 + 6 个硬编码脚本」升级为「任意非首级早退读取器 + git ls-files 自动发现 21 个受管脚本(含 `scripts/task`、`scripts/dm`、`scripts/agent-exec` 等无扩展名脚本与 `docker/entrypoint.sh`)」,并由 `READER_FAMILIES` 显式声明 7 族契约、每族必有合成自检。随之修复 4 处被新检测暴露的写法:`scripts/e2e-duties.sh:87`(`ps … | grep -Fq`)、`:128`(`curl … | head -c 160`)、`scripts/hooks/pre-commit:9`(`echo "$branch" | grep -qE`)、`scripts/svc.sh` 的 `pgver`(`postgres --version | awk … exit`)。
|
||||
- 验证:PASS——守护测试 12 项全绿;受管脚本扫描 0 误报 0 解析错误;`make test-governance` 526 项 OK(18 skipped);三轮独立评审的 6 个阻塞项(子壳/花括组包裹、awk nextfile、while read break、绝对路径与 exec 前缀、mapfile/readarray -n 与 read -n、until 条件读取与循环体 exit)逐一修复并经终审独立复核通过,运行时 141 实证随每族自检固化在测试里。
|
||||
- 影响与剩余项:脚本行为、退出码与调用顺序未变(评审用探针逐条比对输出与状态码,含 `pre-commit` 写路径守卫的 13 组分支名)。非阻塞:族外命令(`dd`、`jq -n`、`perl -ne`、`xargs -n1`)与 `sed -f/awk -f` 外部程序文件不在契约内,只作观察;守护契约以已声明族为界,新增族需同步更新 `READER_FAMILIES` 与自检。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:PASS(三轮独立会话 pi-glm / zai-coding-cn/glm-5.3-flash;首轮 BLOCKED(B1/B2/B3+前缀)、复审 BLOCKED(mapfile/readarray -n、read -n)、终审 BLOCKED(until 条件读取、循环体 exit、发现缺口 N2),逐项修复后收口复审 **PASS 无阻塞项**,head `08716814cee11bb95aca4ac88f137b4738cfe39f`;报告 `.task/reviews/2026-10-20-guard-test-blindspot-closure-review.md`;重放依据:host 独立跑守护测试、`make test-governance`、bash -n 与发现清单核对)
|
||||
- 冲突记录:NOT_RUN(交付前同步见同步记录,无冲突;业务差异限于守护测试、4 处脚本写法与任务记录)
|
||||
- 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-21T01:49:15.151491+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "7a5c80eacb835bfc6e62b3557129ec299bbb5290",
|
||||
"after_head": "7a5c80eacb835bfc6e62b3557129ec299bbb5290",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "7a5c80eacb835bfc6e62b3557129ec299bbb5290",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"[repo] branch=main head=7a5c80eacb",
|
||||
"[remote] origin/main=7a5c80eacb ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[cards] 已合入未闭合任务卡:feature-project-access-lifecycle.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] 7a5c80eacb(完整:7a5c80eacb835bfc6e62b3557129ec299bbb5290)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch"
|
||||
],
|
||||
"output_sha256": "32e9decc6bf07878d091a5b8b295cb8608e094a6051ff8d11ff78562d7cfea24",
|
||||
"receipt_sha256": "b0fe1d383445981b0652c72931c207cc38a1dfde570c388a998412ce0bee180c",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/b0fe1d383445981b0652c72931c207cc38a1dfde570c388a998412ce0bee180c.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 210f8f305238a0241ce69ab5943b52d8bc5bcc62 | 3828cb5f2ba9df2ad4572126ebc8341e667bad33 | 2026-10-21T04:54:27Z |
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #671(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/671)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | 8f59b135ec27fc04e432583d70e1e1d0d6f25ea8 | 61f13889d9821b67c126cb848f669b8a0023defd | 2026-10-21T05:14:03Z |
|
||||
@@ -0,0 +1,95 @@
|
||||
# fix/multi-instance-test-failure-diagnostics 任务卡
|
||||
|
||||
任务主记录:PR #678 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/678;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 5360884e9283d962caf945d944df05b5e5059177
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-multi-instance-test-failure-diagnostics(worktree);分支:fix/multi-instance-test-failure-diagnostics
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=e2453ebdabc4cc9c61bb25a3535fbe7cf0d0fe687f2ea7bc565b799dcf0fcb6d;triage_receipt=/tmp/triage-mi-test-hygiene-20261021.json;receipt_sha256=4f0a610213a5f6363e303e7129cd09a630ae1a04acbaa5116635c804b2f7f56c
|
||||
|
||||
- 授权与交付终点:修复 `tests/integration/multi-instance` 两处测试卫生缺陷(轮询静默超时、断言早于恢复视图),让失败直指真正原因。不得改 Backend 或任何生产代码(其根因已由 Backend PR #237 修复)、不得弱化断言、不得放大超时预算。
|
||||
- 交付模式: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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | 5360884e9283d962caf945d944df05b5e5059177 | 5360884e9283d962caf945d944df05b5e5059177 | 2026-10-21T12:07:32Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:owner_crash_takeover 的 interrupted 轮询改为有界等待 + 显式失败(点名未观察到的 run 状态序列、轮询实例、实际耗时与预算、实例日志),未收口时不再落到后续 POST 的误导性 409;restart_invariants 在断言消息数之前新增「恢复视图」有界等待(存在 interrupted run 且无 pending/running/waiting_hitl),未恢复时给出点名判据的失败信息;其余 `t.Fatal`→`t.Fatalf` 仅补失败信息,断言条件未变。新增 `runStatuses` 与 `e.instanceOf` 两个小helper 供等待条件与失败信息复用。
|
||||
- 验证:PASS——真实 PostgreSQL(127.0.0.1:54331,fsync=on)下空闲机器 15.1s 通过、96 忙循环压满 32 核 17.4s 通过(新增失败路径未误触发);`gofmt`/`go vet` 干净;独立 Review(pi-glm,PASS 无阻塞项)报告 `.task/reviews/2026-10-21-mi-diagnostics-review.md`,6 项非阻塞发现中第 1 项如实指出恢复视图判据的 interrupted 半边由历史 run 平凡满足(不检出 B 重启对账回归,但改动前同样不检出,覆盖无回退)。
|
||||
- 影响与剩余项:不改生产代码、不弱化任何断言、未放大任何超时预算、包级 `go test -timeout` 未动。Review 其余非阻塞项(busy 白名单对新增非终态 status 不设防、轮询只附 logA、`instanceOf` 守卫不可达、顺序耦合、瞬态 5xx 即失败)均为既有共享弱点或死代码,不阻塞。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:PASS(独立会话 pi-glm / zai-coding-cn/glm-5.3-flash;base 5360884e9283d962caf945d944df05b5e5059177,head 232477b22f997bc4ab87b5983ae0b350f6ae4c43;报告 `.task/reviews/2026-10-21-mi-diagnostics-review.md`;无阻塞项,6 项非阻塞发现;重放依据:host 用真实 PG 在空闲与 96 负载两种条件下各跑一次多实例验收,均通过)
|
||||
- 冲突记录:NOT_RUN(一次交付前同步,0 提交 behind)
|
||||
- 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-21T12:06:44.036919+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "5360884e9283d962caf945d944df05b5e5059177",
|
||||
"after_head": "5360884e9283d962caf945d944df05b5e5059177",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "5360884e9283d962caf945d944df05b5e5059177",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"[repo] branch=main head=5360884e92",
|
||||
"[remote] origin/main=5360884e92 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=3",
|
||||
"[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] 5360884e92(完整:5360884e9283d962caf945d944df05b5e5059177)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch"
|
||||
],
|
||||
"output_sha256": "6a290a280e9e8ec5b78b7bf430d194dbe3a55fbbd9507b05b6c12899b01ceebb",
|
||||
"receipt_sha256": "577c22f0e49c012c394aeb6dbcc188fbf681ab8315c5864da6da3d0ca62b90f8",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/577c22f0e49c012c394aeb6dbcc188fbf681ab8315c5864da6da3d0ca62b90f8.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #678(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/678)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,199 @@
|
||||
# fix/multiuser-collab-followups 任务卡
|
||||
|
||||
任务主记录:PR #659 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/659;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= aa9f98d2e5b8ce36724f881752b0a95a353df987
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-collab-followups(worktree);分支:fix/multiuser-collab-followups
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=34037d0f52e27319a94965baaf2ae922e347bbf5ad152b02033ccde16c77540e;triage_receipt=/tmp/rdir/triage.json;receipt_sha256=d3628fadb7432128a57fa1c1415e9c1049a232c47198bad3d98023e5b5a60abf
|
||||
|
||||
- 授权与交付终点:用户 2026-10-20 选择"新开任务,两项都修并部署 8788、浏览器复测";终点=Backend/Frontend/RDM/Dev PR 经 Review 与当前 HEAD CI 后合入,8788 备份后部署并浏览器复测通过;路由沿用用户授权(Cursor 额度耗尽→跳过,agy 失败→PI)。未授权:改写历史、分支保护、发布版本。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
来源:`.task/archive/fix-multiuser-project-collab.md` 复测后续项。
|
||||
|
||||
1. 非管理员创建者的成员页角色目录(Backend 权威切换后):
|
||||
- `rdm.role.options` 400(`RoleList` 走管理员 `RolePolicyAdmin.ListRoles`)→ Backend 新增可选只读能力(启用角色 roleId/name,任一有效账号的受信分发主体可读),RDM `ProjectRoleOptions` 改用;项目动作授权仍由 RDM 判定。
|
||||
- 成员专业角色显示"—"(`enabledProfRole` 只读本地缓存,其他成员快照未取)→ RDM 经调用者 Host `RolePolicyForOwner` 只读取展示摘要,不写授权缓存。
|
||||
- "账号目录暂不可用"(成员姓名走仅管理员可用的 EXT-19 `ListAccounts`)→ Backend 账号目录新增 rdm 专用用途 `project.member-roster`(作用域只限 RDM 解析器返回的当前成员、不得 AllCandidates、允许空检索枚举与 status=all),RDM 解析器按成员读取授权返回当前成员,成员清单姓名改用该用途。
|
||||
2. 被移出项目的成员发送:Backend `POST /api/conversations/{id}/messages` 对本人会话的项目工作授权"不存在"改回 403 `conversation: not-project-member`(不改其他入口的 404 不泄露语义);Frontend 识别后提示"无法继续发送:你已不在该项目中或项目已关闭;历史对话仍可查看",内容保留。
|
||||
|
||||
非目标:不改管理员角色管理面、不改成员候选检索/添加语义、不改其他入口的错误码、不做 UI 重设计。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| rdir-backend-role-options-roster-send | Backend `pkg/extension`、`internal/app`、`internal/server`:RoleOptionsProvider、project.member-roster、移出成员发送 403 | AGENT-EXECUTION.md | implementation | Cursor→agy→PI(用户授权:Cursor 额度耗尽、agy 不可用,均 unavailable) | PI run1 | PI(MiniMax-M3) | success(host Review 通过) | 612e044(已 apply)→ [Backend #228](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Backend/pulls/228) merge ed956a4 | 否 | /tmp/rdir/be-receipt.json |
|
||||
| rdir-frontend-removed-member-send | Frontend `src/store.ts` + test:not-project-member 专用提示 | 同上 | implementation | 同上 | PI run1 | PI | success(host Review 通过) | 9f7f4a1(已 apply)→ [Frontend #121](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Frontend/pulls/121) merge e4cd8f1 | 否 | /tmp/rdir/fe-receipt.json |
|
||||
| rdir-rdm-creator-role-directory | RDM `member_search.go`、`accountdir*.go`、`dirscope.go`、pins backend/frontend:角色选项改走 RoleOptionsProvider、专业角色展示、成员姓名走 roster 用途 | 同上 | implementation | 同上 | PI run1 | PI | blocked(启动前:pins gitlink 未在规范检出初始化,未启动 CLI) | 无 | 否 | /tmp/rdir/rdm-receipt.json |
|
||||
| rdir-rdm-creator-role-directory-r2 | 同上(初始化 pins 后以新单元 ID 重跑) | 同上 | implementation | 同上 | PI run1 | PI | success(host Review 通过) | 2a42673(已 apply)→ [RDM #456](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/456) merge ed3a0f9 | 否 | /tmp/rdir/rdm2-receipt.json |
|
||||
| rdir-dev-repin-backend-frontend | Dev gitlink `modules/backend`→ed956a4、`modules/frontend`→e4cd8f1 | 同上 | implementation | 同上 | PI run1 | PI | success | 9c28a17(已 apply) | 否 | /tmp/rdir/devpin-receipt.json |
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | aa9f98d2e5b8ce36724f881752b0a95a353df987 | aa9f98d2e5b8ce36724f881752b0a95a353df987 | 2026-10-20T11:50:50Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:两项后续均已修复并部署 8788(备份 20261020T123051Z 后 `svc.sh dev deploy --from rdm@ed3a0f9`)。
|
||||
- 验证:
|
||||
- Dev `make test`(本分支 9c28a17 组合)exit 0(SELF,证据块见下方)。
|
||||
- 组件 CI(GATE,PR head):Backend 612e04402ddaae77bcbc6489d7dc48d477670f96 vet-and-test run 4478 + governance run 4479 success;Frontend 9f7f4a164b09766c872d4cdf063aafb949a7525f test-and-build run 4474 + governance run 4475 success;RDM 2a4267337aa6dc209f0ddd0ebe5bbecbb69439b2 build-and-test run 4492 + governance run 4493 success。
|
||||
- 8788 浏览器复测(SELF,Playwright,截图 /tmp/rdir/rt/r01–r05)PASS:非管理员创建者 vf-alice 成员页显示专业角色筛选(4 个启用角色)、bob 专业角色显示"复测成员"、无"账号目录暂不可用"、无 API 错误,按"复测成员"筛选得 1 人;vf-carol 被移出后从仍打开的会话发送 → 后端 403 `conversation: not-project-member`,界面提示"无法发送:你已不在该项目中或项目已关闭,内容已保留",无通用"发送未成功",草稿保留。
|
||||
- 复测后 vf-alice/vf-bob/vf-carol 已重新停用,临时密码与浏览器会话文件已删除。
|
||||
- 影响与剩余项:非阻塞简记——无项目账号加载时 `GET /api/projects/surfaces` 返回 404(控制台噪音,不影响功能)。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:host(当前主模型,独立于 PI 实施会话)审阅 Backend 612e044、Frontend 9f7f4a1、RDM 2a42673、Dev 9c28a17 的实际 diff 与测试:通过,无阻塞项。
|
||||
- 冲突记录:各组件 PR 基于最新 main,无冲突;Dev 分支同步见同步记录。
|
||||
- CI 记录:组件 PR HEAD 的 GATE 结果见"结果与验证";Dev PR 以 `make ci-wait` 核验当前 HEAD。
|
||||
- 合入记录:[Backend #228](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Backend/pulls/228)→ed956a4;[Frontend #121](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Frontend/pulls/121)→e4cd8f1;[RDM #456](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/456)→ed3a0f9(均以 head_commit_id 核验后合入 main);Dev PR 见任务主记录。
|
||||
- 清理记录: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-20T11:48:06.912214+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "aa9f98d2e5b8ce36724f881752b0a95a353df987",
|
||||
"after_head": "aa9f98d2e5b8ce36724f881752b0a95a353df987",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "aa9f98d2e5b8ce36724f881752b0a95a353df987",
|
||||
"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=aa9f98d2e5",
|
||||
"[remote] origin/main=aa9f98d2e5 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=4",
|
||||
"[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] aa9f98d2e5(完整:aa9f98d2e5b8ce36724f881752b0a95a353df987)",
|
||||
"[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": "fca750cab8af9f00ee06d28a9513288c7be9beaeda66843a076313b0352e9b8e",
|
||||
"receipt_sha256": "6e54ff03616e76c7e9f484fbf6913a447043a4311f5f9f25ac98be99753f141d",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/6e54ff03616e76c7e9f484fbf6913a447043a4311f5f9f25ac98be99753f141d.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T12:28:21.313639+00:00",
|
||||
"command": "make test",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-collab-followups",
|
||||
"before_head": "9c28a173139823b85abfe27a87409ace691760a5",
|
||||
"after_head": "9c28a173139823b85abfe27a87409ace691760a5",
|
||||
"branch": "fix/multiuser-collab-followups",
|
||||
"target_branch": "main",
|
||||
"target_head": "873e957306a791eaa36e5251b45e3ed44a863143",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"added 162 packages in 865ms",
|
||||
"",
|
||||
" RUN v4.1.11 /home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-collab-followups/modules/ui",
|
||||
"",
|
||||
"",
|
||||
" Test Files 19 passed (19)",
|
||||
" Tests 187 passed (187)",
|
||||
" Start at 12:35:40",
|
||||
" Duration 1.71s (transform 1.52s, setup 0ms, import 3.48s, tests 3.94s, environment 6.46s)",
|
||||
"",
|
||||
"knip unused exports 61 <= 61",
|
||||
"cd deps/agent-command && go vet ./... && go test -count=1 ./...",
|
||||
"ok \tagent-command\t0.002s",
|
||||
"ok \tagent-command/conformance\t0.001s",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-collab-followups'"
|
||||
],
|
||||
"output_sha256": "7a0152745a64d99399bda3ca3a0b4718ec33e5a0e1aabfbb742d9833554cbf41",
|
||||
"receipt_sha256": "b393cb6d303e5f92720ae1a20f22eab5a7af1ac7f145ae6dc6640bf9956ad07d",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/b393cb6d303e5f92720ae1a20f22eab5a7af1ac7f145ae6dc6640bf9956ad07d.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T12:30:00.628700+00:00",
|
||||
"command": "make test",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-collab-followups",
|
||||
"before_head": "9c28a173139823b85abfe27a87409ace691760a5",
|
||||
"after_head": "9c28a173139823b85abfe27a87409ace691760a5",
|
||||
"branch": "fix/multiuser-collab-followups",
|
||||
"target_branch": "main",
|
||||
"target_head": "873e957306a791eaa36e5251b45e3ed44a863143",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "3dff520d5ae58e4708e86bb6bc0c5835bb4dcd71171e71e4ec72dd14c3bb4cb0",
|
||||
"output_tail": [
|
||||
"added 162 packages in 1s",
|
||||
"",
|
||||
" RUN v4.1.11 /home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-collab-followups/modules/ui",
|
||||
"",
|
||||
"",
|
||||
" Test Files 19 passed (19)",
|
||||
" Tests 187 passed (187)",
|
||||
" Start at 12:37:22",
|
||||
" Duration 1.95s (transform 1.82s, setup 0ms, import 4.29s, tests 4.47s, environment 8.53s)",
|
||||
"",
|
||||
"knip unused exports 61 <= 61",
|
||||
"cd deps/agent-command && go vet ./... && go test -count=1 ./...",
|
||||
"ok \tagent-command\t0.002s",
|
||||
"ok \tagent-command/conformance\t0.002s",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-collab-followups'"
|
||||
],
|
||||
"output_sha256": "6e8bc51b1f3c52ce09b4190798402fb0caee24a0039fcd59e9920b3820af3d60",
|
||||
"receipt_sha256": "21b110da39b1157e849b5ce920e6c10b84c81025de9fe6083ae7cdb53e8ac237",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/21b110da39b1157e849b5ce920e6c10b84c81025de9fe6083ae7cdb53e8ac237.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 886283a4a534de8c418fa4a60d2d7a5d15c0ccfb | 61912e5504ebbbe904b758106d6a2662a90848cd | 2026-10-20T12:57:09Z |
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #659(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/659)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | daf02880bb0aff82c2c127bbc9473b680f429907 | 55ee4c9a3d83e8c59e7c602771d9902e23ef6f02 | 2026-10-20T13:21:53Z |
|
||||
| 同步(记录前对象) | 556201541c9a98f34fdad860cfad886af069279d | 83769b48db21f5da5f85e9fdf961185baf355ff6 | 2026-10-20T14:05:13Z |
|
||||
| 同步(记录前对象) | 7e19cf3e182b566bdc48a7e690e44ff11b4ab33c | 51617e0bc5dbf1c44412afb96735b4be3e588292 | 2026-10-20T14:31:29Z |
|
||||
| 同步(记录前对象) | ed3847c85ce111a74f0cb100908abfb40f62db07 | 3c5a4bf8ff2be7f960b8f3b435d6213637314644 | 2026-10-20T14:47:48Z |
|
||||
@@ -0,0 +1,408 @@
|
||||
# fix/multiuser-project-collab 任务卡
|
||||
|
||||
任务主记录:PR #655 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/655;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= ac594c90e536cfb670e94680f045a6c8247a2513
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab(worktree);分支:fix/multiuser-project-collab
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=2c544aa5ca7754aa2176143efbcfcb67df848dae926986c386d32ee995347ccf;triage_receipt=/tmp/mupc/triage.json;receipt_sha256=1c20828d642c90e745e5e5824412ea6e6b35980f4241ad87ed8fd1b8362e2a80
|
||||
|
||||
- 授权与交付终点:用户 2026-10-20 要求解决 8788 浏览器多用户验证发现的两个阻塞问题和全部非阻塞问题,并批准计划 Plan-9d9489b1(推荐方案:正式切换角色权威到 Backend 并联动管理权限;合入后先备份 8788,再带超管凭据部署并浏览器复测)。终点:Backend/Frontend/RDM 组件 PR 经独立 Review 与精确 HEAD CI 合入;Dev 改钉经本 PR 合入;8788 完整备份后部署 RDM 新版本并执行角色权威切换;浏览器复测通过并停用 `vf-` 测试账号。未授权:改写历史、改分支保护、删除归档引用、发版打标。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
**目标**:8788 上多用户协作流程(建号→建项目→加成员→成员对话与知识检索→权限边界→移除成员)完整可用。
|
||||
|
||||
| ID | 描述 | 验收条件 | 仓 |
|
||||
|---|---|---|---|
|
||||
| R1 | 成员知识检索不再卡死整个服务:共享检索扫描在授权回调查库前关闭结果集 | SQLite 单连接下授权回调查库的回归测试通过;8788 成员检索返回结果且服务可继续响应 | Backend |
|
||||
| R2 | 普通账号凭网页角色可建项目:角色权威切换到 Backend,建项目判定读取 Backend `Linked` 联动(含管理员) | 管理员命令可在生产数据上完成切换(导入 2 职位 3 绑定);有 project.create 角色的账号建项目成功、无角色账号仍被拒 | RDM(+Backend pin) |
|
||||
| R3 | 成员页对无权账号隐藏管理控件、不发无权调用 | 成员列表返回 `canManage`;非创建者看不到添加/移除/职务控件且无 403 调用 | RDM + Frontend |
|
||||
| R4 | 运行计时正确:`CreateRun` 返回真实开始时间;前端零值时间兜底 | 新运行计时从 0 开始,不显示巨大时长 | Backend + Frontend |
|
||||
| R5 | 重启中断的运行留下可见提示 | 中断运行写入带 `run.interrupted` 的助手消息并在对话中可见 | Backend |
|
||||
|
||||
**决策(2026-10-20,用户)**:切换后无职位账号按已批准 I1 合同第 27 行严格执行——项目业务动作默认拒绝、不回落成员默认。8788 上 9 个无职位测试成员(u7-author/rev1/rev2/approver/member、dutyacc-owner/mgr2/member/extra)切换后失去项目访问;复测时测试成员先经网页分配职位再加入项目。8788 Backend 的空角色"系统工程师"(0 权限 0 绑定)在停服备份后直接删库行以满足导入前置(Backend 无删除角色接口)。
|
||||
|
||||
**非目标**:不并入 IAM initiative 未合入的 U3B(RDM b74fc0e 项目失败解释)与 U4(Frontend f015798 角色面板重做);不改 IAM 角色模型;不发版。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| mupc-backend-search-deadlock-run-timing | Backend `internal/store`、`internal/runmgr`(base 8050c6b;R1/R4/R5) | AGENT-EXECUTION.md 默认 | implementation | cursor→agy→pi | Cursor run1 failed-unknown(额度用尽,无改动);用户授权跳过 Cursor(PATH 隐去使其 unavailable×3) | agy | success | 60f37ad → [Backend #223](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Backend/pulls/223) merge b5eb89c | 否 | /tmp/mupc/be-receipt*.json |
|
||||
| mupc-frontend-member-gating-event-time | Frontend 成员页、`rdmManagement.ts`、`store.ts`、test(base d7768d4;R3/R4) | 同上 | implementation | 同上 | 同上(Cursor run1 failed-unknown 额度用尽,授权改 agy) | agy | success | b496c44 → Frontend #116(已关闭:主干合入 IAM U4 #115 已覆盖成员门控,余项转 r2) | 否 | /tmp/mupc/fe-receipt*.json |
|
||||
| mupc-rdm-role-cutover-linked-canmanage | RDM `internal/rdmapp`、`internal/perms`、`plugins/rdm`、pin backend→8050c6b(base 76b4c05;R2/R3) | 同上 | implementation | 同上 | 同上(Cursor run1 failed-unknown 额度用尽,授权改 agy) | agy | failed-unknown(改动完成但 CLI 以 ERROR 收尾无有效终态记录;不集成) | — | 否 | /tmp/mupc/rdm-receipt*.json |
|
||||
| mupc-frontend-r2-event-time-role-options | Frontend r2(base 0b388e0 含 U4):`eventTime` 零值兜底;无管理权时不调 `rdm.role.options` | 同上 | implementation | 同上 | agy failed-unknown(TLS 握手超时);按授权轮到 PI:PI run1 scope-expansion(测试写到 src/store.test.ts),单元包明确测试目录后 PI run2 | PI | success | d997d4a → [Frontend #117](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Frontend/pulls/117) merge c6df2dc(首轮 CI 卡在 npm ci,关闭重开 PR 重跑通过) | 否 | /tmp/mupc/fe2-receipt*.json |
|
||||
| mupc-rdm-role-cutover-linked-r2 | RDM(base 4f745c2 含 U3B):pin backend→b5eb89c;`rdm.role.migrate.finalize`;建项目读 Linked;创建者管理读 Linked.Actions(去掉 canManage,前端用 U4 perm.check) | 同上 | implementation | 同上 | PI run1 改动了正式检出的 pins/agent-system-backend(fail-stop,已还原 3f4b34a);改用仓外只读导出 /tmp/mupc/rdm-ref 并禁止触碰 /home/weichen/projects 后 PI run2 | PI | success(Review 阻塞:未分配职位的系统授权创建者不能管理自建项目;cap-denied 被联动放行,转 r3/r4 修复) | 6ac01d3(已 apply) | 否 | /tmp/mupc/rdm2-receipt*.json |
|
||||
| mupc-dev-repin-backend-frontend | Dev `modules/backend`→b5eb89c、`modules/frontend`→c6df2dc | 同上 | implementation | 同上 | agy run1 | agy | success | b9db4b4(已 apply) | 否 | /tmp/mupc/devpin-receipt*.json |
|
||||
| mupc-rdm-creator-linked-repair-r3 | RDM:未分配创建者按 Linked.Actions 管理自建项目;cap-denied 不放行;pin frontend→c6df2dc | 同上 | implementation | 同上 | agy run1 | agy | failed-unknown(CLI 报 FAILED_PRECONDITION "User location is not supported";不集成,正式检出干净) | — | 否 | /tmp/mupc/rdm3-receipt*.json |
|
||||
| mupc-rdm-creator-linked-repair-r4 | 同 r3 | 同上 | implementation | 同上 | PI run1(按授权 agy 失败轮到 PI) | PI | success(host Review 通过) | 4c72009(已 apply)→ [RDM #454](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/454)(首轮 CI 失败:frontend c6df2dc 需 UI DocBridge.onOpenTaskSource,UI pin 5c2b103 缺) | 否 | /tmp/mupc/rdm4-receipt*.json |
|
||||
| mupc-rdm-repin-ui | RDM `pins/agent-system-ui`→cca5966(UI main) | 同上 | implementation | 同上 | agy run1 | agy | failed-unknown(authentication failed or timed out,0 轮无改动) | — | 否 | /tmp/mupc/rdmui-receipt*.json |
|
||||
| mupc-rdm-repin-ui-r2 | 同上 | 同上 | implementation | 同上 | PI run1(按授权 agy 失败轮到 PI) | PI | success | f16dcc4(已 apply;host 本地 build-product OK,SELF)→ RDM #454 merge 4485158 | 否 | /tmp/mupc/rdmui2-receipt*.json |
|
||||
| mupc-frontend-r3-user-role-view-crash | Frontend r3(base c6df2dc):用户详情对有职位账号白屏(getUserRole 角色无 permissions) | 同上 | implementation | 同上 | agy run1 | agy | failed-unknown(FAILED_PRECONDITION "User location is not supported",0 改动) | — | 否 | /tmp/mupc/fe3-receipt*.json |
|
||||
| mupc-frontend-r4-admin-user-fixes | r3 + 一次性密码 POST 缺 `{}` 体(400 EOF) | 同上 | implementation | 同上 | PI run1(按授权 agy 失败轮到 PI) | PI | failed-unknown(429 Token Plan 用量上限,0 改动) | — | 否 | /tmp/mupc/fe4-receipt*.json |
|
||||
| mupc-frontend-r5-admin-user-fixes | 同 r4 | 同上 | implementation | 同上 | PI run1(额度恢复后新单元重试) | PI | success(host Review 通过) | 1fe3212(已 apply)→ [Frontend #119](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Frontend/pulls/119) merge e2a670e | 否 | /tmp/mupc/fe5-receipt*.json |
|
||||
| mupc-rdm-repin-frontend-admin-fixes | RDM `pins/agent-system-frontend`→e2a670e(base 4485158) | 同上 | implementation | 同上 | PI run1 | PI | success | e3b8af1(已 apply)→ [RDM #455](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/455) merge 8524207 | 否 | /tmp/mupc/rdmfe-receipt*.json |
|
||||
| mupc-dev-repin-frontend-admin-fixes | Dev `modules/frontend`→e2a670e | 同上 | implementation | 同上 | PI run1 | PI | success | 853f774(已 apply) | 否 | /tmp/mupc/devfe-receipt*.json |
|
||||
| mupc-dev-repin-backend-main | Dev `modules/backend`→d8923f1(Backend main,含 b5eb89c;消除 task-sync gitlink 冲突) | 同上 | implementation | 同上 | PI run1 | PI | success | 4d95f9d(已 apply) | 否 | /tmp/mupc/devbe-receipt*.json |
|
||||
| mupc-frontend-r6-knip-exports | Frontend `src/projectmeta.ts` 去掉 `capabilitySourceLabel`、`MemberDuty` 的 export(主干 U4 遗留,knip 60>58 阻塞 Dev `make test`) | 同上 | implementation | 同上 | PI run1 | PI | success(host Review 通过) | f6442cc(已 apply)→ [Frontend #120](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Frontend/pulls/120) merge a13d041 | 否 | /tmp/mupc/fe6-receipt*.json |
|
||||
| mupc-dev-repin-frontend-knip | Dev `modules/frontend`→a13d041 | 同上 | implementation | 同上 | PI run1 | PI | success | d950e02(已 apply) | 否 | /tmp/mupc/devfe2-receipt*.json |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | ac594c90e536cfb670e94680f045a6c8247a2513 | ac594c90e536cfb670e94680f045a6c8247a2513 | 2026-10-20T03:17:46Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:Backend [#223](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Backend/pulls/223)(b5eb89c)、Frontend [#117](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Frontend/pulls/117)(c6df2dc)与 [#119](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Frontend/pulls/119)(e2a670e)、RDM [#454](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/454)(4485158)合入;8788 运维(2026-10-20):`svc.sh dev backup` 快照 runtime-backups/dev/20261020T085635Z(另存 /tmp/mupc/backup-20261020T085636Z,agent-system.db sha256 010a7279…、rdm.db 8bb9f918…)→ 停服删除空角色"系统工程师"及 weichen 06:20 自绑的一条绑定(用户确认)→ 带超管凭据部署 rdm@4485158 → 超管执行 `rdm.role.migrate.finalize`:roles=2、bindings=3、state=ready、sourceSha256 ee5dbb91…,二次调用幂等;库内核对 weichen/test→weichen研发工作、u7-creator→U7创建者,`backend_ready=1`。
|
||||
- 验证(8788 浏览器复测,截图 /tmp/mupc/rt,SELF):网页建职位"复测创建者/复测成员" PASS;超管建号→一次性密码首登强制改密 PASS(网页发放一次性密码 400 → Frontend #119 修复,复测前经 API 发放);普通账号 vf-alice 凭网页职位建项目 PASS;创建者加成员 bob/carol PASS;成员 bob 无管理控件、无 `rdm.role.options` 调用、无 4xx PASS;成员新建对话与回复 PASS,计时"已工作 11 秒"正常 PASS;成员知识检索 78 秒返回结果(3 次工具调用)服务不卡死 PASS;移除 carol 后历史可读、发送被拒(404,内容保留待重试)PASS;运行中重启服务后对话显示"已停止:进程重启或停机,运行已中断"、runs.status=interrupted PASS;管理员打开有职位账号详情白屏 FAIL → Frontend #119 修复;RDM [#455](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/455)(8524207)改钉后备份(runtime-backups/dev/20261020T105522Z)并部署 rdm@8524207,复测:详情正常显示"当前角色 复测创建者 / 创建项目来源:职位角色「复测创建者」"PASS,网页发放一次性密码成功 PASS。收尾:vf-alice/vf-bob/vf-carol 经网页停用(enabled=0),停用后登录 401;测试凭据文件已删除。
|
||||
- 影响与剩余项:后续(非阻塞,用户 2026-10-20 按推荐记为后续任务):切换后普通创建者读取角色目录被拒——成员页 `rdm.role.options` 返回 400(store: account action forbidden),"按专业角色筛选"下拉缺失、部分成员专业角色显示"—"、提示"账号目录暂不可用";成员增删不受影响;修复需 Backend 提供非管理员只读角色目录(Backend+RDM)。移除成员发送失败提示为通用"发送未成功",未说明已被移出项目。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:host 主模型(Cursor 会话,独立于各实施 CLI:agy/PI 新进程,未复用会话)逐单元审阅实际 diff:Backend 60f37ad、Frontend d997d4a/1fe3212/f6442cc、RDM 6ac01d3(阻塞 2 项→r4 4c72009 闭合)/f16dcc4/e3b8af1、Dev b9db4b4/853f774/4d95f9d/d950e02;结论通过。
|
||||
- 冲突记录:Frontend #116 与主干 U4(#115)冲突→关闭并在新主干做 r2;Backend #223、Frontend #117 落后主干经 Gitea update-branch 同步后重跑 CI;RDM #454 首轮 CI 揭示 UI pin 缺 onOpenTaskSource→改钉 UI cca5966 后通过;Dev 分支 task-sync 合并主干时 `modules/backend` gitlink 冲突(主干 d8923f1 vs 本分支 b5eb89c),手工解决的合并提交被 admit 拒绝→abort,经单元 mupc-dev-repin-backend-main 改钉 d8923f1 后 task-sync 干净合并;本地 `make test` 首跑在 frontend knip 棘轮失败(60>58,主干 U4 遗留)→ Frontend #120 修复并改钉 a13d041 后全量通过。
|
||||
- CI 记录(GATE,Gitea Actions,PR HEAD):Backend #223、Frontend #117 97b947a、Frontend #119 1fe321240ad2b911714bb9c2437869784fa8dee0、RDM #454 f16dcc44b54bd9a195076d046e72d7948f4740dc、RDM #455 e3b8af19b667ad6bac8fac5433a626b6103b233f、Frontend #120 f6442cc501e1d763bb9b25e1b9bc5719619ab0b2 均 build/test + governance success(`scripts/ci_wait.py` verdict CI_SUCCESS);Dev 本 PR 见 PR 页。
|
||||
- 合入记录:Backend #223→b5eb89c;Frontend #117→c6df2dc、#119→e2a670e、#120→a13d041;RDM #454→4485158、#455→8524207;均 head_commit_id 锁定已过 CI 的 HEAD 合入 main。Dev 本 PR 改钉 modules/backend→d8923f1(含 b5eb89c)、modules/frontend→a13d041(含 e2a670e)。
|
||||
- 清理记录: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-20T03:16:57.328985+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "ac594c90e536cfb670e94680f045a6c8247a2513",
|
||||
"after_head": "ac594c90e536cfb670e94680f045a6c8247a2513",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "ac594c90e536cfb670e94680f045a6c8247a2513",
|
||||
"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=ac594c90e5",
|
||||
"[remote] origin/main=ac594c90e5 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=6",
|
||||
"[cards] 已合入未闭合任务卡:feature-iam-role-permission-linked-capabilities.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] ac594c90e5(完整:ac594c90e536cfb670e94680f045a6c8247a2513)",
|
||||
"[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": "653818f312318a2746850fefb6c7136aee72b97e768bfa31c4415428a34d2c64",
|
||||
"receipt_sha256": "9bd0aed4e50a12f0d80a753ceb9664dbbdfde6060d7a791dbbfe69c68cb8a1e0",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/9bd0aed4e50a12f0d80a753ceb9664dbbdfde6060d7a791dbbfe69c68cb8a1e0.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | adbef2f49c6c252a09449496afbdb6685201993b | b942e515f45444f581855e53aa3350012dac27fe | 2026-10-20T08:47:24Z |
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T11:02:31.777620+00:00",
|
||||
"command": "make task-sync",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab",
|
||||
"before_head": "902ca4a3ad1f733c38611abbc937b9efbb3024e0",
|
||||
"after_head": "902ca4a3ad1f733c38611abbc937b9efbb3024e0",
|
||||
"branch": "fix/multiuser-project-collab",
|
||||
"target_branch": "main",
|
||||
"target_head": "7e6c544b3c25c510e3d928bfb4515937a1cd6696",
|
||||
"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-wt-fix-multiuser-project-collab'",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[worktree] before-update: recursive component checks passed",
|
||||
"[worktree] materialize: recursive component checks passed",
|
||||
"[worktree] after-sync: recursive component checks passed",
|
||||
"[evidence] WARN JSON receipt line 81: recorded HEAD predates non-card changes ——关键检查须重跑并以 make evidence 重新写入",
|
||||
"[synced] sync_base=7e6c544b3c25c510e3d928bfb4515937a1cd6696 tested_head=902ca4a3ad1f733c38611abbc937b9efbb3024e0 checked_at=2026-10-20T11:03:23Z changed=0 sync_target=main",
|
||||
"[next] 跑正式验收(make test 等),证据写入任务卡",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab'"
|
||||
],
|
||||
"output_sha256": "eb3067da52e583e66efc65ac06b35b8655db8d0ec7135678a3a2f7ef67a42e05",
|
||||
"receipt_sha256": "807ec84234c5fbb97949b3664a0d431b2acdfdf951c5ee33c5ff57853ccac396",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/807ec84234c5fbb97949b3664a0d431b2acdfdf951c5ee33c5ff57853ccac396.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T11:03:26.323564+00:00",
|
||||
"command": "make task-pr-check",
|
||||
"exit_code": 2,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab",
|
||||
"before_head": "902ca4a3ad1f733c38611abbc937b9efbb3024e0",
|
||||
"after_head": "902ca4a3ad1f733c38611abbc937b9efbb3024e0",
|
||||
"branch": "fix/multiuser-project-collab",
|
||||
"target_branch": "main",
|
||||
"target_head": "7e6c544b3c25c510e3d928bfb4515937a1cd6696",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "d1187804f5fb08723368a8e761eb85ebebaa60eaa2f6ed1ec355dc3b27387fe6",
|
||||
"after_tracked_diff_sha256": "d1187804f5fb08723368a8e761eb85ebebaa60eaa2f6ed1ec355dc3b27387fe6",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab'",
|
||||
"python3 scripts/task_pr_check.py local ",
|
||||
"[pr-check] FAIL no open PR has head fix/multiuser-project-collab",
|
||||
"[verdict] PR_CHECK_FAILED",
|
||||
"make[1]: *** [Makefile:68: task-pr-check] Error 1",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab'"
|
||||
],
|
||||
"output_sha256": "321b35a956dd3c650adea6f0e94404affd9d12ed422a4e48698a00d3c8f189a7",
|
||||
"receipt_sha256": "848471e7d664a9b55aa8f77d510e9894924ecd4e658a4938a5d89adfc5113c1d",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/848471e7d664a9b55aa8f77d510e9894924ecd4e658a4938a5d89adfc5113c1d.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T11:03:40.018861+00:00",
|
||||
"command": "make task-pr-check",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab",
|
||||
"before_head": "6f47bdc9df03bdb00b4456e8ebbef8c53cd04b3e",
|
||||
"after_head": "6f47bdc9df03bdb00b4456e8ebbef8c53cd04b3e",
|
||||
"branch": "fix/multiuser-project-collab",
|
||||
"target_branch": "main",
|
||||
"target_head": "7e6c544b3c25c510e3d928bfb4515937a1cd6696",
|
||||
"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-wt-fix-multiuser-project-collab'",
|
||||
"python3 scripts/task_pr_check.py local ",
|
||||
"[verdict] PR_CHECK_OK base=main head=6f47bdc9df03bdb00b4456e8ebbef8c53cd04b3e kind=standalone",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab'"
|
||||
],
|
||||
"output_sha256": "dbe889a94aa50883962d505c7bc6f82f536ec65b14c4f908018cc6ff755744dd",
|
||||
"receipt_sha256": "eb2a31b3933083a985787aff7bd23f20598eef508ff64b224d06f131b99f7624",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/eb2a31b3933083a985787aff7bd23f20598eef508ff64b224d06f131b99f7624.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T11:03:49.132199+00:00",
|
||||
"command": "make test",
|
||||
"exit_code": 2,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab",
|
||||
"before_head": "6f47bdc9df03bdb00b4456e8ebbef8c53cd04b3e",
|
||||
"after_head": "6f47bdc9df03bdb00b4456e8ebbef8c53cd04b3e",
|
||||
"branch": "fix/multiuser-project-collab",
|
||||
"target_branch": "main",
|
||||
"target_head": "7e6c544b3c25c510e3d928bfb4515937a1cd6696",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "ee004c7f3b9c48597bb2e3e9c41d59946a33a3e572890734fa901e806f4c4e53",
|
||||
"after_tracked_diff_sha256": "ee004c7f3b9c48597bb2e3e9c41d59946a33a3e572890734fa901e806f4c4e53",
|
||||
"output_tail": [
|
||||
"(node:530583) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.",
|
||||
"(Use `node --trace-warnings ...` to show where the warning was created)",
|
||||
"(node:530505) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.",
|
||||
"(Use `node --trace-warnings ...` to show where the warning was created)",
|
||||
"(node:530508) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.",
|
||||
"(Use `node --trace-warnings ...` to show where the warning was created)",
|
||||
"",
|
||||
" Test Files 61 passed (61)",
|
||||
" Tests 667 passed (667)",
|
||||
" Start at 11:10:53",
|
||||
" Duration 1.07s (transform 9.63s, setup 0ms, import 16.22s, tests 1.69s, environment 5ms)",
|
||||
"",
|
||||
"knip unused exports 60 exceed baseline 58",
|
||||
"make[1]: *** [Makefile:196: test-frontend] Error 1",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab'"
|
||||
],
|
||||
"output_sha256": "91568aeb41532eb350dd687eb6d2c20dbb2a528e6bfe9418ca9b5ff1ded3df74",
|
||||
"receipt_sha256": "4877e17cdae026a1aa10f15b11a0d17c94374afc7c8396708134d396624676ae",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/4877e17cdae026a1aa10f15b11a0d17c94374afc7c8396708134d396624676ae.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T11:21:00.715404+00:00",
|
||||
"command": "make test",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab",
|
||||
"before_head": "d950e02b3911053a8d071b1db4574a3399ce6e11",
|
||||
"after_head": "d950e02b3911053a8d071b1db4574a3399ce6e11",
|
||||
"branch": "fix/multiuser-project-collab",
|
||||
"target_branch": "main",
|
||||
"target_head": "7e6c544b3c25c510e3d928bfb4515937a1cd6696",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"added 162 packages in 590ms",
|
||||
"",
|
||||
" RUN v4.1.11 /home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab/modules/ui",
|
||||
"",
|
||||
"",
|
||||
" Test Files 19 passed (19)",
|
||||
" Tests 187 passed (187)",
|
||||
" Start at 11:28:07",
|
||||
" Duration 1.50s (transform 765ms, setup 0ms, import 2.48s, tests 3.11s, environment 5.55s)",
|
||||
"",
|
||||
"knip unused exports 61 <= 61",
|
||||
"cd deps/agent-command && go vet ./... && go test -count=1 ./...",
|
||||
"ok \tagent-command\t0.003s",
|
||||
"ok \tagent-command/conformance\t0.004s",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab'"
|
||||
],
|
||||
"output_sha256": "66b2eee2d79f180ef9a5b4068b0ae4817aae36d30aefee48aa06f0a69b82bd48",
|
||||
"receipt_sha256": "fd88f2345c3153f00a4b892892610549d6217aa9a170e859f04c1cc946cb5220",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/fd88f2345c3153f00a4b892892610549d6217aa9a170e859f04c1cc946cb5220.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #655(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/655)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T11:28:46.573322+00:00",
|
||||
"command": "make task-sync",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab",
|
||||
"before_head": "023a9a8fda3997c06fbcc6f502ba024a6feee3f8",
|
||||
"after_head": "023a9a8fda3997c06fbcc6f502ba024a6feee3f8",
|
||||
"branch": "fix/multiuser-project-collab",
|
||||
"target_branch": "main",
|
||||
"target_head": "7e6c544b3c25c510e3d928bfb4515937a1cd6696",
|
||||
"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-wt-fix-multiuser-project-collab'",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[worktree] before-update: recursive component checks passed",
|
||||
"[worktree] materialize: recursive component checks passed",
|
||||
"[worktree] after-sync: recursive component checks passed",
|
||||
"[evidence] WARN JSON receipt line 84: recorded HEAD predates non-card changes ——关键检查须重跑并以 make evidence 重新写入",
|
||||
"[evidence] WARN JSON receipt line 125: recorded HEAD predates non-card changes ——关键检查须重跑并以 make evidence 重新写入",
|
||||
"[evidence] WARN JSON receipt line 160: recorded HEAD predates non-card changes ——关键检查须重跑并以 make evidence 重新写入",
|
||||
"[evidence] WARN JSON receipt line 192: recorded HEAD predates non-card changes ——关键检查须重跑并以 make evidence 重新写入",
|
||||
"[evidence] WARN JSON receipt line 222: recorded HEAD predates non-card changes ——关键检查须重跑并以 make evidence 重新写入",
|
||||
"[synced] sync_base=7e6c544b3c25c510e3d928bfb4515937a1cd6696 tested_head=023a9a8fda3997c06fbcc6f502ba024a6feee3f8 checked_at=2026-10-20T11:29:34Z changed=0 sync_target=main",
|
||||
"[next] 跑正式验收(make test 等),证据写入任务卡",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab'"
|
||||
],
|
||||
"output_sha256": "5ee4f14f0b8c5f037c749403371d2968072430ef8e3a8f4f703ae453da2871dd",
|
||||
"receipt_sha256": "b4b35311e3dca73bca2ff74e68939447df49fec30a77871d65267dd0c5591f1a",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/b4b35311e3dca73bca2ff74e68939447df49fec30a77871d65267dd0c5591f1a.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T11:29:37.583078+00:00",
|
||||
"command": "make task-pr-check",
|
||||
"exit_code": 2,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab",
|
||||
"before_head": "023a9a8fda3997c06fbcc6f502ba024a6feee3f8",
|
||||
"after_head": "023a9a8fda3997c06fbcc6f502ba024a6feee3f8",
|
||||
"branch": "fix/multiuser-project-collab",
|
||||
"target_branch": "main",
|
||||
"target_head": "7e6c544b3c25c510e3d928bfb4515937a1cd6696",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "cc9b52b54d86b87b7534dc01be2fc84da3725aea9f6d62c7a336be0ef7cff74b",
|
||||
"after_tracked_diff_sha256": "cc9b52b54d86b87b7534dc01be2fc84da3725aea9f6d62c7a336be0ef7cff74b",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab'",
|
||||
"python3 scripts/task_pr_check.py local ",
|
||||
"[pr-check] FAIL git ls-remote origin failed",
|
||||
"[verdict] REMOTE_UNVERIFIED",
|
||||
"make[1]: *** [Makefile:68: task-pr-check] Error 6",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab'"
|
||||
],
|
||||
"output_sha256": "fb1007c4d45c061a8810663ae344d2c3f54057073825e85f98568b4a1426c8d0",
|
||||
"receipt_sha256": "31fda187427327a0c17ffde6d28e1dabb6336dc11d5b1209238f21f794054fc4",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/31fda187427327a0c17ffde6d28e1dabb6336dc11d5b1209238f21f794054fc4.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T11:29:45.059295+00:00",
|
||||
"command": "make task-pr-check",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab",
|
||||
"before_head": "023a9a8fda3997c06fbcc6f502ba024a6feee3f8",
|
||||
"after_head": "023a9a8fda3997c06fbcc6f502ba024a6feee3f8",
|
||||
"branch": "fix/multiuser-project-collab",
|
||||
"target_branch": "main",
|
||||
"target_head": "7e6c544b3c25c510e3d928bfb4515937a1cd6696",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "d68c6a62f6367e6cb656461c254cc5fb4c8879b9dbe4b912cc4d26b85cf417c9",
|
||||
"after_tracked_diff_sha256": "d68c6a62f6367e6cb656461c254cc5fb4c8879b9dbe4b912cc4d26b85cf417c9",
|
||||
"output_tail": [
|
||||
"make[1]: Entering directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab'",
|
||||
"python3 scripts/task_pr_check.py local ",
|
||||
"[verdict] PR_CHECK_OK base=main head=023a9a8fda3997c06fbcc6f502ba024a6feee3f8 kind=standalone",
|
||||
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-fix-multiuser-project-collab'"
|
||||
],
|
||||
"output_sha256": "9a94717d232243fac9879b6008ea43df253ca6e48c8f0ad10cd66ae6f0482b45",
|
||||
"receipt_sha256": "fac9b75d815eff6c9ee055f2e6f4da9e714176f8cfd8a716554749062c938183",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/fac9b75d815eff6c9ee055f2e6f4da9e714176f8cfd8a716554749062c938183.json"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,113 @@
|
||||
# fix/no-project-surfaces-404 任务卡
|
||||
|
||||
任务主记录:PR #663 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/663;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 6945b6701f7579bf4e31def283825e392c33ad16
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-no-project-surfaces-404(worktree);分支:fix/no-project-surfaces-404
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=1b640acba3dbeaf8c1845b3a0068132de5c8f156ef23871a3b421697195809e5;triage_receipt=/tmp/rdir/ps-triage.json;receipt_sha256=0ea9b03a7448d0a5f3408d3b4e27edf1029df19da8dbbed2cbdad7414cc4f4c4
|
||||
|
||||
- 授权与交付终点:用户 2026-10-20 要求"解决一下非阻塞问题"(fix/multiuser-collab-followups 记录的无项目账号 surfaces 404);终点=Frontend/RDM/Dev PR 经 Review 与当前 HEAD CI 后合入,8788 备份后部署并浏览器复测;路由沿用用户授权(Cursor 额度耗尽→跳过,agy 失败→PI)。未授权:改写历史、分支保护、发布版本。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
来源:`.task/archive/fix-multiuser-collab-followups.md` 非阻塞简记。
|
||||
|
||||
- 根因:Frontend `src/store.ts` `openConversation` 无当前项目时仍以空 projectId 调 `api.projectSurfaces('')`,请求 `/api/projects//surfaces` 被 Backend 路由清洗为 `/api/projects/surfaces` 而 404。
|
||||
- 修复:无当前项目时不发该请求(`projectSurfaces` 已在同函数内置空);有项目时行为不变。RDM `pins/agent-system-frontend` 与 Dev `modules/frontend` 固定到新 frontend 后部署 8788。
|
||||
|
||||
非目标:不改 Backend 路由与错误码、不改其他 surfaces 入口、不做 UI 改动。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| ps-frontend-no-project-surfaces | Frontend `src/store.ts` + `test/`:无当前项目不请求 projectSurfaces | AGENT-EXECUTION.md | implementation | Cursor→agy→PI(用户授权:Cursor 额度耗尽、agy 不可用) | — | PI | blocked(启动前:unit 文件尚未写入即启动,invalid unit packet;未启动 CLI) | 无 | 否 | /tmp/rdir/ps-fe-receipt.json |
|
||||
| ps-frontend-no-project-surfaces-r2 | 同上(新单元 ID 重跑) | 同上 | implementation | 同上(cursor×3、antigravity×3 unavailable) | PI run1 | PI | success(host Review 通过) | 16a0593(已 apply)→ [Frontend #122](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Frontend/pulls/122) merge 18fc958 | 否 | /tmp/rdir/ps-fe2-receipt.json |
|
||||
| ps-rdm-repin-frontend | RDM `pins/agent-system-frontend`→18fc958 | 同上 | implementation | 同上 | PI run1 | PI | success | 0f35084(已 apply)→ [RDM #457](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/457) | 否 | /tmp/rdir/ps-rdmpin-receipt.json |
|
||||
| ps-dev-repin-frontend | Dev `modules/frontend`→18fc958 | 同上 | implementation | 同上 | PI run1 | PI | success | f66c320(本分支,已 apply;后由下一行撤回) | 否 | /tmp/rdir/ps-devpin-receipt.json |
|
||||
| ps-dev-unpin-frontend | Dev `modules/frontend` 撤回为 R0 值 e4cd8f1(main 已改固定 113103f,冲突无法在本任务范围内解决) | 同上 | implementation | 同上 | PI run1 | PI | success | 7ac0050(本分支,已 apply) | 否 | /tmp/rdir/ps-devunpin-receipt.json |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 6945b6701f7579bf4e31def283825e392c33ad16 | 6945b6701f7579bf4e31def283825e392c33ad16 | 2026-10-20T15:31:49Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:无当前项目时前端不再请求 `/api/projects//surfaces`;修复在 Frontend main 18fc958,经 RDM bd7ea28 固定;8788 已部署 `rdm@bd7ea28`(备份快照 20261020T154639Z)。
|
||||
- Dev 固定:同步时 main 已把 `modules/frontend` 改固定为 113103f(project-access-lifecycle 的 Frontend 分支 `feature/project-access-lifecycle-u6-ui`,尚未合入 Frontend main,且不含 e4cd8f1/18fc958)。合并两者须改动该任务分支,超出本任务范围,故撤回本分支的 frontend 固定,本 PR 仅含任务卡;该任务把 u6-ui 合回 Frontend main 时会带上 e4cd8f1 与 18fc958。
|
||||
- 验证:
|
||||
- 组件 CI(GATE,PR head):Frontend 16a059381c41c3d95d91391de0bf39883db347fd test-and-build run 4541 + governance run 4542 success;RDM 0f35084a394bfa3287f436bd6def2409bbd34a67 build-and-test run 4545 + governance run 4546 success。
|
||||
- 新增单测 `test/open-conversation-no-project.test.ts`:无项目不发请求、有项目仍请求(随 Frontend CI 通过)。
|
||||
- 8788 浏览器复测(SELF,Playwright,/tmp/rdir/rt/r06-carol-no-project-conv.png)PASS:无项目账号 vf-carol 打开旧会话,surfaces 请求仅 `GET /api/conversations/cnv_2e2545025ac2/surfaces`,无 `/api/projects//surfaces`,API 错误为空。复测后 vf-carol 已重新停用,临时口令与会话文件已删除。
|
||||
- 影响与剩余项:记录——Dev main 当前组合的 frontend(113103f)暂不含 Frontend #121/#122 的修复,待 project-access-lifecycle 合回 Frontend main 后由其重新固定;8788 实际运行的是 RDM 固定的 18fc958,不受影响。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:host(当前主模型,独立于 PI 实施会话)审阅 Frontend 16a0593(store.ts 一行守卫 + 新测试)、RDM 0f35084 与 Dev f66c320(各仅一个 gitlink e4cd8f1→18fc958):通过,无阻塞项。
|
||||
- 冲突记录:Frontend/RDM PR 基于最新 main,无冲突;Dev 首次同步 `modules/frontend` gitlink 冲突(本分支 18fc958 vs main 113103f),放弃该次合并,经单元 ps-dev-unpin-frontend 撤回本分支固定后重新同步 main 1850d03,无冲突。
|
||||
- CI 记录:组件见"结果与验证";Dev PR 以 `make ci-wait` 核验当前 HEAD。
|
||||
- 合入记录:[Frontend #122](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Frontend/pulls/122)→18fc958;[RDM #457](https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-RDM/pulls/457)→bd7ea28(均以 head_commit_id 核验后合入 main);Dev PR 见任务主记录。
|
||||
- 清理记录: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-20T15:26:22.705792+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "6945b6701f7579bf4e31def283825e392c33ad16",
|
||||
"after_head": "6945b6701f7579bf4e31def283825e392c33ad16",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "6945b6701f7579bf4e31def283825e392c33ad16",
|
||||
"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=6945b6701f",
|
||||
"[remote] origin/main=6945b6701f ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[recycle] merged un-recycled branches: feature/fast-slow-o2-a01-checkpoint-design",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 6945b6701f(完整:6945b6701f7579bf4e31def283825e392c33ad16)",
|
||||
"[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": "764cb9a09719b74aec61f3fe6443ec035ad0b701efd8bb8cb434b65b722dc600",
|
||||
"receipt_sha256": "2e180d0dd053eec20ad024570a8e1b332956f954bf5929f4843b1f7feae6a7ff",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/2e180d0dd053eec20ad024570a8e1b332956f954bf5929f4843b1f7feae6a7ff.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 1850d03563875fd848664ab9b348f6858d0fa590 | 8264bcbfe5721f6743eb0a33ce7983240b49004b | 2026-10-20T15:55:56Z |
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #663(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/663)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | 7a5c80eacb835bfc6e62b3557129ec299bbb5290 | 9c81fd2d00d52b63788b124ead56d4a916221bcb | 2026-10-21T01:47:29Z |
|
||||
@@ -0,0 +1,96 @@
|
||||
# fix/remaining-hardcoded-ports 任务卡
|
||||
|
||||
任务主记录:PR #676 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/676;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 444fb1dee2a72452be23b97cbaaad9e28c42dffd
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-remaining-hardcoded-ports(worktree);分支:fix/remaining-hardcoded-ports
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=f58f9b9127d88193b75ab8240478a0be0643a63075d58cc54884dfc7b9b4e64e;triage_receipt=/tmp/triage-remaining-port-flakes-20261021.json;receipt_sha256=e190b63d6a5bbcb0285d38a8ae9e1a548a6fb3e3a16dffe8e96dd54cc344a27b
|
||||
|
||||
- 授权与交付终点:消除 PR #668 Review 记下的同类端口硬编码脆弱面,提取 helper 并应用到 tests/test_svc.py 与 tests/test_svc_realcombo.py 的相关位点。不得改 scripts/svc.sh、不得削弱断言、不得改动 test_svc_recover.py / test_task_pr_check.py / test_task_triage.py 的端口。
|
||||
- 交付模式: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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | 444fb1dee2a72452be23b97cbaaad9e28c42dffd | 444fb1dee2a72452be23b97cbaaad9e28c42dffd | 2026-10-21T10:19:04Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:新增 `tests/_port_alloc.py`(`free_test_port()` 每次调用通过 socket reservation 拿一个真正空闲的端口,with 块退出立即 close)。修改 `tests/test_svc.py`(6 处 `svc ... --port 18842/18843/18844` 改为每次 alloc 一个端口,668 与 942 行使用同一分配值因为是同一测试中相同实例的重复确认)和 `tests/test_svc_realcombo.py`(3 个用例各 alloc 一次端口,seed_real/svc/http_ok 三处共享)。`register_pg`/`DSN 18951` 按 PR #668 Review 注明仅写 svc.env、从不绑端口,保留不动;`test_svc_recover.py` 已有自己的 `free_fixture_port` 不动;`test_task_pr_check.py`/`test_task_triage.py` 的端口仅作字典键,不绑端口。
|
||||
- 验证:PASS——`tests.test_port_alloc` 在 64 负载下 OK;`tests.test_svc` 23 项 OK;治理套件 527 项 OK(18 skipped);PR #668 的目标用例 `test_static_fresh_deploy_registers_pidcheck_and_starts` 仍通过。独立 Review(pi-glm,PASS 无阻塞项)。
|
||||
- 影响与剩余项:不改任何生产代码;不削弱断言;realcombo 三例的运行级验证 NOT_RUN(环境门禁与禁网限制),由代码走读与模块级 skip 确认替代。Review 记 3 项非阻塞建议(守护测试未覆盖「外部占用」与 TIME_WAIT 避让、文件名单硬编码而非 glob、helper API 单端口 vs 真实用例可能需多端口对),均不阻塞。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:PASS(独立会话 pi-glm / zai-coding-cn/glm-5.3-flash;base 444fb1dee2a72452be23b97cbaaad9e28c42dffd,head 076a686592f99abb7b4e8c1aa73b7c73b5bccefe;报告 `.task/reviews/2026-10-20-remaining-ports-review.md`;无阻塞项,3 项非阻塞建议;重放依据:host 独立跑治理套件与 64 负载下的 helper 测试)
|
||||
- 冲突记录: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-21T10:18:17.467858+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "444fb1dee2a72452be23b97cbaaad9e28c42dffd",
|
||||
"after_head": "444fb1dee2a72452be23b97cbaaad9e28c42dffd",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "444fb1dee2a72452be23b97cbaaad9e28c42dffd",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"[repo] branch=main head=444fb1dee2",
|
||||
"[remote] origin/main=444fb1dee2 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=2",
|
||||
"[recycle] merged un-recycled branches: feature/card-close-20261021-085838Z",
|
||||
"[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] 444fb1dee2(完整:444fb1dee2a72452be23b97cbaaad9e28c42dffd)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch"
|
||||
],
|
||||
"output_sha256": "3ab6ad8a3228f50b19b9184ab2ded12a122fb8ecf601866a50c269dd41cf03e2",
|
||||
"receipt_sha256": "8bbfc6e4ccd048cce9e097878522ee44a50c32215c0c1df2ab59c8f7d7ae9a83",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/8bbfc6e4ccd048cce9e097878522ee44a50c32215c0c1df2ab59c8f7d7ae9a83.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #676(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/676)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,96 @@
|
||||
# fix/svc-db-card-merge-backfill 任务卡
|
||||
|
||||
任务主记录:PR #680 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/680;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= e634608d97333354c3ead85d47c9e9e368a992a0
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-svc-db-card-merge-backfill(worktree);分支:fix/svc-db-card-merge-backfill
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=3d50b12890499e5825a8b9154efa3f27b7d1557b29cb69fb2267672abe118c88;triage_receipt=/tmp/triage-svcdb-cardbackfill-20261021.json;receipt_sha256=2f6dc84718b854696af1bdedc08e18881a94887e78af9fdd9fdeafd80fb8befb
|
||||
|
||||
- 授权与交付终点:把事故修复任务 svc-db-config-recover 已写好但从未落地的合入回填合入 main,只改 `.task/archive/feature-svc-db-config-recover.md` 的 4 行账务字段(阶段表的合入/合入核验两行 + 合入与清理段的合入/清理记录两行)。不得改动该卡其余任何内容、不得改代码、不得改其他卡。
|
||||
- 交付模式: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 |
|
||||
|---|---|---|---|
|
||||
| 启动 | e634608d97333354c3ead85d47c9e9e368a992a0 | e634608d97333354c3ead85d47c9e9e368a992a0 | 2026-10-21T13:01:16Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:`.task/archive/feature-svc-db-config-recover.md` 的 4 行账务字段由「待定/未评审/NOT_RUN」回填为已发生的合入事实:阶段表 `合入`/`合入核验与收口` 两行、`## 合入与清理` 的 `合入记录`/`清理记录` 两行。内容逐字取自 `feature/svc-db-record-backfill` 分支上早已写好但从未落地的同一段文本,未新增任何断言性表述。分支本身随之失去唯一未并入内容。
|
||||
- 验证:PR 合入事实今日重新取证——PR #342 `merged=true`、merge commit `452f00e6d941ccc72fa72a38e008f7b9c800555d`,且该 merge commit 是 `origin/main` 的祖先;回填文本另提到的 PR #346 为 closed 未合并、#347 已合并,与文本描述一致。CI 结论(`test`/`test-pg` 双 success)沿用该任务自身记录的取证口径,本次未重新取证:该 SHA 的 run 已超出 Gitea Actions API 最近 60 条窗口(2026-10-11),commit-status 接口对这两个 context 返回空状态。文本中该结论本就归属于 PR #342 的记录,非本次新主张。
|
||||
- 影响与剩余项:只动一张已归档卡的账务字段;不改代码、不改该卡的需求/实施/验证/Review 历史;`feature/svc-db-record-backfill` 分支的未并入内容清零。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:NOT_APPLICABLE(纯账务字段回填:4 行 diff,逐字取自已存在的同段文本;PR 合入事实由 host 以 Gitea API 独立复核,见「结果与验证」。按 policy,`.task/**` 属 host 豁免路径,实施不经外部 Agent;独立 Review 由本 PR 的 CI 与 owner 复核承担)
|
||||
- 冲突记录:NOT_RUN(一次交付前同步,0 提交 behind;diff 仅一张归档卡的 4 行账务字段)
|
||||
- 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-21T13:00:15.566597+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "e634608d97333354c3ead85d47c9e9e368a992a0",
|
||||
"after_head": "e634608d97333354c3ead85d47c9e9e368a992a0",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "e634608d97333354c3ead85d47c9e9e368a992a0",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"[repo] branch=main head=e634608d97",
|
||||
"[remote] origin/main=e634608d97 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=4",
|
||||
"[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] e634608d97(完整:e634608d97333354c3ead85d47c9e9e368a992a0)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch"
|
||||
],
|
||||
"output_sha256": "10a3d0e2b4a11180b33c0f27fccccae9f5e1169b677b050628fa038a41d53643",
|
||||
"receipt_sha256": "8d5673f1fabb006921454476d0ea021e42696ee6b50c5d55008e7d58f1e0340c",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/8d5673f1fabb006921454476d0ea021e42696ee6b50c5d55008e7d58f1e0340c.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #680(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/680)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | ccb6dfaa9cdf7e69eb15d7df024dee55f37b8df1 | 588e77d1d8a0f1bd0e15afc4204b1885084ba067 | 2026-10-21T13:22:00Z |
|
||||
@@ -0,0 +1,106 @@
|
||||
# fix/svc-pipefail-early-exit-pipes 任务卡
|
||||
|
||||
任务主记录:PR #664 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/664;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 7e19cf3e182b566bdc48a7e690e44ff11b4ab33c
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-svc-pipefail-early-exit-pipes(worktree);分支:fix/svc-pipefail-early-exit-pipes
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=995ae1c10a5bed201669a98755fcd9db243864384184f224d037521c25d156fb;triage_receipt=/tmp/triage-pipefail-class-20261020.json;receipt_sha256=a136d676c51767e6fc53e36b96e6d518cd235ccc9ad4a77a14449ace86136d8f
|
||||
|
||||
- 授权与交付终点:消除本仓受管 shell 脚本在 `set -o pipefail` 下的早退读取器管道隐患,并加守护测试防复发;按现行门禁完成 Review、PR、CI、合入、任务卡闭合与安全回收。不得改动任何命令的语义与退出码契约,不得改 Makefile/CI/子模块/规范文档,不得用 `|| true`、`set +o pipefail` 掩盖状态,不得新增 schema/开关/通用重构。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
目标:`producer | grep -q PAT`、`... | head -N`、`awk ... exit` 这类「读取器先退出」的管道,在 `set -euo pipefail` 下会让写满 64KiB 管道缓冲的生产者收到 SIGPIPE,管道状态变成 141 而非读取器结论,`if` / `if !` / `&&` 的判定被静默翻转。本任务把本仓受管脚本(`scripts/svc.sh`、`scripts/task.sh`、`scripts/e2e.sh`、`scripts/lint-boundaries.sh`、`docker/verify-env.sh`、`tests/load/collaboration/run_pgbench.sh`)中的此类管道改为语义等价且读取器退出码不再经过管道形态,并新增 `tests/test_shell_pipe_hazard.py` 守护测试防止复发。受影响入口:`make task-finish`、`make precheck`、`make svc` 系列与环境校验。
|
||||
|
||||
非目标:不改变任何命令的行为、退出码与调用顺序;不修 `runtime/` 下的 ignored 陈旧克隆;不改守护测试的检测范围(中段读取器、`awk exit`、`| read`、前缀化 grep 等盲区按 Review 建议保留为非阻塞);不改 Makefile、CI、子模块 pin、共享规范子模块与文档;不新增 schema、运行时开关或通用化重构。
|
||||
|
||||
验收条件:(1) 受管脚本中不再有末级早退读取器管道,守护测试通过;(2) 变换前后行为等价(含命中/未命中/多行匹配/命令替换取值/退出码),并有对照证据;(3) `make test-governance` 与 `test_svc*.py` 全绿;(4) 独立 Review 无阻塞项。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| svc-pipefail-early-exit-pipes | 6 个受管脚本 + `tests/test_shell_pipe_hazard.py` | 本仓 AGENT-EXECUTION.md | implementation | cursor, antigravity, pi-glm, pi, codex | 1 | cursor / auto | success | success (`f87d63f`) | false | `/tmp/svc-pipefail-impl-r1-20261020.json` |
|
||||
| svc-pipefail-independent-review | `.task/reviews/2026-10-20-svc-pipefail-independent-review.md` | 同上 | review | pi-glm, pi, antigravity, cursor, codex | 1 | pi-glm / zai-coding-cn/glm-5.3-flash | success(PASS,无阻塞项) | success (`2ef67de`) | false | `/tmp/svc-pipefail-review-r1-20261020.json` |
|
||||
| svc-pipefail-residual-sites | `scripts/svc.sh` | 同上 | repair | cursor, antigravity, pi-glm, pi, codex | 2(r1 failed-unknown:cursor `auto` 模型不可用,零产物不导入) | cursor / auto | success | success (`5fadf85`) | false | `/tmp/svc-pipefail-repair-r2-20261020.json`(r1:`/tmp/svc-pipefail-repair-r1-20261020.json`) |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 7e19cf3e182b566bdc48a7e690e44ff11b4ab33c | 7e19cf3e182b566bdc48a7e690e44ff11b4ab33c | 2026-10-20T14:22:14Z |
|
||||
| 交付前同步(最终) | 6a7f008ed491e813d7f43fb013cb9542d849ff85 | b9a0309aa4b517d95012b83086a4e02b0d02da85 | 2026-10-20T15:48:20Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:6 个受管脚本中共 41 处早退读取器管道改为语义等价形态——变量入管道改 here-string(`grep -q PAT <<<"$var"`)、命令生产者改进程替换(`grep -q PAT < <(producer)`,二进制 `grep -qa` 亦走进程替换以避免 NUL 截断)、`sed … | head -1` 改 `sed -n '/re/{s//x/p;q;}'`;另新增 `tests/test_shell_pipe_hazard.py`(词法扫描管道末级 + 可失败的合成自检)。未使用 `|| true` / `set +o pipefail` 掩盖状态。
|
||||
- 验证:PASS——行为对照 `/tmp/pipefail-demo-evidence-20261020.json`:旧式在 >64KiB 生产者下把命中分支错误地跳过,新式正确命中,小输入语义不变;dsn 提取新旧表达式在命中/未命中/值内含伪 dsn/文件缺失/多行匹配五种输入下逐一 MATCH(`/tmp/dsn-equiv-evidence-20261020.json`);6 个脚本 `bash -n` 全过;最终 HEAD 上守护测试 4 项 OK、`test_svc*.py` 213 项 OK(18 skipped)、`make test-governance` 518 项 OK(18 skipped)。真实故障参照:PR #657 修复前 Backend 组件索引 76386 字节、旧式管道 rc=141。
|
||||
- 影响与剩余项:不改变任何命令的行为与退出码调用方式;进程替换后生产者自身的退出码不再参与 `if` 判定(Review 逐处判定为修正旧判定翻转或中性等价)。非阻塞(按 Review 建议保留):守护测试仅覆盖末级读取器,中段 `| head`、`awk … exit`、`| read`、前缀化 `grep` 为已知盲区;`SCRIPTS` 元组硬编码,新增受管脚本需手动登记。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:PASS(独立会话 pi-glm / zai-coding-cn/glm-5.3-flash;base 7e19cf3e182b566bdc48a7e690e44ff11b4ab33c,head 2ef67de19e8bceb0eaa1b4ab1dba4667fc108212;报告 `.task/reviews/2026-10-20-svc-pipefail-independent-review.md`;无阻塞项,4 项非阻塞建议;其点名的 svc.sh 三处残留已由 repair 单元 `5fadf858` 收干净,重放依据:host 行为对照 + dsn 等价对照 + `test_svc*.py`/治理套件)
|
||||
- 冲突记录:NOT_RUN(一次交付前同步 `make task-sync`,无冲突;业务差异始终限于 6 个脚本、1 个新增测试与任务记录)
|
||||
- CI 记录:NOT_RUN(当前 HEAD 完整 SHA、全部必需检查、运行链接、执行身份及 GATE 结果)
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-20T14:21:06.568629+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "7e19cf3e182b566bdc48a7e690e44ff11b4ab33c",
|
||||
"after_head": "7e19cf3e182b566bdc48a7e690e44ff11b4ab33c",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "7e19cf3e182b566bdc48a7e690e44ff11b4ab33c",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"[repo] branch=main head=7e19cf3e18",
|
||||
"[remote] origin/main=7e19cf3e18 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=4",
|
||||
"[recycle] merged un-recycled branches: feature/fast-slow-o2-a01-checkpoint-design",
|
||||
"[initiatives] local cards (not authoritative): none",
|
||||
"[hooks] core.hooksPath=scripts/hooks(pre-commit/post-merge 实际接线)",
|
||||
"[facts] policy/facts.md 6 条事实均在有效期内",
|
||||
"[required-reading] core/baseline.md, workflows/delivery.md §1, policy/delivery.md",
|
||||
"[R0] 7e19cf3e18(完整:7e19cf3e182b566bdc48a7e690e44ff11b4ab33c)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch"
|
||||
],
|
||||
"output_sha256": "cc58d05246cfa8a11a3e80ab42f15f9909ea2177ddcf2cb8b01700745fdd9921",
|
||||
"receipt_sha256": "24204950509fc31a6cd58c01eff987d4841dd861e3fc5018df52297722f6b6f9",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/24204950509fc31a6cd58c01eff987d4841dd861e3fc5018df52297722f6b6f9.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 6a7f008ed491e813d7f43fb013cb9542d849ff85 | ba4917e62bec2a4e149391f4f7535e6e937e4559 | 2026-10-20T15:48:20Z |
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #664(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/664)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | 1850d03563875fd848664ab9b348f6858d0fa590 | 3b7123bc5a56bbfefc007167875c044ce3dfe69c | 2026-10-20T16:37:20Z |
|
||||
| 同步(记录前对象) | 36466b99f2fbc8e389167e01e722ece33965df2b | 657dfdf015f77a08cc9817d1d5acc3b272430015 | 2026-10-20T17:05:29Z |
|
||||
@@ -0,0 +1,204 @@
|
||||
# fix/svc-static-deploy-test-port-flake 任务卡
|
||||
|
||||
任务主记录:PR #668 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/668;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= a9566a68505ac97dead15ed3b5672954f15cea1c
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-svc-static-deploy-test-port-flake(worktree);分支:fix/svc-static-deploy-test-port-flake
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=9dd8c0eec3933926486dbc7537df17ada16cbe48d6151410626f43be81914353;triage_receipt=/tmp/svc-flaky-triage.json;receipt_sha256=7e0e09ea9fd8067ba091a3b657f7930bac6d5eceb08da1a0a2c86c415a4c2c7a
|
||||
|
||||
- 授权与交付终点:修复 `tests/test_svc.py` 中因硬编码端口导致的偶发失败用例,standalone 直达 main(PR + 完整 CI + 独立 Review)。不含 `scripts/svc.sh` 改动、不含其它用例的端口治理、不含发布/部署。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 预期结果:`tests/test_svc.py` 的 `SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts` 不再因端口占用而偶发失败;该用例守护的语义(static 首启探活不得把 `http.server` 判成「进程即退」)保持不变。
|
||||
- 根因(host 确定性复现,非推断):用例第 989 行硬编码 `port = 18951`。只要机器上任何进程占用该端口,`scripts/svc.sh` 的 `check_port_free` 就会正确拒绝(「端口被占用(非本服务)」、退出 1),用例因此失败。host 实测:占用 18951 时该用例稳定失败(`AssertionError: 1 != 0`),不占用时 10/10 通过。
|
||||
- 受影响面:仅 `tests/test_svc.py`(测试代码)。产品侧 `scripts/svc.sh` 不改——其拒绝占口的行为是正确的。
|
||||
- 非目标:不改 `scripts/svc.sh`;不改 `tests/test_svc.py:753-754` 的 `register_pg("pg1", 18951, ...)` 夹具(只写 `svc.env` 文本、从不绑端口,与本问题无关);不重构测试类;不新增跨用例端口管理框架(TOCTOU 套件级解法属独立增量)。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| svc-static-deploy-test-port-flake | `tests/test_svc.py` 单文件:硬编码端口 → 内核分配空闲端口 | AGENT-EXECUTION.md digest ccceb3bf | implementation | cursor→agy→pi-glm→pi→codex | 1 | cursor(`cursor-agent-stable`,model auto;session f4bd5b1f-6258-48f5-bcbd-442e1f8a66f2) | success | d0b217d3(refs/agent-exec/850b1455f…/5ca3ade8a790),已 `agent-exec apply` 快进入分支 | never | /tmp/svc-flake-run.json |
|
||||
| review-svc-static-deploy-test-port-flake | 只读审查 | 同上 digest | review | pi-glm→pi→agy→cursor→codex | 1 | pi-glm(`pi`,zai-coding-cn/glm-5.3-flash,session not-observable) | **failed-unknown**(worker exit 0 且只读无改动,控制器不建 transport) | 无 | never | /tmp/svc-flake-review-run.json |
|
||||
|
||||
> 第二次 Review 尝试以全新 `pi` CLI 进程(非 `--continue`/`--resume`,fresh session)单独执行,结论 PASS;原始输出 `/tmp/svc-flake-review-final.log`。上表 failed-unknown 一行如实保留:按 policy/delivery.md,该终态不得计为门禁 Review。
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | a9566a68505ac97dead15ed3b5672954f15cea1c | a9566a68505ac97dead15ed3b5672954f15cea1c | 2026-10-21T02:38:22Z |
|
||||
| 合入前同步 | a496ea0f0249b2b412354677ef97612ccd692d25 | 4ebf9e1b47edb701541f57a72020b31d9e3fb323 | 2026-10-21T03:16:00Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:`tests/test_svc.py:989` 的硬编码 `port = 18951` 换成 `socket.bind(("127.0.0.1", 0))` 内核分配空闲端口(3 增 1 删,单文件单 hunk)。用例的 8 条断言一条未删未弱化。
|
||||
- 验证:
|
||||
- PASS|目标用例在干净主机通过(被测 HEAD 4ebf9e1,SELF,收据 7dfe7b09)
|
||||
- PASS|**目标用例在旧硬编码端口 18951 被外部进程占用时仍通过**——这是修复的决定性一腿;同一场景在修复前确定性失败
|
||||
- PASS|`tests.test_svc` 全模块 23 项 OK(被测 HEAD 4ebf9e1,SELF,收据 7dfe7b09)
|
||||
- PASS|`make test-affected BASE=main` 全量 518 项 OK(被测 HEAD d0b217d3,SELF;同步后非卡片变更触发 WARN,该收据按治理规则作废并以 4ebf9e1 重跑,见上)
|
||||
- NOT_RUN|交付信任所需的精确 HEAD CI(`test`/`test-pg`/`svc-realcombo`)——合入前由 `make ci-wait` 取得 GATE
|
||||
- 影响与剩余项:
|
||||
- 未解决但非阻塞:释放保留端口到 `svc.sh` 真正 bind 之间存在 TOCTOU 窗口。失效模式是响亮失败(`check_port_free` 报错退出),不会静默假绿;且与本仓既有 5 处同类夹具(`test_svc_recover.py:187`、`test_svc.py:366`、`test_task_pr_check.py:100`、`test_task_triage.py:230`、`test_svc_realcombo.py:128`)风险同级。套件级解法属独立增量,不在本任务扩围。
|
||||
- 本任务不解决「其它用例也硬编码端口」这一更大面上的同类脆弱性;仅记录,不在本任务处理。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:PASS。两次独立全新 CLI 会话:① `pi`(zai-coding-cn/glm-5.3-flash),base=origin/main a496ea0f,head=0bd5a4b / 4ebf9e1;② `pi-glm`(zai-coding-cn/glm-5.3-flash),base=2fd11effd9ccb0ae35ae5a47caccefccbbb05a8e,head=c4528653b17665885a7b8f5b5b8f612a68081cef(Backend repin 之后基线)。两次均非 host 主模型、非实施会话、未使用 `--continue`/`--resume`/既有 session id,session 身份 not-observable。核对项:动态取端口写法与仓内 5 处既有夹具惯例一致且 `socket` 已在 `:12` 导入;逐条核对 8 条断言未删未弱化;diff 仅 `tests/test_svc.py` 单 hunk,`:753-754` 夹具未动;TOCTOU 与既有惯例同级、非阻塞;无回归面。第二次 Review 给出 4 项非阻塞建议:reservation TOCTOU 窗口在实践中可忽略、用法可观察性保留、register_pg 改不改的判定一致、其他用例硬编码端口的归属明确(已记入本卡)。重放依据:host 已在 4ebf9e1 与 c4528653b 独立重跑目标用例(含占端口一腿)与全模块(`/tmp/svc-flake-finish-test-20261021.json`、`/tmp/svc-flake-svc-tests-20261021.json`);首次审阅原始输出在仓外 `/tmp/svc-flake-review-final.log`,二次复盘 `/tmp/svc-flake-review-r1-20261021.json`。
|
||||
- 冲突记录:无冲突。合入前同步 `make task-sync` 纳入 origin/main a496ea0f(仅并入另一任务的归档卡与 spec,无代码变更、无组件 pin 变动);同步导致被测 HEAD 变化,关键检查已在 4ebf9e1 重跑。
|
||||
- CI 记录:原 CI 失败(run 4592/4594,均为 npm registry `ECONNRESET`/`EIDLETIMEOUT` 与一次 RealCombo SQLite fixture 启动失败)已在本任务外解决:Backend 仓 `Manager.ResolveHITL` 写库顺序竞态修复(PR #237,merge `f084d10`)经 Dev 仓 gitlink 重钉(PR #674,merge `2fd11eff`)落地,CI 抖动在 2fd11eff 之后回归正常(参见 2fd11eff 之后的多次 Dev CI 全绿记录)。本任务在 2fd11eff 之后基线上重跑 PR #668 的 GATE:由 `make ci-wait` 在 fold-closed head 上单次取得(见合入核验)。
|
||||
- 「与本改动无关」的依据:本次 diff 仅 `tests/test_svc.py`;三次失败分别发生在 npm registry 网络与真实 RDM 夹具启动,均不在该文件的影响面;同一窗口 `main@a496ea0f` 的 run 4587 三作业全绿,而 run 4585/4593(其它 PR)同样失败。
|
||||
- 未完成:精确 HEAD 的三作业 GATE 结果。不得以本地 SELF 替代,不得合入。
|
||||
- 合入记录:NOT_RUN(交付授权范围、PR、被核验 HEAD、合入 SHA、目标分支及内容核验)
|
||||
- 清理记录:NOT_RUN(授权依据、写者退出确认、worktree/分支/任务卡处置)——任务卡尚未闭合,worktree `Agent-System-Dev-wt-fix-svc-static-deploy-test-port-flake` 按规则保留现场,不得回收。
|
||||
|
||||
## 当前阻塞与下一动作
|
||||
|
||||
- 阻塞:精确 HEAD 的必需 CI 三作业连续 3 次因基础设施故障红(2× npm registry `ECONNRESET`、1× 真实 RDM 夹具启动退出)。代码侧无待办:独立 Review PASS、本地关键检查在新 HEAD 全绿。
|
||||
- 下一动作:平台 run 级重跑接口已耗尽;需以新提交(仅 `.task/**`)取得新 SHA 后 `make ci-wait`,或由用户决定先处理 CI 基础设施稳定性。
|
||||
- 遗留发现(**不在本任务范围,未修**):`tests/test_svc_realcombo.py` 仍有硬编码端口 9471/9473/9474(`seed_real` / `http_ok` / `deploy --port`),与本次修掉的 `tests/test_svc.py:989` 属同类脆弱性;本任务只授权了后者。
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-21T02:37:25.839631+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "a9566a68505ac97dead15ed3b5672954f15cea1c",
|
||||
"after_head": "a9566a68505ac97dead15ed3b5672954f15cea1c",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "a9566a68505ac97dead15ed3b5672954f15cea1c",
|
||||
"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=a9566a6850",
|
||||
"[remote] origin/main=a9566a6850 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=4",
|
||||
"[cards] 已合入未闭合任务卡:feature-project-access-lifecycle.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] a9566a6850(完整:a9566a68505ac97dead15ed3b5672954f15cea1c)",
|
||||
"[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": "a43e43203f983403f6985ffa5d4fe7de220822e00febdc8b81cf0b86389db00b",
|
||||
"receipt_sha256": "1247ce47e3885b17ccae7257ba8e6fdd8760f56ef681f89967a0e313ce8142ff",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/1247ce47e3885b17ccae7257ba8e6fdd8760f56ef681f89967a0e313ce8142ff.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-21T03:04:28.871393+00:00",
|
||||
"command": "sh -c 'set -e; echo \"[1] target test on a clean host:\"; PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts 2>&1 | tail -3; echo; echo \"[2] same test while the legacy hard-coded port is held by a foreign process:\"; bash /tmp/occupy-port.sh 18951 env PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts 2>&1 | tail -4; echo; echo \"[3] whole module tests.test_svc:\"; PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc 2>&1 | tail -3; echo; echo \"[4] legacy port released again:\"; (ss -ltn | grep -q \":18951 \" && echo LEAK) || echo released'",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-svc-static-deploy-test-port-flake",
|
||||
"before_head": "d0b217d3a486d32db91d69539c39d07ed9dbdae0",
|
||||
"after_head": "d0b217d3a486d32db91d69539c39d07ed9dbdae0",
|
||||
"branch": "fix/svc-static-deploy-test-port-flake",
|
||||
"target_branch": "main",
|
||||
"target_head": "a496ea0f0249b2b412354677ef97612ccd692d25",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"OK",
|
||||
"",
|
||||
"[2] same test while the legacy hard-coded port is held by a foreign process:",
|
||||
"----------------------------------------------------------------------",
|
||||
"Ran 1 test in 0.599s",
|
||||
"",
|
||||
"OK",
|
||||
"",
|
||||
"[3] whole module tests.test_svc:",
|
||||
"Ran 23 tests in 29.185s",
|
||||
"",
|
||||
"OK",
|
||||
"",
|
||||
"[4] legacy port released again:",
|
||||
"released"
|
||||
],
|
||||
"output_sha256": "d8f352a6d39b09c2fca2e868a40723780fa8c7cf3ea4ab79600d894d2069db9f",
|
||||
"receipt_sha256": "314db6279eec9bfc2666b2533347df850ada748e6e99c481bc1633f3faf34c4d",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/314db6279eec9bfc2666b2533347df850ada748e6e99c481bc1633f3faf34c4d.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | a496ea0f0249b2b412354677ef97612ccd692d25 | bbb9024e9718497adf0d6385f6795cb5c7d9255f | 2026-10-21T03:16:00Z |
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-21T03:16:10.154890+00:00",
|
||||
"command": "sh -c 'set -e; echo \"[1] target test, clean host:\"; PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts 2>&1 | tail -3; echo; echo \"[2] target test while legacy hard-coded port is held:\"; bash /tmp/occupy-port.sh 18951 env PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts 2>&1 | tail -3; echo; echo \"[3] whole module:\"; PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc 2>&1 | tail -3; echo; echo \"[4] port released:\"; (ss -ltn | grep -q \":18951 \" && echo LEAK) || echo released'",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-svc-static-deploy-test-port-flake",
|
||||
"before_head": "4ebf9e1b47edb701541f57a72020b31d9e3fb323",
|
||||
"after_head": "4ebf9e1b47edb701541f57a72020b31d9e3fb323",
|
||||
"branch": "fix/svc-static-deploy-test-port-flake",
|
||||
"target_branch": "main",
|
||||
"target_head": "a496ea0f0249b2b412354677ef97612ccd692d25",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"",
|
||||
"OK",
|
||||
"",
|
||||
"[2] target test while legacy hard-coded port is held:",
|
||||
"Ran 1 test in 0.574s",
|
||||
"",
|
||||
"OK",
|
||||
"",
|
||||
"[3] whole module:",
|
||||
"Ran 23 tests in 26.871s",
|
||||
"",
|
||||
"OK",
|
||||
"",
|
||||
"[4] port released:",
|
||||
"released"
|
||||
],
|
||||
"output_sha256": "41ab6880e27c3f44906b392bbe76d342d1019ae1a2aad0690c2739d691330669",
|
||||
"receipt_sha256": "7dfe7b09cfcb370d00c0e9a504a8857530b8637d18e1ab7bf2d4e434093bdb33",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/7dfe7b09cfcb370d00c0e9a504a8857530b8637d18e1ab7bf2d4e434093bdb33.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 2fd11effd9ccb0ae35ae5a47caccefccbbb05a8e | c766a81277112907333ac7f1810436aed51cff21 | 2026-10-21T09:48:46Z |
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #668(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/668)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,165 @@
|
||||
# fix/svc-superadmin-envfile-surface-conv 任务卡
|
||||
|
||||
任务主记录:PR #683 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/683;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= d9e9c0560a2767539f4415b579adbeff86a22ba6
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-svc-superadmin-envfile-surface-conv(worktree);分支:fix/svc-superadmin-envfile-surface-conv
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=1c2e2ec0089a4a524959716deb24ea3e48312199979f87e70337c4b40197747b;triage_receipt=/tmp/svc-sa-surface-20261021/triage.json;receipt_sha256=2adcb48f6e44ae1996bb1e0b09ba34860e4e0a2ae3e5b3dce4e0a84a19a6375e
|
||||
|
||||
- 授权与交付终点:用户 2026-10-21「未处理的其他记录也解决修复一下」(svc 不持久化超级管理员凭据;`POST /api/surfaces` 接受空 `conversationId`);Backend 单元 failed-unknown 后用户选择「全新重跑一次」。终点:Backend 与 Dev 经 PR 合入,并以 `AGENT_SYSTEM_ENV_FILE` 重启 prod 一次登记凭据路径。未授权:prod 业务角色配置、高后果 git 操作
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 目标 1(`scripts/svc.sh`):agent 服务新增登记键 `superadmin_envfile=`,只存凭据文件绝对路径。deploy/start/restart 时若环境给出 `AGENT_SYSTEM_ENV_FILE`,先在停旧服务前校验(普通文件、属当前用户、无组/其他权限位),通过后登记;空值清除登记。拉起 serve 时本次环境未显式给超管凭据则转发登记路径,recover/watchdog 的清空环境子进程因此也能拉起认证实例。`status` 显示登记路径;`docs/svc.md` §4.2 补说明;新增 `tests/test_svc_superadmin_envfile.py`。
|
||||
- 目标 2(Backend):`POST /api/surfaces` 新建页面(无 `surfaceId`)时要求本人存在的 `conversationId`:空值 400,不存在或他人会话 404;修订语义不变。原因:空/未知会话使页面无项目归属,动作接口随之跳过项目生命周期检查。
|
||||
- 非目标:svc 读取或复制凭据内容;surfacesvc/模型工具路径改动;prod 业务角色配置。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| dev-svc-superadmin-envfile-persist | scripts/svc.sh、tests/test_svc_superadmin_envfile.py、docs/svc.md | AGENT-EXECUTION.md | implementation | 按配置 | 1(fresh_session) | cursor | success | apply 快进 0e6453b | 未用 | /tmp/svc-sa-surface-20261021/dev-run.json |
|
||||
| backend-surface-submit-conversation-required | Backend internal/server/platform_api.go、surface_api_test.go | Backend AGENT-EXECUTION.md | implementation | 按配置 | 1(fresh_session) | pi | failed-unknown(隔离区执行 `git submodule update --init` 致 local_config 变化,违反单元约束;源 worktree 未变,现场保留)→ fail-stop | 无 | 未用 | /tmp/svc-sa-surface-20261021/backend-run-attempt1-failed-unknown.json |
|
||||
| backend-surface-submit-conversation-required(用户批准全新重跑,单元补子模块初始化与配置逐字节恢复约束) | 同上 | Backend AGENT-EXECUTION.md | implementation | 按配置 | 1(fresh_session) | pi | success | apply 快进 1e73822→954e744(amend 规范提交,tree 不变) | 未用 | /tmp/svc-sa-surface-20261021/backend-run.json |
|
||||
| dev-svc-superadmin-surface-backend-repin | modules/backend → 528e3a4(Backend #239) | AGENT-EXECUTION.md | implementation | 按配置 | 1(fresh_session) | cursor | failed-unknown(worker 改动正确但终态记录未从信封解出;源 worktree 未变,隔离现场保留)→ fail-stop | 无 | 未用 | /tmp/svc-sa-surface-20261021/repin-run-attempt1-failed-unknown.json |
|
||||
| dev-svc-superadmin-surface-backend-repin(用户批准全新重跑) | 同上 | AGENT-EXECUTION.md | implementation | 按配置 | 1(fresh_session) | cursor | success | apply 快进 → 3c0faf76(amend 规范提交,tree 不变) | 未用 | /tmp/svc-sa-surface-20261021/repin-run.json |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | d9e9c0560a2767539f4415b579adbeff86a22ba6 | d9e9c0560a2767539f4415b579adbeff86a22ba6 | 2026-10-21T14:04:06Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:两项完成。(1) svc.sh 登记 `superadmin_envfile=`(只存路径),deploy/start/restart 校验后登记,裸 restart/recover 自动转发;(2) Backend #239 合入(`528e3a4`),`POST /api/surfaces` 新建页要求本人会话;本分支 gitlink 已钉到该提交。合入后将以 `AGENT_SYSTEM_ENV_FILE` 重启 prod 一次以登记路径。
|
||||
- 验证:`tests.test_svc_superadmin_envfile`+`test_svc_recover`+`test_svc_identity` 51 项 OK(1 跳过,SELF,见证据表);`bash -n scripts/svc.sh` 通过;Backend #239 CI success(GATE,954e744);本 PR 当前 HEAD 的 CI 为合入依据。
|
||||
- 影响与剩余项:svc 成功路径不变,只增路径登记与转发;Backend 只改新建页校验。剩余(非目标):prod 无业务角色时 `admin` 仍不能新建项目。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:当前主模型(host,非实施会话)审阅实际 diff:svc.sh/docs/测试、Backend handler+测试(经 #239)、gitlink;结论通过,无阻塞项;重放依据为上方测试证据与 Backend CI。
|
||||
- 冲突记录: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-21T14:03:32.182767+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "d9e9c0560a2767539f4415b579adbeff86a22ba6",
|
||||
"after_head": "d9e9c0560a2767539f4415b579adbeff86a22ba6",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "d9e9c0560a2767539f4415b579adbeff86a22ba6",
|
||||
"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=d9e9c0560a",
|
||||
"[remote] origin/main=d9e9c0560a ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=3",
|
||||
"[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] d9e9c0560a(完整:d9e9c0560a2767539f4415b579adbeff86a22ba6)",
|
||||
"[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": "d56c09db1d3fa308d17976569429fb7ab950587f2a97e9438432c21db6de7f25",
|
||||
"receipt_sha256": "ec446f0a238e6f6c64167ff09c23059b47bf08df92852d1ac12ee540348affed",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/ec446f0a238e6f6c64167ff09c23059b47bf08df92852d1ac12ee540348affed.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-21T14:22:12.910602+00:00",
|
||||
"command": "PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc_superadmin_envfile tests.test_svc_recover tests.test_svc_identity",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-svc-superadmin-envfile-surface-conv",
|
||||
"before_head": "0e6453bd530feedd973ccfb05f587c5d9d9a6ffc",
|
||||
"after_head": "0e6453bd530feedd973ccfb05f587c5d9d9a6ffc",
|
||||
"branch": "fix/svc-superadmin-envfile-surface-conv",
|
||||
"target_branch": "main",
|
||||
"target_head": "d9e9c0560a2767539f4415b579adbeff86a22ba6",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"......................................s............",
|
||||
"----------------------------------------------------------------------",
|
||||
"Ran 51 tests in 52.867s",
|
||||
"",
|
||||
"OK (skipped=1)"
|
||||
],
|
||||
"output_sha256": "5cde79b38a542c3831ae872cd4d7a2b9cd0ba84b0b129442ded6d4adea577a27",
|
||||
"receipt_sha256": "3d71bc9d4f8e5ed81db87aae76cc3e649dd90f09ee17dafc1bb8ab494725a5ce",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/3d71bc9d4f8e5ed81db87aae76cc3e649dd90f09ee17dafc1bb8ab494725a5ce.json"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
|
||||
```json
|
||||
{
|
||||
"summary_schema": 1,
|
||||
"source": "SELF",
|
||||
"timestamp": "2026-10-21T15:12:21.352177+00:00",
|
||||
"command": "PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc_superadmin_envfile tests.test_svc_recover tests.test_svc_identity",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-fix-svc-superadmin-envfile-surface-conv",
|
||||
"before_head": "3c0faf76b06b999855733bc2a452476793c5d2a0",
|
||||
"after_head": "3c0faf76b06b999855733bc2a452476793c5d2a0",
|
||||
"branch": "fix/svc-superadmin-envfile-surface-conv",
|
||||
"target_branch": "main",
|
||||
"target_head": "d9e9c0560a2767539f4415b579adbeff86a22ba6",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"......................................s............",
|
||||
"----------------------------------------------------------------------",
|
||||
"Ran 51 tests in 54.457s",
|
||||
"",
|
||||
"OK (skipped=1)"
|
||||
],
|
||||
"output_sha256": "28f0a1224bc1d46a43b8faad062d463820003eb8fd3b4c688f06cd8173380a22",
|
||||
"receipt_sha256": "ebaf7682157339ba8813c1ff743a25fa6c0a85eedd8e06cdba3d22c208c242e8",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/ebaf7682157339ba8813c1ff743a25fa6c0a85eedd8e06cdba3d22c208c242e8.json"
|
||||
}
|
||||
```
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #683(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/683)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
@@ -0,0 +1,105 @@
|
||||
# fix/task-finish-force-detect-pipefail 任务卡
|
||||
|
||||
任务主记录:PR #657 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/657;状态:已完成(合入前闭合);owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 7e6c544b3c25c510e3d928bfb4515937a1cd6696
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-task-finish-force-detect-pipefail(worktree);分支:fix/task-finish-force-detect-pipefail
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=128fff01fd538f05112d31e1660a8e97b407241acd5436fbe4036854be0d0bc6;triage_receipt=/tmp/triage-tasksh-force-20261020.json;receipt_sha256=45df21ed5696bc97dda80a2e2b4fafc764852c09f2b8bb0fa93e5e457a40719d
|
||||
|
||||
- 授权与交付终点:修复 `scripts/task.sh` 回收路径的 force 探测缺陷并补回归测试,按现行门禁完成 Review、PR、CI、合入、任务卡闭合与安全回收。不得改动其它门禁语义、不得扩到 `scripts/svc.sh` 等同类隐患、不得新增 schema/开关/通用重构;不改子模块 pin、CI 配置与共享规范子模块。
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
目标:`cmd_finish` 在索引含 gitlink 时必定以 `git worktree remove --force` 删除工作区。当前 `git ls-files --stage | grep -q '^160000 '` 在 `set -euo pipefail` 下,当索引输出超过 64KiB 管道缓冲时,git 收到 SIGPIPE(实测 rc=141),条件被判假、`--force` 静默丢失,Git 拒绝并报 `working trees containing submodules cannot be moved or removed`(2026-10-20 实测于 Backend 组件工作区,索引 76386 字节)。受影响入口:`make task-finish` 对任何含子模块工作区的回收。
|
||||
|
||||
非目标:不改其它回收门禁语义(写者确认、分支类型、合入祖先校验、远端 lease、Git 拒绝后禁止 `rm` 兜底或追加 force 等级、分支与远端分别报告);不修仓库其它 `... | grep -q` 早退管道(如 `scripts/svc.sh`,仅记录为非阻塞);不改 CI、Makefile、子模块 pin、共享规范子模块;不新增 schema、运行时开关或通用化重构。
|
||||
|
||||
验收条件:(1) 索引含 gitlink(含 >64KiB 清单)时回收成功,未修复脚本上同一用例失败;(2) 不含 gitlink 时不追加 `--force`;(3) `make test-governance` 全绿;(4) 独立 Review 无阻塞项。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| task-finish-force-detect-pipefail | `scripts/task.sh`, `tests/test_task.py` | 本仓 AGENT-EXECUTION.md | implementation | cursor, antigravity, pi-glm, pi, codex | 1 | cursor / auto(effective 见收据) | success | success (`d755fa0`) | false | `/tmp/tasksh-force-implementation-r1-20261020.json` |
|
||||
| tasksh-force-independent-review | `.task/reviews/2026-10-20-tasksh-force-independent-review.md` | 同上 | review | pi-glm, pi, antigravity, cursor, codex | 1(r1 为 pi-glm,failed-unknown:产物无有效终态记录,不导入) | pi-glm / zai-coding-cn/glm-5.3-flash | success | success (`a94f06c`) | false | `/tmp/tasksh-force-review-r2-20261020.json`(r1:`/tmp/tasksh-force-review-r1-20261020.json`,failed-unknown) |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | 7e6c544b3c25c510e3d928bfb4515937a1cd6696 | 7e6c544b3c25c510e3d928bfb4515937a1cd6696 | 2026-10-20T11:42:11Z |
|
||||
| 交付前同步 1 | 873e957306a791eaa36e5251b45e3ed44a863143 | d699ba23b2d59c13f4ec8f2907ef98a372ae9c5f | 2026-10-20T12:22:44Z |
|
||||
| 交付前同步 2(最终) | 886283a4a534de8c418fa4a60d2d7a5d15c0ccfb | f56c53881731f4bc0312757026f1d5ec792cb74c | 2026-10-20T12:57:12Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:`cmd_finish` 的 gitlink 检测改为读取完整清单(`git ls-files --stage | awk '$1 == "160000" { found = 1 } END { exit !found }'`),与索引大小无关,不再被 SIGPIPE + `pipefail` 吞掉 `--force`;新增回归用例在临时真实仓库中构造 >64KiB 且 gitlink 行在前的索引,断言回收成功。
|
||||
- 验证:PASS——回归用例在修复后脚本上绿、在修复前脚本(8a8f9f5)上以生产同款错误 `fatal: working trees containing submodules cannot be moved or removed` 红(证据 `/tmp/tasksh-force-red-20261020.json`、`/tmp/tasksh-force-green-20261020.json`);最终 HEAD 上 `finish` 子集 21 项全绿(`/tmp/tasksh-force-final2-finish-20261020.json`)、`make test-governance` 514 项 OK/18 skipped(`/tmp/tasksh-force-final2-governance-20261020.json`);PR #657 首轮 `ci / test` 因 npm registry `ECONNRESET` 失败(基础设施,非代码回归,job 日志 `/tmp/pr657-job7313.log`),同一 SHA 重跑后三项 GATE 全绿。真实组件证据:2026-10-20 Backend 组件工作区 `git ls-files --stage` 为 76386 字节、旧式管道 rc=141。
|
||||
- 影响与剩余项:仅 `scripts/task.sh` 一行判定与 `tests/test_task.py` 一个用例;不改变任何门禁语义(不含 gitlink 时仍不加 `--force`,Git 拒绝后仍禁止 `rm` 兜底或追加 force 等级)。非阻塞:`scripts/svc.sh` 等处的 `... | grep -q` 早退管道存在同类 pipefail 隐患,本次不修,留待独立任务。
|
||||
|
||||
## 合入与清理
|
||||
|
||||
- Review 记录:PASS(独立会话 pi-glm / zai-coding-cn/glm-5.3-flash;base 8a8f9f57a58b4ec8701b576a98c5f0c0c5be5cd5,head a94f06c627938668a653ddf56ee5e2e613d356d5;报告 `.task/reviews/2026-10-20-tasksh-force-independent-review.md`,无阻塞项,非阻塞建议 N1 测试余量;重放依据:host 独立红/绿对照 + `make test-governance`)
|
||||
- 冲突记录:NOT_RUN(两次交付前同步均为 fast-forward/merge 文本合并,无冲突;`git diff origin/main...HEAD` 业务差异始终只有 `scripts/task.sh`、`tests/test_task.py` 与任务记录)
|
||||
- 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-20T11:41:18.622845+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "7e6c544b3c25c510e3d928bfb4515937a1cd6696",
|
||||
"after_head": "7e6c544b3c25c510e3d928bfb4515937a1cd6696",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "7e6c544b3c25c510e3d928bfb4515937a1cd6696",
|
||||
"root_target": "main",
|
||||
"immediate_target": "main",
|
||||
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"output_tail": [
|
||||
"[repo] branch=main head=7e6c544b3c",
|
||||
"[remote] origin/main=7e6c544b3c ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=3",
|
||||
"[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] 7e6c544b3c(完整:7e6c544b3c25c510e3d928bfb4515937a1cd6696)",
|
||||
"[worktree] before-sync: recursive component checks passed",
|
||||
"[verdict] READY — 保留本次 precheck receipt 后建任务工作区:PRECHECK_RECEIPT=/tmp/task-precheck.json make task-start NAME=feature/your-branch"
|
||||
],
|
||||
"output_sha256": "08fd903a645c79b23e9b7cb102169eb975d11052d0abd3187492501b2340bf96",
|
||||
"receipt_sha256": "b8b3b5430bc2ffb78f4604f5ab0070bf5abf4f10ea19f74347daf6e7259e0e8c",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/b8b3b5430bc2ffb78f4604f5ab0070bf5abf4f10ea19f74347daf6e7259e0e8c.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | 873e957306a791eaa36e5251b45e3ed44a863143 | 2af6ca38fd00084b6d6801bb0380d00aad42087c | 2026-10-20T12:22:44Z |
|
||||
| 同步(记录前对象) | 886283a4a534de8c418fa4a60d2d7a5d15c0ccfb | a52999ba6ce9263c502af2991fd540b3ef8d098f | 2026-10-20T12:57:12Z |
|
||||
|
||||
## 闭合记录(合入前折叠)
|
||||
|
||||
- 本卡在 PR #657(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/657)合入前闭合并归档,随该 PR 一同合入。
|
||||
- 该 PR 的合并提交即本任务的落地记录。
|
||||
- 合入后无需回填,也无需单独的闭合 PR。
|
||||
| 同步(记录前对象) | daf02880bb0aff82c2c127bbc9473b680f429907 | a514c64740e0a5a34ea72cafd089873c242ff3b4 | 2026-10-20T13:23:43Z |
|
||||
@@ -0,0 +1,202 @@
|
||||
# fix/pal-component-delivery-pins 任务卡
|
||||
|
||||
任务主记录:<PR 链接,交付前填写>;状态:进行中;owner:weichen
|
||||
|
||||
- 权威仓:ssh://git@192.168.3.2:2222/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= a496ea0f0249b2b412354677ef97612ccd692d25
|
||||
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-fix-pal-component-delivery-pins(worktree);分支:fix/pal-component-delivery-pins
|
||||
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
|
||||
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=5bd167003a0df76cc4ce5252be7b24ce28dbcc9614493a978e3e1b7a8e864dcc;triage_receipt=/tmp/pal-pins-triage.json;receipt_sha256=0f53fa4aab99a9d10ebac3f76c9be1308b4b84b1066a59230f1497bff05e917c
|
||||
|
||||
- 授权与交付终点:<本次允许的动作与终点;未授权事项写明>
|
||||
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
|
||||
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
|
||||
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
|
||||
|
||||
## 目标与非目标
|
||||
|
||||
- 预期结果:把 PR #634 已合入 Dev main、但未进入交付件的组件内容补齐——Backend 与 RDM 各自合入 main,Dev 推进对应 pin,使 U1–U5 真正进入可部署制品。
|
||||
- 已核实基线(2026-10-21 建任务时点):Dev main 钉 `modules/backend=dfb31da`(不含生命周期内容)、`modules/frontend=113103f`(即 U6 交付头,随 Dev 交付但未入 Frontend main)、RDM pin `c6304224`(2026-10-13,早于角色权威切到 Backend 的 #454)。上述取值均为时点快照,精确落后量不作依据。
|
||||
- 非目标:不改 U1–U5 的规格语义;不部署(C16 另起,需 pin 就位后进行);不处理 `feature/rdm-two-trees-followup`(PR #635)的范围。
|
||||
|
||||
## 决策记录
|
||||
|
||||
- **D1(role cap 是否受 R3 carve-out 约束)——用户裁决 2026-10-21,选 A1。** RDM 的 `evalPerm` 有两层不同的覆盖:role cap(`switch` 的 `RoleReasonCapDenied` 与 legacy 的 `!defined || !capAllowed`)与项目覆盖层(末尾 `ps.OverrideFor`)。计划 R3 原文只约束后者(原文用词为 per-action member/project **overrides**)。
|
||||
- 裁决内容:**只让项目覆盖层不再撤销创建者权威本身授予的权限**;role cap 维持现状、继续阻断创建者。覆盖层的「授予」方向也不变。平台上限、账号停用、职位停用、Linked.Conflicts、归档/结项状态守卫、对非创建者的拒绝全部不变。
|
||||
- 实现:`internal/rdmapp/perms_api.go` 的 `evalPerm` 末尾,`creatorGranted := base && relation == perms.RoleOwner && p.CreatedBy == actor && isManagementAction(act)`;`if set && (override || !creatorGranted)`。
|
||||
- 该裁决曾以「Decision A1」写进 `plugins/rdm/system_projects_test.go` 的注释,但当时未在任何仓内决策记录中留痕——这是本卡补记的原因。
|
||||
- **D2(archive/restore 授权门不得被 R3 收敛吞掉)——由计划原文直接决定,非新决策。** U2 第 219 行:「Do not change archive/restore, which remains a separate lifecycle.」故 `canManageProject` 按 act 分流:`ActProjectArchive` 沿用 main 的「创建者或显式授权」门,其余走 R3 的 `requireProjectManagement`。
|
||||
- **D3(ArchivedAt 守卫必须还原,ClosedAt 不设)——由计划原文与 R6 共同决定。** 归档属 Out of scope,main 的只读守卫必须恢复(含创建者);结项属 U4/R6,R6 明文「The creator can still use project-management actions needed to reopen or administer the project.」,故 ClosedAt 刻意不设守卫。
|
||||
- **D4(平台上限对创建者同样生效)——由领域规则与 SR-G9 决定。** 「system.grants 与平台上限写入门禁保持 system.permissions.manage,不得因 R13 放宽」;`perms.SetCap` 注释「所有业务主体均受约束」;SR-G9 创建者「仍受有效角色与平台 cap」。故 `requireProjectManagement` 带 act 参数并恢复 `CapAllows`。
|
||||
|
||||
## Agent 实施收据
|
||||
|
||||
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
|
||||
|
||||
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| dev-advance-rdm-and-dm-pins | `tests/integration/lifecycle/rdm.pin` + `deps/development-manager` 指针 | AGENT-EXECUTION digest ccceb3bf | implementation | cursor→agy→pi-glm→pi→codex | 1 | cursor(session not-observable) | success | b5a0abf(refs/agent-exec…),已 apply 快进入分支 | never | /tmp/dev-pin-run.json |
|
||||
|
||||
## 同步记录
|
||||
|
||||
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|
||||
|---|---|---|---|
|
||||
| 启动 | a496ea0f0249b2b412354677ef97612ccd692d25 | a496ea0f0249b2b412354677ef97612ccd692d25 | 2026-10-21T03:47:24Z |
|
||||
|
||||
## 证据(Evidence)
|
||||
|
||||
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
|
||||
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 组件交付结果(2026-10-21)
|
||||
|
||||
| 组件 | 状态 | 依据 |
|
||||
|---|---|---|
|
||||
| Backend | ✅ 已进 main(`7158bea`) | 生命周期工作已由 PR #234 合入;Dev 的 backend pin `f084d10` 已含该提交(他人推进,已核实祖先关系) |
|
||||
| RDM | ✅ 已进 main(`bc8db88`) | PR #460,五轮独立 Review 终判 PASS,CI 双作业绿 |
|
||||
| Frontend | ✅ 随 Dev 交付 | pin `113103f` 即 U6 交付头 |
|
||||
|
||||
### 交付前的状态
|
||||
|
||||
**可核事实**:Dev 的门禁从未消费交付分支。`Makefile` 的 `test` 目标只测**各组件钉住的检出**(`test-backend` 测 `modules/backend` 的 pin),`rdm.pin` 仅被 `test-lifecycle` / `test-regulations-kb` 消费,而真实生命周期门禁需要真实 PostgreSQL 与 RDM 源仓。因此「Dev 门禁全绿」与「交付分支本身有缺陷」可以同时成立。
|
||||
|
||||
**SELF 观测(非本仓可复核,2026-10-21 时点)**:我在组件仓的临时 worktree 上检出交付分支顶端提交并运行其自带测试,观察到失败——
|
||||
- Backend `bd640df`:单跑 `TestConversationWorkErrorCategorization` 失败,同一用例在 `origin/main` 通过。
|
||||
- RDM `301cf56`:全量 `go test ./...` 有 4 个用例失败,同一运行中 `internal/rdmapp` 绿、`plugins/rdm` 红。
|
||||
|
||||
这两个观测随后被下面的六项缺陷所印证与闭合。若需复核,请在对应组件仓按上述提交检出并运行其自带测试;本仓无法复现,故**不得把本段当作 GATE 证据引用**。
|
||||
|
||||
**勘误**:提交 `537bf66` 的信息中写有「red-at-tip finding」,该表述把上述 SELF 观测说成了既定发现;本条以本节为准。`537bf66` 另写的「差 27 文件/1379 行」「落后 RDM main 918 提交」为当时点的粗略量测,与任何自然口径都不精确对应,现予作废,不作为依据。
|
||||
|
||||
### 修复过程中发现并闭合的授权缺陷
|
||||
|
||||
| 编号 | 内容 | 性质 |
|
||||
|---|---|---|
|
||||
| B1 | `canManageProject` 被整体替换,archive/restore 授权门被吞;两个 main 测试被反向改写成「获授者也拒绝」并注释「新旧一致」 | 越界 + 伪装成延续 |
|
||||
| B2 | 已归档项目只读守卫丢失,改名/成员增删/override 全放行 | 越界(计划禁止改 archive) |
|
||||
| B3 | **平台上限失效**:管理员 `SetCap` 关闭后创建者管理写入全部成功,而 member add 被拒、remove 放行 | **安全天花板被绕过** |
|
||||
| F1 | 已结项项目上命令面 member-add 拒绝创建者,直接 API 却放行 | 自相矛盾(违反 R6) |
|
||||
| F2 | role cap 只挡 add 不挡 remove/suspend/reactivate | 口径不一致 |
|
||||
| F3 | render-profile 族允许非创建者凭 owner 标签或显式覆盖行使 `project.settings` | 违反 R3 |
|
||||
|
||||
结构根因:R3 把管理路径从 `evalPerm` 迁到 `requireProjectManagement` 时**逐项丢门**(先丢归档,再丢平台上限)。最终形态是两个独立维度——`managementLifecycleDenial`(归档拒绝、结项按 R6 不设)与 `managementCeilingDenial`(平台上限、role cap、联动冲突、停用、有效访问)。
|
||||
|
||||
### 顺带落地的治理修复
|
||||
|
||||
Development-Manager PR #32(`2c2e372`)为 `admit` 增加「权威基线内容认可」:暂存路径若与权威基线**逐字节相同**即免 transport。已实测生效——原本 65 个暂存路径全被拒,修复后仅 5 个需 transport,其余 60 个自动认可;最终以空合并提交记录祖先关系时 `admit: ok`,全程无 transport 妥协。
|
||||
|
||||
## 结果与验证
|
||||
|
||||
- 结果:RDM pin 与 dm pin 均已推进并推送(PR #682);**CI `test-pg` 红,当前 blocked。**
|
||||
|
||||
### test-pg 失败(唯一阻塞)
|
||||
|
||||
`svc-realcombo` success、`test` 通过,`test-pg` 在「Clone Agent-System-RDM with nested pins」步骤失败:组合二进制编译报 `missing go.sum entry for module providing package agent-document-renderer/{render,markdown,model,profile,profiles,word}`。
|
||||
|
||||
已核实的成因:RDM `bc8db88` 的 `go.mod` 用本地路径 replace `agent-document-renderer => pins/agent-document-renderer`,因此其 `go.sum` 中**没有任何** agent-document-renderer 条目(实测计数 0)。Dev 侧构建会把 RDM 的 backend replace 重写到 `../backend`(即 Dev 的 `modules/backend`,当前 `f084d10`),而该 backend 直接 import document-renderer 的上述包,于是 RDM 的模块图无法提供对应条目。另注:RDM 自身钉的 backend 是 `bd640df`,与 Dev 的 `f084d10` 已分叉。
|
||||
|
||||
这正是「只有真实组合门禁才能发现」的那一类不兼容——`make test` 全绿(exit 0)也照样漏掉。
|
||||
|
||||
### 根因(已定位,非制品缺陷)
|
||||
|
||||
同一份 RDM 在本地跑 `scripts/build-product.sh` **成功**(产物 `9ea3fcc8cef0df8a`),说明 RDM 制品无缺陷。报错只出现在 CI,原因是 **RDM 的 pins 布局从 4 个变成 6 个**(新增 `pins/agent-document-renderer`、`pins/agent-system-kb-regulations`):
|
||||
|
||||
| 提交 | pins 数量 |
|
||||
|---|---|
|
||||
| `legacy_sha` 72bb1fec | 4 |
|
||||
| 旧 `prev_sha` 3484ec2 | 4 |
|
||||
| 旧 `sha` c6304224 | 4 |
|
||||
| 新 `sha` bc8db88 | **6** |
|
||||
| bc8db88 的第一父 59b3624 | 6 |
|
||||
|
||||
CI 步骤逐个检出并构建三个 pin,依赖三者布局一致。新 `sha` 是 6-pin,而 `prev_sha`/`legacy_sha` 仍是 4-pin,布局不再一致;本次 fail-loud 断言直接点名:`rdm pin c6304224 的 pins/agent-system-frontend 未落地:缺少 go.mod`。
|
||||
|
||||
我先前把 `prev_sha` 设成旧 `sha`(c6304224)是错的——它是 4-pin 布局,且我在单元包与注释里都未核对布局一致性,这是本次返工的直接原因。
|
||||
|
||||
### 进展(run 4714 / job 7830)
|
||||
|
||||
CI 物化修复**已生效**:三个 pin 全部通过 fail-loud 断言并各自构建成功,`missing go.sum entry` 不再出现。`svc-realcombo` 与 `test` 均绿。
|
||||
|
||||
失败因此推进到下一道门禁——真实组合门禁 `make test-lifecycle`,三个用例红:
|
||||
|
||||
| 用例 | 失败点 |
|
||||
|---|---|
|
||||
| `test_prev_and_current_pin_switch_and_back` | `svc.sh` 的 `git fetch(独立 RDM 源仓:rdm)` 退出 1 |
|
||||
| `test_mixed_batch_updates_forward_and_deploy_reverses` | 同上,更新检测失败 |
|
||||
| `test_phase_interrupt_reconciles_on_real_artifacts` | 同上,退出码 86 |
|
||||
|
||||
即:CI 里 `rdm/` 是 `--depth=1` 克隆 + 对三个 pin 的 `--depth=200` 取,`svc.sh` 物化 RDM 提交时要对该源仓做 `git fetch`,在浅克隆/对象边界下失败。这属于 `scripts/svc.sh` 的 RDM 源仓取用方式与 CI 浅克隆之间的新问题,**不在本 PR 的 pin 推进范围内**,且我未继续深挖。
|
||||
|
||||
### 我的两处失误(均已修正)
|
||||
|
||||
1. 选 `prev_sha` 时未核对 pins 布局一致性,把 4-pin 的旧 sha 当成 prev(已改为 6-pin 区间内最老的提交)。
|
||||
2. CI 断言写成「每个 pin 必须有 go.mod」——pin 集合混有 Go 仓与非 Go 仓(前端仓根无 go.mod),产生假失败(已改为「HEAD 等于 gitlink 且工作树有已跟踪文件」)。
|
||||
|
||||
### 未决(需决策)
|
||||
|
||||
- `prev_sha` 应取 bc8db88 的第一父 `59b3624`(6-pin、与新 sha 布局一致、紧邻交付);但这样 `prev_sha` 与 `sha` 仅隔一个合并,生命周期套件的「来回部署身份稳定性」覆盖力度下降。
|
||||
- `legacy_sha` 必须保持「无身份」语义,因而必然是老提交(4-pin)。构建循环是否应继续要求三个 pin 都可构建?若否,则该步骤对 `legacy_sha` 应放宽断言或单独处理——这属于 CI 步骤语义的设计取舍,不宜由实现侧默定。
|
||||
|
||||
|
||||
### 其余验证
|
||||
|
||||
- PASS|pin 取值自洽:`sha=bc8db88` 含交付内容(`managementCeilingDenial`、`managementLifecycleDenial`、`txProjectManaged`、`MemberAddResolveFailure` 均在其树内);`prev_sha` 为原 pin,供生命周期套件来回部署;`legacy_sha` 未动。
|
||||
- PASS|`make test` 在 `98d794e` 上 exit 0、无 FAIL。
|
||||
- PASS|独立 Review 首轮判 FAIL(任务卡把会话内观测写成既定结论),已按其要求改写为可核表述 + SELF 观测 + 勘误,复审通过项见上文。
|
||||
- NOT_RUN|精确 HEAD 三作业 GATE(`test-pg` 红)。本地 SELF 不可替代,不得合入。
|
||||
|
||||
|
||||
- 结果:
|
||||
- 验证:<结论摘要 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-21T03:46:26.935822+00:00",
|
||||
"command": "make precheck",
|
||||
"exit_code": 0,
|
||||
"cwd": "/home/weichen/projects/Agent-System-Dev",
|
||||
"before_head": "a496ea0f0249b2b412354677ef97612ccd692d25",
|
||||
"after_head": "a496ea0f0249b2b412354677ef97612ccd692d25",
|
||||
"branch": "main",
|
||||
"target_branch": "main",
|
||||
"target_head": "a496ea0f0249b2b412354677ef97612ccd692d25",
|
||||
"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=a496ea0f02",
|
||||
"[remote] origin/main=a496ea0f02 ahead=0 behind=0",
|
||||
"[tree] clean submodules_drift=0 uninit=0",
|
||||
"[worktrees] linked=5",
|
||||
"[cards] 已合入未闭合任务卡:feature-project-access-lifecycle.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] a496ea0f02(完整:a496ea0f0249b2b412354677ef97612ccd692d25)",
|
||||
"[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": "d63eb6dfcf96743449b73edabd63a126308f731b7528b1e249ec5a7f4ce8aae4",
|
||||
"receipt_sha256": "b659e67e444244c0f70ec7f71c89044666ba229581bab6ccca200bbf491cd24c",
|
||||
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/b659e67e444244c0f70ec7f71c89044666ba229581bab6ccca200bbf491cd24c.json"
|
||||
}
|
||||
```
|
||||
| 同步(记录前对象) | ccb6dfaa9cdf7e69eb15d7df024dee55f37b8df1 | 2af80d6a24bcde0a6da1f091acdb25c7e1e9a1f4 | 2026-10-21T13:32:30Z |
|
||||
| 同步(记录前对象) | a736d5a420370cff052bd946f6d6397d4c354547 | e8dbbe001ae12048217c0a763ab7699d183f5b1f | 2026-10-21T13:57:35Z |
|
||||
@@ -0,0 +1,91 @@
|
||||
# 收口复审:fix/guard-test-blindspot-coverage(B1/B2 修复后)
|
||||
|
||||
- 审查对象:`70c49470a6fdc0bb8bbe7339dafdc271b981d40a..HEAD`(HEAD=`fcdf57fdfc89041194c7ebd773ae8c27a12d5662`,经 `git rev-parse` 确认与分支 `fix/guard-test-blindspot-coverage` 尖端一致)。范围即单个提交 fcdf57f,`git diff --stat` 显示仅改 `tests/test_shell_pipe_hazard.py`(+195/−26);四处脚本改写与其余已闭合结论所涉文件不在本范围内
|
||||
- 前置报告:`.task/reviews/2026-10-20-guard-test-blindspot-final.md`(BLOCKED,B1=`until <reader>` 条件终止、B2=循环体 `exit` 含 select)
|
||||
- 审查身份:全新独立会话,未参与实现;只读审查,除本报告外未修改任何文件;合成脚本与探针全部位于 /tmp,仓库内无残留
|
||||
- 审查日期:2026-10-21
|
||||
|
||||
## 结论:PASS
|
||||
|
||||
上一份报告的两个阻塞项均已闭合且运行时实证 141;发现机制已按非阻塞建议 N2 扩到无扩展名托管脚本与 docker/*.sh(21 个受管脚本,0 解析错误、0 命中,ignored 陈旧克隆仍不可能入扫描);READER_FAMILIES 声明与实现一致、逐族自检仍在且 while 族扩为 4 条用例;放宽后对受管脚本无新误报/新漏检(守护测试全量 12/12 通过);awk 引号串误报已消除、mapfile 词序豁免有据注释。残留 4 项均为声明外/声明豁免面的非阻塞观察(见下),依本轮可证伪边界不构成阻塞。
|
||||
|
||||
---
|
||||
|
||||
## 重点核验(按本轮 5 项)
|
||||
|
||||
### 1. 上轮阻塞项闭合 — 通过(检测器报出 + 运行时 141 独立实证)
|
||||
|
||||
`_lex` 新增 `exit_out()`(循环帧链上命令位 `exit` 置 `broken`,遇 `)`/`$(` 帧即停——子壳内 exit 不外溢,与运行时一致)、`_until_condition_reads()`(`do` 时捕获 `found[cond_start:]` 为条件列表,`until` 条件含声明族读者即报,首级以 `!` 开头的列表跳过)、`LOOPS` 扩入 `select`。/tmp 独立探针(不复制套件断言;`set -euo pipefail` 下运行):
|
||||
|
||||
| 形态(`seq 1 200000 | <stage>`) | 检测器 | 运行时 |
|
||||
|---|---|---|
|
||||
| `until read -r l; do :; done`(B1 复测) | 报 | 141 |
|
||||
| `until IFS= read -rt 1 l; do :; done` | 报 | 141 |
|
||||
| `until (read -r l); do :; done`(新:条件读者在子壳内) | 报 | 141 |
|
||||
| `until /usr/bin/head -1; do :; done`(新:条件读者绝对路径) | 报 | 141 |
|
||||
| `until timeout 1 head -1; do :; done`(新:条件读者经包装) | 报 | 141 |
|
||||
| `until true && read -r l; do :; done`(新:读者在条件后继列表) | 报 | 141 |
|
||||
| `while read -r l; do exit; done` 与条件 `[ … ] && exit`(B2 复测) | 报 | 141 |
|
||||
| `select x in a b; do exit; done`(新:select 体无条件 exit) | 报 | 141 |
|
||||
| `while read -r l; do for x in a; do exit; done; done`(新:for 嵌套内 exit) | 报 | 141 |
|
||||
| `while read -r l; do { echo "$l"; exit; }; done`(新) | 报 | 141 |
|
||||
| `while read -r l; do case $l in *) exit ;; esac; done`(新) | 报 | 141 |
|
||||
|
||||
反例不报且正确:`(exit 1)`、`x=$(exit 1)`、`case` 体 `echo exit` 均不报且生产者 PIPESTATUS[0]=0(读满,无 SIGPIPE)——子壳/命令替换内 exit 本就不出循环,判定精确。`while` 条件含读者(`while read`/`while grep -q 1`)与 `until ! read`/`until ! grep -q 1` 不报且读满(生产者 0),与声明「条件失败即 EOF 才结束」「! 取反除外」一致。
|
||||
|
||||
### 2. 发现机制 — 通过(N2 闭合,0 解析错误、0 命中)
|
||||
|
||||
- `SCRIPT_GLOBS` → `SCRIPT_DIRS = ("scripts", "docker")` + `_is_shell_script`(`.sh` 直纳,否则按首行 `#!` 的 `\b(?:ba|da|k|z)?sh\b` 判定)。实测 `git ls-files` 目录级 34 个跟踪文件,shebang 过滤排除 13 个非 shell(`docker/Dockerfile`、`docker/README.md`、9 个 `*.py`(`#!/usr/bin/env python3`)、2 个 `*.go`),得 **21 个受管脚本**,含上轮缺口 `scripts/task`、`scripts/dm`、`scripts/agent-exec`、`docker/entrypoint.sh`;`docker/verify-env.sh`、`tests/load/collaboration/run_pgbench.sh` 仍在。
|
||||
- 逐个跑 `_lex` + `hazards`:**0 解析错误、0 命中**(与 `test_managed_scripts_have_no_early_exit_pipe_reader` 一致);`EXEMPT={}` 且过期条目守卫仍在。
|
||||
- ignored 陈旧克隆仍不入扫描:发现只经 `git ls-files`(只列跟踪文件),`.gitignore:9 runtime/`(:10 `runtime-backups/`)经 `git check-ignore` 复核;本 worktree 无 runtime/ 目录,测试仍断言无 `runtime/` 前缀。
|
||||
|
||||
### 3. READER_FAMILIES 声明 ↔ 实现一致 — 通过
|
||||
|
||||
- 循环族声明扩为「while/until/select 循环:循环内命令位的 break 出本循环、命令位的 exit(子壳内除外)、until 条件含上列读取器(! 取反除外)」,与 `LOOPS`、`break_out`、`exit_out`(`)`/`$(` 截断)、`_until_condition_reads`(`!` 跳过)逐条吻合;否定面声明(`while` 条件读者读满、`mapfile` 无 `-n` 读满)也与实现一致。
|
||||
- 前缀/包装清单:`PREFIXES={!,time,command,builtin,env,exec}` + 新 `WRAPPERS={nohup,timeout(−s/−k/−−signal/−−kill-after 带参、1 个时长操作数),nice(−n/−−adjustment),stdbuf(−i/−o/−e/…)}`,与声明「!/time/command/builtin/env/exec 前缀、nohup/timeout/nice/stdbuf 包装命令及其选项与 timeout 时长」一致;`_skip_wrapper_args` 在首操作数处停。新写法探针:`nohup timeout 30 head -1`(包装嵌套)、`timeout --signal=KILL 5 head -1`、`nice -n 5 /usr/bin/head -1` 均报且 141。
|
||||
- `test_declared_reader_families_each_flagged` 仍是逐族自检且加严:while 族扩为 4 条用例(break/exit/until-read/select-break)逐条 `subTest` 断言命中,缺族/空字段即失败;套件实测通过。
|
||||
|
||||
### 4. 放宽后无新误报/新漏检(受管脚本面) — 通过
|
||||
|
||||
- 守护测试全量:`PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_shell_pipe_hazard -v` → **Ran 12 tests, OK**(上轮 11 个 + 新增 `test_loop_family_and_wrappers_flagged_and_harmful`,后者对 until 条件读取/循环体 exit/select break/四种包装逐条断言命中 **且** bash 运行时 141,对 7 个读满写法断言 rc=0 且检测器不报)。
|
||||
- 受管脚本扫描(含新纳入 4 个):21 个全部 0 命中、0 解析错误,无新误报;新族内写法对受管脚本的漏检未发现(本轮 11 个新探针全报出)。
|
||||
- 范围内未触碰 scripts/ 与 docker/(diff 仅测试文件),上轮「四处改写语义等价」「已闭合族」结论无需重查、未发现失效迹象。
|
||||
|
||||
### 5. awk 引号串误报消除、mapfile 词序豁免有据 — 通过
|
||||
|
||||
- N1 闭合:`_awk_exits_early` 先对单引号程序内 awk 字符串字面量 `AWK_STRING → ""` 再查 `exit|nextfile`。实测 `awk '{print "exit"}'`、`awk 'END{print "nextfile"}'` 不报且 rc=0;真危害 `awk '{print "exit"; exit}'`、`awk '{nextfile}'` 仍报且 141——只剥字符串、不放过语句。
|
||||
- N4 闭合:`_mapfile_exits_early` 首操作数截断处新增注释「bash 内建在首个操作数处停止选项解析:`mapfile arr -n 1` 的 -n 不生效、读满输入,不是提前退出」;实测 `mapfile arr -n 1` rc=0(读满),豁免有据且与运行时相符。
|
||||
|
||||
## 非阻塞观察(依可证伪边界,均在声明外或声明豁免面)
|
||||
|
||||
- **O1**:`while read -r l; do : || exit; done` 被报(声明「命令位的 exit」按词法命中,含 `||` 后位),运行时 rc=0(`:` 恒真、exit 永不执行)——过度近似、安全方向;受管脚本无此写法(扫描 0 命中)。与上轮 awk 引号串同类,若未来想收,需区分 `&&`/`||` 条件语境。
|
||||
- **O2**:`until ! awk '{exit 1}'; do :; done` 不报、运行时 141——读者早退且非零退出,`!` 取反后条件首 iter 即成功、循环即止。声明明文「! 取反除外」,属声明豁免面的残留危害(构造性强),记录在案不改判定。
|
||||
- **O3**:`awk -v s="exit" '{print s}'` 仍误报(双引号 shell 参数中的 exit 不在 `SHELL_SQ` 剥离面),rc=0。上轮 N1 的残余同类面(上轮即未含此形),安全方向、受管脚本无触发。
|
||||
- **O4**:`seq 1 200000 | timeout 5`(无被包装命令的畸形用法)不报、rc=125——timeout 用法错误立即退出同样会使管道读取级消失,但声明语义是「透过包装命令找读者」,无读者可找;畸形用法,不属声明族。
|
||||
- **O5**(沿上轮 N3/N6/N7,本轮未重查亦未失效):算术 `$((…|…))` 拆级误报、族外读者(dd/jq/perl/xargs)、`sed -f`/`awk -f` 外部程序文件,均声明外。
|
||||
|
||||
## 可证伪性注入记录(约束要求)
|
||||
|
||||
本轮独立注入 11 个上轮与套件均未覆盖的新形态(B1 条件读者 4 变体、B2/select/嵌套 exit 5 变体、包装 2 变体),**全部被报出且运行时 141**;另注入 6 个读满写法(含 `(exit 1)`、`x=$(exit 1)`、`until ! grep -q 1`),全部不报且生产者读满(PIPESTATUS[0]=0)。检测器对注入并非照单全收:声明外形态(O1 误报、O2/O4 漏报)如实暴露,失败集中在「`!` 豁免面」与「词法过度近似」两处,均为声明已划界处,不构成族内漏检。
|
||||
|
||||
## 已运行的验证命令与结果(均只读;合成物全部位于 /tmp)
|
||||
|
||||
| # | 命令 | 结果 |
|
||||
|---|---|---|
|
||||
| 1 | `git rev-parse HEAD fix/guard-test-blindspot-coverage` | 二者同为 fcdf57fd…;`git log --oneline 70c4947..HEAD` 仅 fcdf57f;`git diff --stat` 范围内仅 `tests/test_shell_pipe_hazard.py` +195/−26 |
|
||||
| 2 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_shell_pipe_hazard -v` | **Ran 12 tests, OK** |
|
||||
| 3 | `git ls-files -- scripts docker …`(旧 glob 与新目录两版对比)+ `_is_shell_script` 差集 | 旧 17 个 → 新 21 个;13 个非 shell(py/go/Dockerfile/README)被 shebang 过滤排除,首行内容逐一核对 |
|
||||
| 4 | Python 遍历 21 个受管脚本 `_lex`+`hazards` | errors=[],findings=[] |
|
||||
| 5 | `git check-ignore -v runtime/x`;`grep -n runtime .gitignore` | `.gitignore:9 runtime/`(:10 runtime-backups/);worktree 无 runtime/ 目录 |
|
||||
| 6 | /tmp 探针矩阵(见第 1、3 节表)+ `set -euo pipefail` 运行时 | 11 危害形态全报且 141;`(exit 1)`/`x=$(exit 1)`/`select :`/`until ! grep -q 1`/`until ! read` 不报且生产者 0 |
|
||||
| 7 | awk/mapfile/timeout 边界探针 | `{print "exit"}`、`END{print "nextfile"}` 不报 rc=0;`{print "exit"; exit}`、`{nextfile}` 报且 141;`mapfile arr -n 1` rc=0;`timeout 5` rc=125 不报(O4) |
|
||||
| 8 | `git status`(收尾) | 除本报告外无任何修改 |
|
||||
|
||||
第 6 项首轮执行时我的探针脚本误用 `set -uo pipefail`(缺 `-e`),管道失败后脚本继续走 `echo` 致基线全 0——判定探针自身缺陷后改用与套件一致的 `set -euo pipefail` 重跑,上表数据以重跑为准(如实记录,含失败)。
|
||||
|
||||
## 未验证事项
|
||||
|
||||
- 未运行 CI、未做 REPLAY(本审查单元不要求);全部结论基于本地只读命令与 /tmp 合成探针。
|
||||
- O2 的 `until ! <读者>` 面仅实测 awk(非零退出)与 `until ! head -1`(timeout 120s 观察到死循环、未等其自然终止);`!` 面其余读者组合未逐一实测。
|
||||
- O3 仅实测 `-v s="exit"` 一种双引号传参形;同类其它传参形未穷举。
|
||||
- 上轮已闭合项(B1/B2 之外的族内写法、四处脚本改写等价性)按收口范围未重查,仅确认本轮 diff 未触及对应文件与逻辑(grep/sed/head/read/mapfile 判定函数无改动,awk 仅加字符串剥离且经反例确认不回归)。
|
||||
@@ -0,0 +1,137 @@
|
||||
# 最终独立审查:fix/guard-test-blindspot-coverage(终审)
|
||||
|
||||
- 审查对象:`7a5c80eacb835bfc6e62b3557129ec299bbb5290..HEAD`(分支 `fix/guard-test-blindspot-coverage`,HEAD=`6ddbae089f61dcda8d74d7f5980fb81631e30ac5`,经 `git rev-parse` 确认即分支尖端)
|
||||
- 涉及文件:`tests/test_shell_pipe_hazard.py`(+406/−44)、`scripts/e2e-duties.sh`(2 处)、`scripts/hooks/pre-commit`(1 处)、`scripts/svc.sh`(1 处)、任务卡与首轮评审文档(`.task/**`,host 豁免路径)
|
||||
- 审查身份:全新独立会话,未参与实现;只读审查,除本报告外未修改任何文件;合成脚本与探针全部位于 /tmp,仓库内无残留
|
||||
- 审查日期:2026-10-21
|
||||
|
||||
## 结论:BLOCKED
|
||||
|
||||
历次评审的全部阻塞项(首轮 B1 子壳/花括组包裹、B2 awk nextfile、B3 while read break、绝对路径与 exec 前缀;第二轮 mapfile/readarray -n 与 read -n)在当前 HEAD 已全部闭合并有运行时 141 实证;四处脚本改写语义等价、发现机制稳定、契约表述诚实。但按本轮判据第 (2) 条与可证伪约束,实测构造出 **2 个此前未覆盖、运行时确认 pipefail 下 141、检测器不报** 的 **while/until 已声明命令族内其它停止写法**:
|
||||
|
||||
1. `seq 1 200000 | until read -r l; do :; done`(条件 read 首行成功即终止循环,无 break);
|
||||
2. `seq 1 200000 | while read -r l; do exit; done` 及条件形 `[ "$l" = 7 ] && exit`(循环体以 exit 终止读取级子壳,无 break)。
|
||||
|
||||
按本轮约束「构造至少一个此前未覆盖的早退读取器形态……如报不出,作为阻塞项」,判 BLOCKED。不推翻已闭合项与等价性结论。
|
||||
|
||||
---
|
||||
|
||||
## 历次评审闭合核验(全部闭合,含独立复测)
|
||||
|
||||
| 历次阻塞项 | 闭合证据(本轮独立复测) |
|
||||
|---|---|
|
||||
| R1-B1 子壳/花括组包裹 `\| (head -1)`、`\| { head -1; }` | 检测器报 `(1,'(head -1)')`/`(1,'{ head -1; }')`;pipefail 实测 141。且帧设计自然覆盖组内嵌套管道:`seq \| (cat \| head -1)`、`seq \| { cat \| head -1; }`、`seq \| ( ( head -1 ) )` 均报,实测均 141 |
|
||||
| R1-B2 awk nextfile | `awk '$1 == 7 { print; nextfile }'` 报;141 实测(套件 test_wrapped_and_prefixed… 内含 bash 运行时断言) |
|
||||
| R1-B3 while read + break(含 break N、brace 内 break) | `while read -r l; do break; done`、`do break 2; done`(嵌套 for)、`do { break; }; done` 均报,实测 141;`do (break); done` 不报且实测 rc=0——子壳 break 本就不出外层循环,判定正确(无误报) |
|
||||
| R1 绝对路径 / exec 前缀 | `\| /usr/bin/grep -q 1`、`\| exec grep -q 1`、`\| exec /usr/bin/head -1`、`\| /usr/bin/env LC_ALL=C grep -q 1` 均报 |
|
||||
| R2 mapfile/readarray -n | `-n1`、`-t -n 3`、`-tn2`、`-u 0 -n 1`、`--n 1`、`--numlines 1`、`builtin mapfile -n 1`、`{ mapfile -n 1 arr; }` 均报;套件对 `-n 1`/`read -n 5` 附带 bash 运行时 141 断言并通过 |
|
||||
| R2 read -n | `read -n 5 -r line`、`read --chars 5 line` 均报(read 族「任意用法」) |
|
||||
|
||||
## 重点核验(按本轮 5 项)
|
||||
|
||||
### 1. READER_FAMILIES 七族自检与族外不误报 — 通过
|
||||
|
||||
- `READER_FAMILIES`(tests/test_shell_pipe_hazard.py:23)7 族非空三元组;`test_declared_reader_families_each_flagged`(:548)以 `cases` 字典逐族断言「声明族必有被命中合成用例」,缺族/空字段即失败——契约↔测试 linkage 成立,套件 11/11 通过。
|
||||
- 族外安全写法不误报:套件 33 行安全清单通过;本轮追加探针 `seq \| (cat)`、`seq \| env FOO=1 command cat`、`seq \| LC_ALL=C grep -c 1`、`seq \| mapfile -t arr`、out-of-family 读者 `dd`/`jq -n`/`perl -ne`/`xargs -n1` 全部不报。两处既有安全方向误报(awk 引号串 "exit"、算术按位或)见 N1/N3,均不涉当前受管脚本。
|
||||
|
||||
### 2. 族内同族写法漏检 — 发现 2 个阻塞项(见下),其余族闭合
|
||||
|
||||
- grep:`_grep_exits_early`(:336)**不因操作数停扫**(非选项词仅跳过),故 `grep 1 -q`、`grep 1 --quiet`、`grep 1 -m1`、`grep -e 1 -q`、`grep -f file -q 1`、`grep -d skip -q 1` 全部报;grep 无 -q/-m 之外的提前退出选项。族内无漏检。
|
||||
- sed:`[qQ][0-9]*` 覆盖 `q`/`Q`/`qN`(`sed 'q2'` 实测即带退出码 2 的立即退出,被报);GNU sed 无其它停止命令。词法不可见的 `-f progfile` 见 N7。
|
||||
- awk:END 块剥离后查 `exit|nextfile`,BEGIN 内 exit 报、仅 END 内不报(读满输入,安全)——与声明一致。
|
||||
- head/read:任意用法全报。
|
||||
- mapfile:`-n` 簇内/独立/长选项全覆盖;词序倒置 `mapfile arr -n 1` 不报但 bash 内建 getopt 停于首操作数、`-n` 不生效,实测 rc=0 读满输入——惰性写法,非危害(N4)。
|
||||
- **while/until:除 break 外仍有两种停止写法漏检 → B1/B2 阻塞。**
|
||||
|
||||
### 3. 发现机制与受管脚本扫描 — 通过
|
||||
|
||||
- `managed_scripts()`(:51)经 `git ls-files -z -- ':(glob)scripts/**/*.sh' ':(glob)scripts/hooks/*' + 2 个 EXTRA`,本轮实测发现 17 个文件(含 3 个无扩展名 hooks);`scripts/hooks/pre-commit` 无扩展名仍被 `:(glob)scripts/hooks/*` 命中。
|
||||
- 稳定性:`_lex`+`hazards` 显式跑遍 17 个脚本,0 解析错误、0 命中(与 `test_managed_scripts_have_no_early_exit_pipe_reader` 一致);`EXEMPT={}` 且有过期条目守卫(:613)。
|
||||
- ignored 陈旧克隆不入扫描:`git ls-files` 只列跟踪文件 + `.gitignore:9` `runtime/`(`runtime-backups/:10` 同理);测试断言无 `runtime/` 前缀路径。本 worktree 无 runtime/ 目录,机制上也不可能漏入。
|
||||
- 非阻塞残留(N2,首轮已提、本轮复核仍在):`scripts/agent-exec`、`scripts/dm`、`scripts/task`(无扩展名 bash)与 `docker/entrypoint.sh` 不在 glob 范围;旧硬编码列表同样未含,无回归。
|
||||
|
||||
### 4. 四处脚本改写语义等价 — 通过(含写路径守卫)
|
||||
|
||||
探针(/tmp,同输入同状态对比,全部 SAME):
|
||||
|
||||
| 改写 | 状态覆盖 | 结果 |
|
||||
|---|---|---|
|
||||
| `ps … \| grep -Fq` → `grep -Fq < <(ps …)`(e2e-duties.sh:87 cleanup) | 活进程匹配(exec -a 伪造 argv)→ 双方 matched-kill;活进程不匹配 → no/no;死 PID → no/no | 三态等价 |
|
||||
| `curl … \| head -c 160` → `head -c 160 < <(curl …)`(e2e-duties.sh:128 log) | 1000B 取前 160B(165 字符含 `\|rc=` 完全一致)、短体、生产者失联 → 三态输出与 rc 等价(此处替换状态本就不被消费) | 等价 |
|
||||
| `echo "$branch" \| grep -qE` → `grep -qE <<<"$branch"`(pre-commit:9) | 13 组分支名(4 合法 + feat/x、feature/、feature//x、含空格、空串、大写、`fix/-n pwned`、换行名)退出码逐一相同 | 写路径守卫未削弱:合法分支照常 admit,非法照旧拒绝且文案不变;`git rev-parse --abbrev-ref` 的 detached="HEAD" 两侧同拒;here-string 尾随换行与 echo 一致 |
|
||||
| `postgres --version \| awk …exit` → `awk … < <(postgres --version) \|\| true`(svc.sh:4360 pgver) | 版本正常(24.1/24.1)、二进制失败(空/空)、空输出(空/空)、二进制缺失(空/空) | 捕获值与替换状态恒 0 两侧一致(`\|\| true` 保留) |
|
||||
|
||||
三脚本 `bash -n` 通过、均 `#!/usr/bin/env bash`(`<()`/`<<<` 合法);命令替换状态来源变化(head/awk 而非管道)在两处分别「不被消费」与「被 `\|\| true` 归零」,无行为差异。
|
||||
|
||||
### 5. 契约表述与实现一致 — 通过(无夸大承诺)
|
||||
|
||||
- docstring 明示「守护的承诺是『已声明的各族在发现到的脚本中处处被拦截』,而非『理解任意 bash 命令』」——承诺范围诚实收窄。
|
||||
- `READER_FAMILIES` 7 条描述与实现逐条吻合:grep -q/-m(含 egrep/fgrep、长选项与 `=`)↔ `_grep_exits_early`;head 任意 ↔ 短路 True;sed q/Q ↔ `SED_QUIT`;awk END 外 exit/nextfile(含 gawk/mawk/nawk 别名)↔ `_awk_exits_early`;read 任意 ↔ True;mapfile `-n/--n/--numlines`(含 readarray)↔ `_mapfile_exits_early`;while「循环体 break 出本循环(含 until)」↔ `_lex` loops + `break_out`(brace 透明、子壳 `)` 正确截断、break N 层级计数)。
|
||||
- 测试名与内容相符;「…flagged_and_harmful」用例真实执行 bash 并断言 141,非纸面断言。
|
||||
- 保留意见:while/until 族触发词限定为「break」是**如实**描述实现,但该族的其它停止写法(条件终止、体中 exit)落在声明触发词之外而命令族之内——正是本轮第 (2) 条要求排查的形态(见阻塞项)。
|
||||
|
||||
---
|
||||
|
||||
## 阻塞项
|
||||
|
||||
### B1 检测器漏检:`until` 循环条件 read 首行成功即终止(无 break)
|
||||
|
||||
- 形态:`seq 1 200000 | until read -r l; do :; done`。`until` 条件为 read:首行读取成功 → 循环即结束 → 读取级子壳退出、stdin 关闭。语义等价 `head -1` 的纯 bash 写法,属已声明 while/until 命令族的另一种停止写法(触发机制是条件成功而非 break)。
|
||||
- 运行时:`bash -c 'set -o pipefail; seq 1 200000 | until read -r l; do :; done'` → **141**(/tmp 实测)。
|
||||
- 检测器实测:`hazards(...)` → `[]`。`early_exit_reader`(:392)对 while/until 仅查 `_lex(s)[1]` 的 `broken`(break 置位),`break_out`(:113)只认 `break` 命令。
|
||||
- 影响:`until read`/`until IFS= read -t …` 形态可使缺陷类静默回流;当前 17 个受管脚本无此写法(`git grep "until "` 仅命中 ImageMagick `-until` 参数),属未来回归面。
|
||||
|
||||
### B2 检测器漏检:`while`/`until` 循环体内 `exit` 终止读取(无 break)
|
||||
|
||||
- 形态:`seq 1 200000 | while read -r l; do exit; done` 与惯用条件形 `… do [ "$l" = 7 ] && exit; done`。「读到目标行即退出」是常见习语;管道末级 while 在子壳中运行,exit 直接终止子壳、关闭 stdin。
|
||||
- 运行时:两形态 `set -o pipefail` 下均 **141**(/tmp 实测)。
|
||||
- 检测器实测:两形态均 `[]`——同 B1,`break_out` 不识别 `exit`。
|
||||
- 影响:同族「读满即停」的第二个入口,词法可静态识别(循环体内命令位的 `exit`),未覆盖即构成本轮可证伪条款下的阻塞。
|
||||
- 修复方向(供实施者参考,本轮未动代码):`break_out` 对循环帧内命令位的 `exit`/`exec <cmd>` 置 `broken`;`until` 循环在条件含声明族读者(read/head/…)时视为早退(`while` 条件读者则读满,安全,现判定已正确)。是否扩展触发词到 `return`(函数内管道级不可见,无意义)由实施者裁量。
|
||||
|
||||
两阻塞项均为「同一命令族(while/until)、运行时确认真实致害(141)、检测器不报」,满足本轮约束「如报不出,作为阻塞项」;亦满足首轮确立的「漏检使缺陷类可回流 = 阻塞」判据。合并修复面小(`break_out` + until 条件判定各一处),不影响其余已闭合结论。
|
||||
|
||||
## 非阻塞建议
|
||||
|
||||
- **N1(沿首轮)**:`seq 1 200000 | awk '{print "exit"}'` 被误报——`AWK_EXIT` 对程序文本裸 `\bexit\b`,引号串未剥离。安全方向;当前受管脚本无触发。建议 `_awk_exits_early` 剥引号串后再查。
|
||||
- **N2(沿首轮)**:发现缺口——`scripts/agent-exec`、`scripts/dm`、`scripts/task`(无扩展名 bash)与 `docker/entrypoint.sh` 不被 `:(glob)scripts/**/*.sh`/`:(glob)scripts/hooks/*`/EXTRA 命中。建议增补 `:(glob)scripts/*` 与 `:(glob)docker/*.sh`。
|
||||
- **N3(沿首轮)**:算术 `$((0|read))` 的 `\|` 被当管道拆级,操作数恰为 `read`/`head` 时误报。极牵强、安全方向,可忽略。
|
||||
- **N4**:`mapfile arr -n 1`(选项在操作数后)不报——bash 内建 getopt 停于首操作数,`-n` 不生效,实测 rc=0 读满输入,惰性无害;可加注释说明词序豁免理由。
|
||||
- **N5**:包裹前缀白名单 `PREFIXES`(:45)未含 `nohup`/`timeout`/`nice`/`stdbuf` 等 exec 型包装命令;`seq 1 200000 | timeout 5 head -1`、`| nohup head -1` 实测 141 且不报。属已声明「前缀」机制的可扩展清单之外(声明为「env/exec 等前缀」枚举),按可扩展契约记观察,不阻塞。
|
||||
- **N6(按可证伪边界记录)**:族外读者 `dd`(`| dd bs=1 count=1` 实测 141)、`jq -n`、`perl -ne 'exit if …'`、`xargs -n1`、`select` 循环(`| select x in a; do break; done` 实测 141)均不报——非 READER_FAMILIES 声明族,依本轮明确边界只记非阻塞观察。
|
||||
- **N7**:`sed -f prog.sed`/`awk -f prog.awk` 的程序文本在外部文件,词法不可见;与函数间接调用同级,契约「理解任意 bash 命令」之外,记观察。
|
||||
|
||||
## 判定标准对照
|
||||
|
||||
- 检测器漏检使该缺陷类可回流:**成立(B1/B2,运行时确认 141)→ 阻塞**。
|
||||
- 七族自检 + 族外不误报:成立(第 1 节)。
|
||||
- 发现机制稳定、无解析错误、ignored 陈旧克隆不入扫描:成立(第 3 节)。
|
||||
- 改写引入语义回归:未发现(4/4 等价,含写路径守卫 13 组分支名探针)。
|
||||
- 写守卫被削弱:未发现。
|
||||
- 契约夸大承诺:未发现(承诺已按「已声明族 + 已发现脚本」收窄);while/until 触发词如实标注 break,B1/B2 按同族停止写法计阻塞而非虚言。
|
||||
- 族外新命令族/构造:仅记 N5/N6/N7 非阻塞观察(边界条款)。
|
||||
|
||||
## 已运行的验证命令与结果(均只读;合成物全部位于 /tmp)
|
||||
|
||||
| # | 命令 | 结果 |
|
||||
|---|---|---|
|
||||
| 1 | `git rev-parse HEAD fix/guard-test-blindspot-coverage` | 二者同为 6ddbae089f61…;范围 `7a5c80e..HEAD` 即分支全量(diff --stat 6 文件) |
|
||||
| 2 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_shell_pipe_hazard -v` | **Ran 11 tests, OK** |
|
||||
| 3 | `git ls-files -z -- ':(glob)scripts/**/*.sh' ':(glob)scripts/hooks/*' docker/verify-env.sh tests/load/collaboration/run_pgbench.sh` | 17 文件(含 3 个 hooks 无扩展名脚本,无 runtime/) |
|
||||
| 4 | `head -1` + `bash -n`:scripts/e2e-duties.sh、scripts/hooks/pre-commit、scripts/svc.sh | 均 `#!/usr/bin/env bash`,bash -n 全过 |
|
||||
| 5 | /tmp 探针:检测器注入形态矩阵(grep 词序/参数选项、until-read、while-exit、组嵌套管道、mapfile 词序、sed qN、前缀包装、族外读者、$((算术\|))、awk 引号串) | 报:grep 全词序形态、组嵌套管道 `(cat\|head -1)`/`{ cat\|head -1; }`/`( (head -1) )`、`while :; do read && break; done`、`sed 'q…'`;**不报:until-read-nobreak、while+exit(B1/B2)**、`(break)`(正确,实测 0)、mapfile 词序(实测 0)、族外读者、nohup/timeout 前缀;误报:awk 'exit' 串、`$((0\|read))` |
|
||||
| 6 | /tmp pipefail 运行时基线(`bash -c 'set -o pipefail; …'`) | until-read、while+exit(无条件/条件)、`(cat\|head -1)`、`{ cat\|head -1; }`、`( (head -1) )`、`{ break; }`、`read && break`、`grep 1 -q`、`dd count=1`、`select …break`、`timeout 5 head -1`、`nohup head -1` → **141**;`while :`/`(break)`/`read \|\| break`/`mapfile arr -n 1`/`mapfile arr` → 0 |
|
||||
| 7 | /tmp 等价性探针(pre-commit 13 分支名、cleanup 3 态含真实匹配 PID kill、head -c160 3 态、pgver 4 态) | 全部 SAME |
|
||||
| 8 | 模块化扫描:`_lex`+`hazards` 遍历 17 个受管脚本 | errors=0,found=[] |
|
||||
| 9 | `git grep "until "`、`\| while` 于受管脚本 | 无 until 管道读者;7 处 `\| while` 循环体均读满输入(printf/continue),无 exit/break 于读取级 |
|
||||
| 10 | `grep -n runtime .gitignore`、`git check-ignore` 语义复核 | `.gitignore:9 runtime/`(:10 runtime-backups/),ignored 克隆不可能入 `git ls-files` |
|
||||
| 11 | `sed 'q2'`/`sed '5q2'`/`sed '5q 2'` 行为定性 | `q2` 即退出码 2 立即退出(检测器 `[qQ][0-9]*` 命中);空格形惰性(rc=0),不构成漏检危害 |
|
||||
|
||||
## 可证伪性注入记录(约束第 4 条)
|
||||
|
||||
本轮人为注入 5 组此前套件未覆盖的族内新写法:`grep 1 -q`(词序倒置)→ 报 ✓;`grep -e 1 -q`(参数选项后置)→ 报 ✓;`(cat | head -1)`(组内嵌套管道)→ 报 ✓;`until read -r l; do :; done` → **不报 ✗**;`while read -r l; do exit; done` → **不报 ✗**。检测器对注入并非全盘照收,失败集中于 while/until 停止写法一处机制(`break_out` 仅认 `break`),修复面集中。按约束,不报者计阻塞项。
|
||||
|
||||
## 未验证事项
|
||||
|
||||
- 本报告全部结论基于本地只读命令与 /tmp 合成探针;未运行 CI、未做 REPLAY(审查单元不要求)。
|
||||
- N5 中 `nice`/`stdbuf` 未逐个运行时实测(nohup/timeout 已实测 141),归类依同一 exec 包装机制推断。
|
||||
@@ -0,0 +1,124 @@
|
||||
# 独立代码审查:fix/guard-test-blindspot-coverage
|
||||
|
||||
- 审查对象:`7a5c80eacb835bfc6e62b3557129ec299bbb5290..8803bea966283996e4287a6f3b883d42f29b8e0b`(分支 `fix/guard-test-blindspot-coverage`,HEAD=8803bea)
|
||||
- 涉及文件:`tests/test_shell_pipe_hazard.py`、`scripts/e2e-duties.sh`、`scripts/hooks/pre-commit`、`scripts/svc.sh`(另有任务卡 `.task/fix-guard-test-blindspot-coverage.md`,属 host 豁免路径)
|
||||
- 审查身份:全新独立会话,未参与实现;只读审查,仅本报告文件为新增写产物
|
||||
- 审查日期:2026-10-20
|
||||
|
||||
## 结论:BLOCKED
|
||||
|
||||
变更整体是一次严格的覆盖面改进(4 处脚本改写全部语义等价、发现机制消除了硬编码、主流漏检形态已闭合、自检可证伪),但按本单元判定标准「检测器漏检使该缺陷类可回流 = 阻塞」,实测构造出 3 个**此前未被覆盖、运行时确认真实致害(pipefail 下 141)、检测器却不报**的早退读取器形态,其中 `awk nextfile` 属背景中点名的 awk 早退家族成员。故判 BLOCKED;不推翻已完成的等价性与发现机制结论。
|
||||
|
||||
---
|
||||
|
||||
## 阻塞项
|
||||
|
||||
### B1 检测器漏检:`| (head -1)` —— 子壳包裹的早退读取器不被报
|
||||
|
||||
- 形态:`seq 1 200000 | (head -1)`。`(` 走 `push_cmd(c)` 入帧(tests/test_shell_pipe_hazard.py:157),帧内 `head -1` 成为**单级管道**,而 `hazards()` 只检查 `stages[1:]`(tests/test_shell_pipe_hazard.py:289-298),故永不命中。
|
||||
- 运行时确认真实致害:`bash -c 'set -o pipefail; seq 1 200000 | (head -1) >/dev/null'` → exit **141**(/tmp/groundtruth.sh 实测)。
|
||||
- 检测器实测:`hazards('seq 1 200000 | (head -1)\n')` → `[]`(/tmp/hazard_matrix.py,`subshell-reader` 行:old=miss, new=miss)。
|
||||
- 影响:head/read/grep -q 全家族均可被一层子壳静默绕过,缺陷类可回流。
|
||||
|
||||
### B2 检测器漏检:`awk … nextfile` —— awk 家族成员不被报
|
||||
|
||||
- 形态:`seq 1 200000 | awk '$1==7{print; nextfile}'`。`_awk_exits_early` 只识别 `exit`(tests/test_shell_pipe_hazard.py:257-267),`nextfile` 同样会在读完输入前停止读取、关闭 stdin。
|
||||
- 运行时确认:`bash -c 'set -o pipefail; seq 1 200000 | awk "\$1==7{print; nextfile}" >/dev/null'` → exit **141**。
|
||||
- 检测器实测:`hazards(...)` → `[]`(矩阵行 `awk-nextfile`:old=miss, new=miss)。
|
||||
- 影响:背景(PR #664 遗留 a)明确点名 `awk … exit` 为盲区;`nextfile` 是同一 awk 早退家族的常用习语("取首条匹配即停"),修复后仍可无声回流。按判定标准构成阻塞。
|
||||
|
||||
### B3 检测器漏检:`| while read … break` —— 安全清单放行了带 break 的危险变体
|
||||
|
||||
- 形态:`seq 1 200000 | while read -r l; do break; done`。`early_exit_reader` 首词为 `while`,直接返回 False(tests/test_shell_pipe_hazard.py:270-288 无 while 分支)。
|
||||
- 运行时确认:`bash -c 'set -o pipefail; seq 1 200000 | while read -r l; do break; done'` → exit **141**。
|
||||
- 加重因素:新测试把 `seq 1 200000 | while read -r l; do :; done` 列入安全形态(tests/test_shell_pipe_hazard.py:347),未限定"循环体不含 break/exit",等于把危险变体也放行——违反"安全形态清单是否过宽以致把危险写法也放行"的关注点。
|
||||
- 附带同类(次要、非独立阻塞):`| /usr/bin/grep -q 1`(绝对路径前缀)与 `| exec grep -q 1` 因首词不在前缀剥离清单(tests/test_shell_pipe_hazard.py:273)而漏检,实测 141;词法检测器对动态派发有固有局限,但上述三种均为静态可识别形态。
|
||||
|
||||
---
|
||||
|
||||
## 已验证通过项(非阻塞)
|
||||
|
||||
### 1. 检测正确性(主流形态闭合)
|
||||
|
||||
新词法扫描(tests/test_shell_pipe_hazard.py:44-253 `pipelines`)实测判定可靠的形态:
|
||||
|
||||
| 形态 | 期望 | 实测 |
|
||||
|---|---|---|
|
||||
| heredoc 正文内的管道 | 不报 | 不报(正文按 `<<` 定界符跳过;`heredoc-body-hazard`、`heredoc-unterminated` 均 miss,属正确) |
|
||||
| heredoc 之后的真实管道 | 报 | 报(`heredoc-then-hazard` → [(4,'grep -q 1')]) |
|
||||
| case 模式中的 `\|` | 不报 | 不报(`case-pattern-pipe` miss,模式态由 cases 栈维护) |
|
||||
| case 体内管道读取器 | 报 | 报(`case-body-pipe` → FLAG) |
|
||||
| 续行后的管道 | 报 | 报(`cont-line-pipe` → [(2,'grep -q 1')]) |
|
||||
| `$(...)` 嵌套管道 | 报 | 报(`dq-nested-pipe` → [(1,'head -1')]) |
|
||||
| 反引号 | 报 | 报(`backtick-head` → [(1,'head -1`')],靠词法拆级碰巧覆盖) |
|
||||
| `$'...'` ANSI 引用 | 正常 | 正常(`ansi-quote` 内 `$(…\|head -1)` 仍报) |
|
||||
| 花括组读取器 `seq \| { head -1; }` | 报 | 报(`{` 在前缀剥离清单,tests/test_shell_pipe_hazard.py:273) |
|
||||
| 前缀化读取器 `LC_ALL=C grep -q`、`env … grep -m1` | 报 | 报(单测 tests/test_shell_pipe_hazard.py:324-325) |
|
||||
|
||||
对当前安全写法无误报(全 17 个受管脚本套件通过;矩阵误报候选仅 1,见 N1)。
|
||||
|
||||
### 2. 发现机制(相对旧硬编码严格变宽)
|
||||
|
||||
- `managed_scripts()` 经 `git ls-files -z`(tests/test_shell_pipe_hazard.py:28-33),实测发现 17 个文件,覆盖旧列表全部 6 个 + `scripts/check-pins.sh`、`scripts/evidence.sh`、`scripts/facts-verify.sh`、`scripts/fetch-rdm-pin.sh`、`scripts/hooks/{commit-msg,post-merge,pre-commit}`、`scripts/lint-architecture.sh`、`scripts/plugin-new.sh`、`scripts/zcode/write-guard.sh`。将来新增 `*.sh`/hooks 文件自动纳入,无需改测试。
|
||||
- `scripts/hooks/*` 被 `:(glob)scripts/hooks/*` 覆盖(含无扩展名脚本);`scripts/**/*.sh` 覆盖 `scripts/zcode/write-guard.sh` 与 `scripts/svc.sh`(零层目录也命中)。
|
||||
- runtime/ 陈旧克隆不会误扫:`git ls-files` 只列跟踪文件,`runtime/` 在 `.gitignore:9`(`runtime/`),测试断言 `p.startswith("runtime/")` 为空(tests/test_shell_pipe_hazard.py:366),实测通过。
|
||||
- `EXEMPT = {}`(tests/test_shell_pipe_hazard.py:17)恰当:无静默豁免文件;过期条目守卫(tests/test_shell_pipe_hazard.py:367-368)防止残留。
|
||||
- 遗留缺口见 N2(非阻塞)。
|
||||
|
||||
### 3. 自检有效性
|
||||
|
||||
- 新用例断言精确 `(行号, 级文本)`(tests/test_shell_pipe_hazard.py:317-331),词法任何漂移即失败,可证伪。新旧对照实测证明新用例针对真实盲区:`seq 1 200000 | { head -1; }`、`x=\`…\| head -1\``、管道中段 `head`、`awk exit`、`read` 等形态 old=miss → new=FLAG(/tmp/hazard_matrix.py)。
|
||||
- 全套件实测通过:`PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_shell_pipe_hazard -v` → Ran 6 tests, OK。
|
||||
- 反向注入验证(本审查构造):见阻塞项 B1/B2/B3——检测器对这三种新构造形态确实不报(这正是判 BLOCKED 的依据)。
|
||||
|
||||
### 4. 脚本改写语义等价(4/4 通过)
|
||||
|
||||
以 /tmp/equiv.sh 对旧/新写法做同输入探针(输出+退出码):
|
||||
|
||||
| 改写 | 位置 | 探针结果 |
|
||||
|---|---|---|
|
||||
| `ps … \| grep -Fq` → `grep -Fq < <(ps …)` | scripts/e2e-duties.sh:87 | 活 PID/死 PID/匹配 PID 三态新旧一致(no/no、yes/yes);`<()` 状态不入判定,`-F -q --` 参数不变 |
|
||||
| `curl … \| head -c 160` → `head -c 160 < <(curl …)` | scripts/e2e-duties.sh:128 | 短体(abc)、长体(1000B 取前 160B)、生产者失败三态新旧一致(含 `st=0`,替代捕获状态取 head 而非管道,此处状态本就未被消费) |
|
||||
| `echo "$branch" \| grep -qE` → `grep -qE <<<"$branch"` | scripts/hooks/pre-commit:9 | 9 组分支名(合法/非法/空/含空格/`-n pwned`/含引号)退出码逐一相同;here-string 尾随换行与 echo 一致;拒绝文案与 admit 后续执行不变 |
|
||||
| `"$pgbin/postgres" --version \| awk … exit \|\| true` → `awk … < <(postgres --version) \|\| true` | scripts/svc.sh:4360 | 正常版本(24.1)、二进制缺失(空)、非零退出(空)三态新旧捕获值一致;`pgver` 的 `\|\| true` 保留使替换状态恒 0,与旧 `管道 \|\| true` 等价;awk 提前退出改由 `<()` 吸收 SIGPIPE,不影响替换状态 |
|
||||
|
||||
三个被改脚本 `bash -n` 全部通过;均为 `#!/usr/bin/env bash`,`<()`/`<<<` 合法。
|
||||
|
||||
### 5. 范围与写守卫
|
||||
|
||||
- 变更范围:`git diff --stat` 仅 4 个目标文件 + 任务卡(`.task/**` 为 host 豁免路径),无越界。
|
||||
- `scripts/hooks/pre-commit` 属写路径守卫:改动仅把 `echo | grep` 换成等价 here-string(探针见上表),正则、拒绝分支、后续 `admit` 调用原样(scripts/hooks/pre-commit:16);shebang 为 bash,here-string 不引入 sh 兼容风险;守卫未被削弱。
|
||||
|
||||
---
|
||||
|
||||
## 非阻塞建议
|
||||
|
||||
- **N1 误报**:`seq 1 200000 | awk '{print "exit"}'` 被误报(`AWK_EXIT` 对 awk 程序文本做裸 `\bexit\b`,tests/test_shell_pipe_hazard.py:259,267),运行时实测 exit=0 安全。当前 17 个受管脚本无触发,仅造成未来开发摩擦(偏安全方向);建议 `_awk_exits_early` 像剥 END 块一样剥离引号串后再查 `exit`。
|
||||
- **N2 发现缺口(盲区 b 的残留)**:`scripts/` 下既有无扩展名 bash 脚本 `scripts/agent-exec`、`scripts/dm`、`scripts/task`(均 `#!/usr/bin/env bash`)不被 `:(glob)scripts/**/*.sh` 或 `:(glob)scripts/hooks/*` 命中;`docker/entrypoint.sh`(bash,`set -euo pipefail`)亦不在 EXTRA。旧硬编码列表同样未含它们,无回归、人工抽查未见多级管道读取器,但"新增受管脚本不被扫描"对这一命名惯例仍成立。建议增补 `:(glob)scripts/*`(及按需 `:(glob)docker/*.sh`);另 `:(glob)scripts/hooks/*` 不含 hooks 子目录。
|
||||
- **N3 词法小疵**:`$((a|b))` 算术中的 `|` 会被当作管道拆级(操作数非命令词故暂无误报;若变量名为 `head`/`read` 等且参与按位或可致误报)。可忽略。
|
||||
- **N4**:安全清单中 `while read` 条目建议加注释/用例说明"仅限循环体无 break/exit",与 B3 修复联动。
|
||||
|
||||
---
|
||||
|
||||
## 已运行的验证命令与结果(均只读;合成脚本全部位于 /tmp,仓库内无残留)
|
||||
|
||||
| # | 命令 | 结果 |
|
||||
|---|---|---|
|
||||
| 1 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_shell_pipe_hazard -v` | Ran 6 tests, OK |
|
||||
| 2 | `git diff --stat 7a5c80e..8803bea` | 5 文件(4 目标 + 任务卡),+198/−33 |
|
||||
| 3 | `git ls-files -z -- ':(glob)scripts/**/*.sh' ':(glob)scripts/hooks/*' …` | 17 文件(含 3 个 hooks 无扩展名脚本,无 runtime/) |
|
||||
| 4 | 新旧检测器对照 `/tmp/hazard_matrix.py`(import 仓库模块与 `git show 7a5c80e:tests/…` 提取的旧模块,26 形态) | 新检 12 形态 FLAG;漏检 4(B1/B2/B3/绝对路径 grep);误报 1(N1);安全形态全部正确放行 |
|
||||
| 5 | 运行时基线 `/tmp/groundtruth.sh`(bash pipefail) | `(head -1)`、`awk nextfile`、`while break`、`/usr/bin/grep -q` → **141**;`mapfile`/`sed -n 1p`/`xargs -n1`/`awk '{print "x"}'` → 0 |
|
||||
| 6 | 等价性探针 `/tmp/equiv.sh`(pre-commit 9 分支名、cleanup 3 态、head -c160 4 态、pgver 4 态) | 全部新旧一致(1 行 DIFF 为探针自身期望值笔误,/bin/postgres 本机不存在,新旧同为空) |
|
||||
| 7 | `bash -n scripts/{e2e-duties,svc}.sh scripts/hooks/pre-commit` | 全部通过 |
|
||||
| 8 | 受管脚本 `nextfile`/`\| (`/`\| while`/`\| mapfile` 抽查(`git grep`) | 无 nextfile、无子壳读取器;6 处 `| while read` 循环体仅 `continue`(安全),1 处 `exit 2` 为函数用法错误分支 |
|
||||
| 9 | `head -1` 各被改脚本 / `git grep` 无扩展名 scripts/ 脚本 | shebang 均 bash;`scripts/{agent-exec,dm,task}` 为 bash 脚本(N2 依据) |
|
||||
| 10 | `git check-ignore -v runtime`、`.gitignore` | `.gitignore:9 runtime/`(ignore 生效,陈旧克隆不入 ls-files) |
|
||||
|
||||
## 判定标准对照
|
||||
|
||||
- 检测器漏检使该缺陷类可回流:**成立(B1/B2/B3,运行时确认 141)→ 阻塞**。
|
||||
- 误放行危险写法:安全清单 `while read` 未限 break 变体(B3 加重因素)。
|
||||
- 改写引入语义回归:未发现(4/4 等价)。
|
||||
- 写守卫被削弱:未发现。
|
||||
- 风格与可维护性:N1–N4,非阻塞。
|
||||
@@ -0,0 +1,86 @@
|
||||
# 独立代码审查:fix/remaining-hardcoded-ports(剩余同类端口硬编码改为内核分配,与 PR #668 同形)
|
||||
|
||||
- 审查对象:`444fb1dee2..HEAD`(HEAD=70c810a)。本单元 diff = `.task/fix-remaining-hardcoded-ports.md`(host 豁免路径)+ `tests/_port_alloc.py`(新增 13 行)+ `tests/test_port_alloc.py`(新增 24 行)+ `tests/test_svc.py`(4 处用例、6 个 `svc --port` 调用点 + 批量更新 2 个 seed 端口)+ `tests/test_svc_realcombo.py`(3 个 realcombo 用例 + import)。产品侧 `scripts/svc.sh` 零改动(`git diff 444fb1dee2..HEAD --stat -- scripts/` 为空)。
|
||||
- 审查者身份:全新会话独立审查,未参与实现;本会话除本报告外不写任何仓库文件;无 git add/commit/push,无 PR 操作。工作区(detached HEAD 70c810a)在报告写入前后 `git status --porcelain` 仅有本报告。
|
||||
- 依据:PR #668 审查(.task/reviews/2026-10-20-svc-flake-review.md)N4 记录的同类脆弱面 =「rdm 组真实绑口的 18842/18843/18844 与 realcombo 9471/9473/9474」;本单元即对该类做同形治理(bind(("127.0.0.1", 0)) 内核分配)。
|
||||
|
||||
## 结论:PASS(无阻塞项;非阻塞建议 3 条)
|
||||
|
||||
判定标准逐项:契约用例不再依赖固定端口(实测全数转换,动态临时口 32768-60999 与全仓剩余固定口不相交);helper 与 PR #668 及仓内既有 5 处夹具(test_svc_recover.py:187 `free_fixture_port` 等)逐字节同构,非新模式;受保护内容(`scripts/svc.sh`、`register_pg`/DSN 18951、`test_svc_recover.py`、`test_task_pr_check.py`、`test_task_triage.py`)零改动;补丁未外溢(`git diff --name-only` 仅 5 个文件)。realcombo 三例因需真实二进制环境(SVC_TEST_REAL / SVC_REAL_RDM_BIN / SVC_RDM_ORIGIN,本单元禁网)为 NOT_RUN,以代码走读 + 本地只读模块跑通(16 skipped 门槛确认)替代。
|
||||
|
||||
---
|
||||
|
||||
## 重点 1:helper `free_test_port` 的设计
|
||||
|
||||
**写法**(tests/_port_alloc.py):每次调用新开 `AF_INET/SOCK_STREAM` 套接字 → `bind(("127.0.0.1", 0))` 由内核从临时口范围分配 → `getsockname()[1]` 取实际端口 → `with` 退出即 close(return 表达式在 with 内求值,关闭发生在返回前)。不池化、不 SO_REUSEPORT,与 `free_fixture_port`(test_svc_recover.py:187-190)及 PR #668 写法同构。docstring 如实声明「closes it before returning」「Ports are not pooled」。
|
||||
|
||||
**「真空闲」的三类场景实测**(合成测试 /tmp/port_alloc_synthetic.py,独立于守护测试,3 次运行全 7/7 PASS):
|
||||
|
||||
1. **外部占用**:先取 helper 返回端口 P1 并真 LISTEN 占住(SO_ACCEPTCONN=1 确认),再次调用 helper 与后续 50 次调用共 51 次,**0 次命中 P1**,且每个返回值都能立即 bind+listen 成功——内核 `bind(0)` 的冲突检测以套接字表为准,与占用者是本进程还是外部进程无关,故 in-process LISTEN 持有者可作外部占用的等价证据。被占端口本身确实可用(我们正是用它完成 LISTEN)。
|
||||
2. **TIME_WAIT**:制造真实 TIME_WAIT(服务端先关,端口 45387/53933/42733 三轮),先证明无 SO_REUSEADDR 的裸 rebind 报 EADDRINUSE(TIME_WAIT 确实阻断重绑),再测 helper 50 次调用 **0 次命中** TIME_WAIT 端口——内核分配阶段即跳过,helper 不会把调用方推进一个必然 bind 失败的端口。
|
||||
3. **多调用区分度**:连续 500 次无持有调用 → 497 个不同值(远处可重复 3 次),但**相邻两次重复 0/499**——内核临时口游标使相邻调用实际上总不同。守护测试的 `assertNotEqual(first, second)` 依赖的正是相邻性,实证可靠(另 3×50 连续调用亦全不同)。远处重复(如两次 alloc 间隔数百次调用后撞回同值)无害:前一次的 reservation 早已 close 且从未 LISTEN 过、无 TIME_WAIT、无持有者,调用方 bind 必成功。
|
||||
4. **子进程仍持套接字**:与场景 1 同表(内核冲突表不分进程),daemon 未退时 helper 不会返回其端口;helper 自身的 reservation 从未 accept/连接,close 后不留 TIME_WAIT。
|
||||
|
||||
**残余 TOCTOU 窗口(非阻塞,与 PR #668 同等接受)**:reservation close 到 daemon 真正 bind 之间端口不被保留。这是该模式(含仓内既有 5 处)的固有窗口,docstring 已如实声明。失效模式是响亮失败而非假绿:端口真被抢时 `check_port_free`(svc.sh:1575)报「端口被占用(非本服务)」非零退出 → `self.svc(check=True)` 断言带全量 stdout 失败,可归因(`port_owner`)。动态口 32768-60999(实测 `/proc/sys/net/ipv4/ip_local_port_range`)与仓内全部固定口(9431-9472、188xx-189xx)不相交,套件内不会互相踩。套件级彻底解法(传 fd/重试)属独立增量,不建议本任务扩围。
|
||||
|
||||
## 重点 2:三个 realcombo 用例端口一致性
|
||||
|
||||
逐例核验(tests/test_svc_realcombo.py):
|
||||
|
||||
- `test_U3_real_agent_identity_lifecycle_sqlite`:`:172 port = free_test_port()` 一次 → `seed_real("rc1", port)`(写 svc.env 的 addr 与 exec `--addr 127.0.0.1:port`)→ `svc start` → `http_ok(port)`(:186)→ 两次裸 restart(svc restart 沿用登记端口)→ `http_ok(port)`(:211)→ stop。三处同源同值。✓
|
||||
- `test_U3_real_rdm_binary_lifecycle_sqlite`:`:219` 一次 alloc → seed_real/start/http_ok(:226)全用 `port`。✓
|
||||
- `test_U3_real_rdm_full_deploy_track`:`:245 agent_port = free_test_port()` 一次 → `deploy --port str(agent_port)` → 裸 restart 后 `http_ok(agent_port)`(:269)。伴生 `rcfweb` 的端口不归本 alloc:svc.sh `deploy_rdm_companion_ui`(svc.sh:3141)以无 --port 子进程走 `derive_port`(8800+hash%300 冲突递增),测试从 `kv("rcfweb","addr")` 读回再探活(:263)——读写分离正确,alloc 只管 agent 口。✓
|
||||
|
||||
文件内 9471/9473/9474 零残留(grep 实测)。**alloc→bind 窗口在 realcombo 最宽**(seed_real 里的 `initialize_store` 会起停一个独立临时口的 fixture 服务,秒级),但该窗口语义与重点 1 相同:碰撞即响亮失败(start 门禁报占用),不会假绿;环境门禁(无真实二进制即 skip)意味着 CI 常规路径根本不执行,风险面进一步收窄。NOT_RUN 如实标注。
|
||||
|
||||
## 重点 3:test_svc.py 六个 `svc --port` 调用点
|
||||
|
||||
六个调用点(行号为 HEAD)全部经 `free_test_port()` 取值:`:904`(rdm e2e deploy)、`:931`(rdm 缺 web 失败 deploy)、`:944/:948/:954`(source-switch 三连)、`:970`(rdm update 轨道 deploy)。零 18841-18844 残留(grep 实测)。
|
||||
|
||||
**「同一测试多次 svc 调用端口应不同」的精确判定**:唯一的同测试多次调用是 `test_source_switch_requires_flag`,它 **有意** 共享一次 alloc(`:942 port = str(free_test_port())`,:944/:948/:954 三处同值)。这不违反意图:三个调用是对**同一实例 sw1** 的顺序 deploy → 被拒 deploy → 换源 redeploy;svc.sh deploy 流程「停旧 → 原子切换 → 启动」(agent_do_deploy,svc.sh:3211 起)在重绑前先停掉旧服务,`check_port_free`/`reclaim_own_port`(svc.sh:1575/1115)兜底本服务残留——端口不会「被自身 reuse 占满」,因为旧持有者在重绑前已被 svc.sh 自己停掉;被拒调用在 `require_allow_source_switch` 门禁就失败,从未 bind。该流程在改前用固定 18844 即已如此,本次仅换端口来源,实测通过(见证据 5)。每实例多端口场景(批量更新 a/b)则是每实例各 alloc 一次(`:673`),两次相邻调用必不同(重点 1.3)。判定:**无「每调用必须独立 alloc」的违例;共享 alloc 是对该用例语义的最小忠实转换。**
|
||||
|
||||
另有 `test_update_preserves_binding` 等用例经 update/restart 沿登记端口重启——属 svc.sh 持久化语义,端口来源已由 seed 处 alloc/保留,不在六调用点之列。
|
||||
|
||||
## 重点 4:守护测试 test_port_alloc 的覆盖度
|
||||
|
||||
**能否真的失败——能,两类回归可拦**:
|
||||
- 退化成固定/缓存端口(如误改为 `return 18844`):`assertNotEqual(first, second)` 即刻失败——合成证据 4 用假 helper 复现了该失败路径。
|
||||
- 返回不可绑端口(如未来改成返回他者持有口):循环内 `bind+listen` 直接抛 OSError → 用例 ERROR。
|
||||
- 相邻两次拿到同值(游标回绕):assertNotEqual 失败(实测概率 ≈0,0/499 相邻重复,且该回绕对调用方无害——见重点 1.3,但作为回归信号仍成立)。
|
||||
|
||||
**覆盖缺口(非阻塞)**:守护测试未覆盖「端口已被外部占用时 helper 避开它」与 TIME_WAIT 避让——本审查以 /tmp 合成测试补证(外部 LISTEN 持有者 51 次调用 0 命中;TIME_WAIT 端口 50 次 0 命中),结论为 helper 行为正确,仅测试面未固化。alloc→bind TOCTOU 窗口则任何确定性测试都无法覆盖(内核不为已 close 的 reservation 保留端口),与 PR #668 的接受一致。若后续想固化,可在 `test_two_calls_...` 前置一步「先占住第一次的返回值再取第二次」的断言,属独立增量。
|
||||
|
||||
## 重点 5:范围核对
|
||||
|
||||
- `git diff --name-only 444fb1dee2..HEAD` 实测仅 5 文件:`.task/fix-remaining-hardcoded-ports.md` + 4 个测试文件;`scripts/` 零差异(`git diff --stat -- scripts/` 空)。✓
|
||||
- `tests/test_svc_recover.py` 不动:正确——其已有同构 `free_fixture_port`(:187),全部用例早已内核分配。✓
|
||||
- `tests/test_task_pr_check.py` 不动:正确——`ThreadingHTTPServer(("127.0.0.1", 0))`(:90)与 `closed_port_url()`(:100-102)本就内核分配,无固定绑口。✓
|
||||
- `tests/test_task_triage.py` 不动:正确——夹具为内联 `bind(("127.0.0.1", 0))`(:230-232);文中的数字端口(如 30319)仅出现在 URL 脱敏字符串断言里,不 bind。✓
|
||||
- `register_pg`/DSN 18951 保留:正确——`register_pg`(test_svc.py:242 起)仅 `write_text` 写 svc.env 元数据,从不建/绑 socket,18951 不可能引发占口型偶发失败。实测 base `444fb1dee2` 的 753-754 两行与 HEAD 逐字一致(行号平移到 757-758,系文件头新增 4 行 import 所致;本单元 diff 中 18951 出现次数为 0)。✓
|
||||
|
||||
**残余同类面(非阻塞,均不在本单元契约内,仅记录供后续增量)**:test_svc.py 仍有会在 `svc start/update` 时真实绑口的固定 seed 端口(18809、18832、18834-18837、18952,共 7 用例);`test_svc_plugins.py` 的 `deploy --port 18981/18982`、`test_svc_identity.py` 9431-9435、`test_svc_u2.py`/`test_svc_user_binding.py`/`test_svc_rdm_admin.py`/`test_svc_lifecycle_isolation.py` 的 943x-947x seed 端口同属 N4 类。动态临时口与它们不相交,本单元的转换不与之交互、也不掩盖其问题。
|
||||
|
||||
---
|
||||
|
||||
## 已运行的验证命令与结果(均只读;工作区除本报告外零修改)
|
||||
|
||||
| # | 命令 | 结果 |
|
||||
|---|---|---|
|
||||
| 1 | `git diff --stat 444fb1dee2..HEAD` 与 `--name-only` | 5 文件(任务卡 + 4 测试文件);`-- scripts/` 为空 |
|
||||
| 2 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_port_alloc -v` | **OK**(Ran 1, 0.000s) |
|
||||
| 3 | `python3 /tmp/port_alloc_synthetic.py`(合成测试:占住 helper 返回口再调用、51 次避让、TIME_WAIT 制造与避让、假 helper 触发断言;3 次运行) | 3 次 **exit=0,7/7 PASS**(典型值:held=36633 recall=49051;50-calls distinct=49-50/50 且 0 次命中被占口) |
|
||||
| 4 | `python3 -c` 500 次连续调用统计 | 497/500 不同;**相邻重复 0/499** |
|
||||
| 5 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest`(5 个改造用例:rdm e2e、rdm 缺 web、source-switch 三连、rdm update 轨道、批量更新双实例) | **OK**(Ran 5, 8.557s) |
|
||||
| 6 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc_realcombo -v` | OK(skipped=16:SVC_TEST_REAL/SVC_REAL_RDM_BIN/SVC_RDM_ORIGIN 未设 → realcombo 三例 NOT_RUN,禁网无法取得真实二进制) |
|
||||
| 7 | `grep` 残留扫描(9471/9473/9474/18841-18844)与 `free_test_port` 调用点清单 | 两文件零残留;调用点 = test_svc.py:673/904/931/942/970 + realcombo:172/219/245 |
|
||||
| 8 | `git show 444fb1dee2:tests/test_svc.py` 753-754 行 vs HEAD 757-758;本单元 diff 中 `18951` 计数 | 内容逐字一致;diff 计数 0 |
|
||||
| 9 | `cat /proc/sys/net/ipv4/ip_local_port_range` | 32768 60999(与全部固定测试口不相交) |
|
||||
| 10 | `git status --porcelain` / `git stash list`(测试运行前后) | 空(测试全在 /tmp 临时目录内自包含) |
|
||||
|
||||
## 判定标准对照
|
||||
|
||||
- 契约用例仍会因端口占用偶发失败:未发生(6 个 --port 调用点 + 批量更新 2 seed + realcombo 3 例全数转内核分配;动态口与仓内固定口不相交;占口失效模式为响亮失败)→ 非阻塞。
|
||||
- helper 在外部占用/TIME_WAIT 下取错端口:未发生(合成实测 0 命中)→ 非阻塞。
|
||||
- 受保护内容被改(svc.sh、register_pg 18951、recover/pr_check/triage 三文件):未发生(零 diff 实测)→ 非阻塞。
|
||||
- 补丁外溢到契约外文件:未发生(5 文件实测,其一为 host 豁免的任务卡)→ 非阻塞。
|
||||
- 结论:**PASS**。realcombo 三例运行级验证 NOT_RUN(环境门禁 + 禁网),以代码走读与模块级 skip 确认替代,已如实标注。
|
||||
@@ -0,0 +1,73 @@
|
||||
# 独立代码审查:fix/svc-static-deploy-test-port-flake(static 首启用例硬编码端口致偶发失败)
|
||||
|
||||
- 审查对象:packet 指定为 `5c3a1d4eb8f3a5c0c0c5be5cd5`(implementation 的 transport commit `d0b217d3a486d32db91d69539c39d07ed9dbdae0`)相对同步后 base `2fd11effd9ccb0ae35ae5a47caccefccbbb05a8e` 的 diff。**对象勘误(事实陈述,见 N1)**:`5c3a1d4e…` 在本工作区不存在(`git cat-file -t` → fatal);transport commit `d0b217d3` 的父提交是任务卡提交 `409f4749`,其分支自 R0=`a9566a68`(01:58 的 main)切出,而 `2fd11ef` 是 09:30 的 main——两者互不为祖先(`git merge-base --is-ancestor` 双向否定,merge-base=`a9566a68`)。因此 `git diff 2fd11ef d0b217d3` 含约 2400 行与本单元无关的主线演进内容(tests/test_shell_pipe_hazard.py 重写、e2e-duties.sh、hooks/pre-commit、svc.sh:4360、modules/backend gitlink、删除一份 spec 文档);**本单元自身 diff(`git diff a9566a68 d0b217d3` = `git diff 409f4749 d0b217d3`)只有**:`.task/fix-svc-static-deploy-test-port-flake.md`(host 豁免路径)+ `tests/test_svc.py` 单 hunk(3 增 1 删,第 989 行 `port = 18951` → `with socket.socket() as reservation: bind(("127.0.0.1", 0)); port = reservation.getsockname()[1]`)。产品侧 `scripts/svc.sh` 本单元零改动(d0b217d3 与当前 HEAD 2841cb4 在 svc.sh 的 1 行差异是主线管道治理提交所致,位于 `db_create` 的 postgres 路径,本用例不执行)。
|
||||
- 审查者身份:全新会话独立审查,未参与实现;本会话仅写入本报告,其余只读。
|
||||
- 被测代码:当前 HEAD `2841cb4` 下 `tests/test_svc.py` 与 `d0b217d3` 版本逐字节一致(`git diff d0b217d3 2841cb4 -- tests/test_svc.py` 为空),故只读测试在被测 HEAD 上运行即覆盖 transport commit 的测试代码。
|
||||
|
||||
## 结论:PASS(无阻塞项;4 项非阻塞建议)
|
||||
|
||||
判定标准逐项核对:用例语义未被削弱(9 条断言逐条与 base 相同,仅端口来源一行变化);TOCTOU 未引入可静默通过的偶发失败(失效模式是 `check_port_free` 报错退出 → `self.svc(check=True)` 断言带全量 stdout 响亮失败);受保护内容(`scripts/svc.sh`、`tests/test_svc.py:753-754` register_pg 夹具、测试类结构)未被改;补丁未外溢到其他用例或脚本(单文件单 hunk,实测 `git diff --stat a9566a68..d0b217d3` 仅 2 文件且其一为 `.task/**`)。
|
||||
|
||||
---
|
||||
|
||||
## 重点 1:端口分配语义与 TOCTOU 窗口
|
||||
|
||||
**写法**:`socket.socket()`(默认 AF_INET/SOCK_STREAM)→ `bind(("127.0.0.1", 0))` 由内核在临时口范围内分配 → `getsockname()[1]` 取实际端口 → `with` 块退出即 close。与本仓 5 处既有夹具逐字节同构:`tests/test_svc.py:366-368`(同类同类)、`tests/test_svc_recover.py:187-190`、`tests/test_task_pr_check.py:100-102`、`tests/test_task_triage.py:230-232`、`tests/test_svc_realcombo.py:128-130`(另 `tests/test_ci_wait.py:76`)。属既有惯例,非新引入模式。
|
||||
|
||||
**TOCTOU 窗口评估**(分析 + 局部实测):reservation close 到 `svc.sh` 内 `python3 -m http.server` 真正 bind 之间存在窗口,内核不为已关闭的监听套接字保留端口。实践中风险可忽略:
|
||||
- 本机临时口范围 `32768-60999`(`/proc/sys/net/ipv4/ip_local_port_range` 实测),与套件所有固定端口(188xx/189xx/947x)**不相交**,动态端口不可能撞上兄弟用例;碰撞只可能来自无关进程在同一瞬间抽中同一临时口。
|
||||
- 「服务主动保留端口」环境(`ip_local_reserved_ports`):内核 `bind(0)` 分配本就跳过保留段,分配步不会拿到保留口,不构成偶发源【分析推断,未单独实测】。
|
||||
- 「worker 池已尽/临时口耗尽」:`bind(0)` 自身抛错 → 用例报错,属环境问题的响亮失败,不会假绿【代码路径推断,未单独复现】。
|
||||
- **决定性实测(run 2)**:外部监听器占住旧硬编码口 18951 期间,目标用例 PASS(0.518s)——修复前该场景确定性失败(任务卡 host 复现 `AssertionError: 1 != 0`)。
|
||||
|
||||
**外部进程(如 Node)占口后的可观察性**:若抽中的端口在 `check_port_free`(scripts/svc.sh:1575)时已被占,static 部署报「端口被占用(非本服务):…」并退出非零 → `self.svc(check=True)` 的 `assertEqual(returncode, 0, stdout)` 失败并打印全量 stdout,占用者 pid 可查(`port_owner`)。比修复前更可归因:从「神秘硬编码口撞车」变为「带占用者信息的明确冲突」。
|
||||
|
||||
## 重点 2:用例失败可观察性是否保留
|
||||
|
||||
对比 base(a9566a68)与 d0b217d3 的用例方法体:`self.assert*` 计数 9=9,逐条未删未弱化(assertIn「已启动」、assertNotIn「进程即退」、assertEqual kind=static、assertIsNotNone pidcheck、assertIn `--directory`、assertIn resolve 后 htdocs、assertTrue pid_alive、assertEqual 200、assertIn static-ok)。仍然可见的失败类:(a) 端口冲突 → 部署退出非零 + stdout 全文;(b) pidcheck 登记回归 → assertIsNotNone/assertIn;(c) 首启探活误判 → assertNotIn「进程即退」;(d) http.server 未起/内容不符 → 部署失败或 urlopen 断言失败。唯一语义变化是「端口被他者占用时不再假失败」——这正是修复目标,不是可观察性损失。
|
||||
|
||||
## 重点 3:不改 register_pg(:753-754)是否合理
|
||||
|
||||
**合理,且为契约要求**。实测 `register_pg`(tests/test_svc.py:231-246)仅把 kind/env/addr/tags/source/version/user[/password]/dbname/authmode/pgdata 各行 `write_text` 进 `runtime/<name>/svc.env`,从不创建或绑定任何 socket;`:753-754` 的 `18951` 只是夹具元数据,被 stub 以 DSN 字符串消费。它不可能引发端口占用型偶发失败,不改正确。修复后全仓跟踪文件中 `18951` 仅剩这两行(`git grep` 实测)。
|
||||
|
||||
## 重点 4:其他用例是否仍硬编码 18951 / 是否需同改
|
||||
|
||||
`18951` 已无其他出现点。其他用例确有硬编码端口:rdm 组 `18842/18843/18844`(经 stub 内嵌 `http.server` **真实绑口**,同属偶发脆弱类)、若干仅作 svc.env 元数据的 187xx/1889x、以及任务卡已登记的 `tests/test_svc_realcombo.py` 9471/9473/9474。**均不在本任务授权范围**(任务卡非目标明示「不含其它用例的端口治理」,并已把 realcombo 记为遗留发现);动态临时口(32768+)与它们不相交,本修复既不掩盖也不交互。属后续独立增量,非阻塞。
|
||||
|
||||
## 重点 5:commit message 与 PR 描述是否如实
|
||||
|
||||
- transport commit message 仅为传输元信息(`agent-exec transport 850b1455…/5ca3ade8a790`),不含范围声明,无夸大面。
|
||||
- implementation commit `5c3a1d4e…` 对象不在本工作区,其 message **未验证**;PR #668 描述在仓外且本单元禁网,**未验证**。
|
||||
- 仓内可核的诚实性证据成立:任务卡的范围陈述与实测 diff 完全一致(「单文件单 hunk」「scripts/svc.sh 不改」「TOCTOU 非阻塞、套件级解法属独立增量」「不解决其它用例硬编码端口,仅记录」)。一处文档小疵见 N3。
|
||||
|
||||
---
|
||||
|
||||
## 非阻塞建议
|
||||
|
||||
- **N1 对象拓扑**:packet 字面审查对象(2fd11ef→d0b217d3 的 diff)混入分支落后于 base 的主线内容;忠实的单元 diff 应对 merge-base/R0(a9566a68)或 transport 父提交(409f4749)取。同步记录显示 c766a81/2841cb4 已并入主线且 `tests/test_svc.py` 与 d0b217d3 一致,合并视角无实际风险;仅提示后续以「相对 main 的最终 diff」复核时勿把主线反向内容误记为本单元改动。
|
||||
- **N2 TOCTOU 套件级解法**:reservation-close 到 bind 的窗口是既有 5 处夹具共有的固有模式;若未来要求消除,可考虑传递已绑定的 fd/listening socket 或碰撞重试一次。属独立增量(任务卡已记),不建议在本任务扩围。
|
||||
- **N3 任务卡计数笔误**:卡称「8 条断言」,实测该用例 `self.assert*` 为 9 条(base 与修复后均 9);实质主张「一条未删未弱化」经核对为真。仅文档精度问题。
|
||||
- **N4 同类脆弱面**:rdm 组真实绑口的 18842/18843/18844 与 realcombo 9471/9473/9474 仍为固定端口;建议另立任务按同一惯例治理。
|
||||
|
||||
## 已运行的验证命令与结果(均只读;测试 3 次,≤5 次限额内)
|
||||
|
||||
| # | 命令 | 结果 |
|
||||
|---|---|---|
|
||||
| 1 | `git cat-file -t 5c3a1d4eb8f3a5c0c0c5be5cd5` | fatal: Not a valid object name(对象不在本工作区) |
|
||||
| 2 | `git merge-base --is-ancestor`(2fd11ef↔d0b217d3 双向) | 均非祖先;merge-base = a9566a68 |
|
||||
| 3 | `git diff 409f4749 d0b217d3` 与 `git diff --stat a9566a68 d0b217d3` | 仅 `tests/test_svc.py` 单 hunk(3+/1−)+ `.task` 卡;产品文件零改动 |
|
||||
| 4 | `git diff d0b217d3 2841cb4 -- tests/test_svc.py` | 空(被测 HEAD 测试代码与 transport commit 一致);svc.sh 差异仅 db_create 1 行(主线管道治理,本用例不执行) |
|
||||
| 5 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests.test_static_fresh_deploy_registers_pidcheck_and_starts` | **OK**(Ran 1, 0.507s) |
|
||||
| 6 | 同上用例,前台命令内以临时监听器占住 18951 后运行、结束时 kill 并确认释放(`ss -ltn` 前后核对) | 占口确认 LISTEN → **OK**(0.518s)→ 占用者已回收、18951 released |
|
||||
| 7 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc.SvcDbRecoveryTests`(注:`-k svc_db_recovery` 因大小写不匹配该类名,改以显式类名运行同一系列) | **OK**(Ran 23, 24.607s) |
|
||||
| 8 | `cat /proc/sys/net/ipv4/ip_local_port_range` | 32768 60999(与全部固定测试端口不相交) |
|
||||
| 9 | `git grep -n 18951`(排除 .task) | 仅 tests/test_svc.py:753-754(受保护夹具) |
|
||||
| 10 | base/transport 用例方法 `self.assert*` 计数对比、`register_pg` 函数体、`svc.sh` `static_do_start`/`check_port_free`/`write_static_reg` 通读 | 9=9 断言一致;register_pg 只写 svc.env;static 路径占口即报错退出、探活语义未变 |
|
||||
|
||||
## 判定标准对照
|
||||
|
||||
- 用例语义被削弱:未发生(断言 9=9,探活守护链完整)→ 非阻塞。
|
||||
- TOCTOU 引入可观察的偶发失败:未发生(失效模式为响亮失败,run 2 决定性场景通过)→ 非阻塞。
|
||||
- 未按约束改动的内容被改:未发生(svc.sh/register_pg/测试类结构均未动)→ 非阻塞。
|
||||
- 补丁外溢到其他用例或脚本:未发生(单文件单 hunk 实测)→ 非阻塞。
|
||||
- 结论:**PASS**。implementation commit message 与 PR #668 描述原文未验证(对象不在本地/禁网),已在重点 5 如实标注,不构成阻塞。
|
||||
@@ -0,0 +1,116 @@
|
||||
# 独立代码审查:fix/svc-pipefail-early-exit-pipes(svc 管道早退读取器治理)
|
||||
|
||||
- 审查对象:`7e19cf3e182b566bdc48a7e690e44ff11b4ab33c..f87d63ffd8ece163bd7054c80cb51ccd6159f994`
|
||||
- 涉及文件:scripts/svc.sh、scripts/task.sh、scripts/e2e.sh、scripts/lint-boundaries.sh、docker/verify-env.sh、tests/load/collaboration/run_pgbench.sh、新增 tests/test_shell_pipe_hazard.py(另含任务卡 `.task/fix-svc-pipefail-early-exit-pipes.md`,属 `.task/**` 豁免范围)
|
||||
- 审查者身份:全新会话独立审查,未参与实现;本会话仅写入本报告,其余只读
|
||||
- 参照先例:Dev PR #657(5562015,scripts/task.sh 回收路径 `git ls-files --stage | grep -q '^160000 '` → awk 全量消费,commit 8557ee9/daf0288..8557ee9)
|
||||
|
||||
## 结论:PASS(无阻塞项;4 项非阻塞建议)
|
||||
|
||||
判定标准逐项核对:无语义回归、无判定翻转、守护测试非形同虚设(真实可失败、对已修复形态全部拦截)、无二进制误判。
|
||||
|
||||
---
|
||||
|
||||
## 一、逐处语义等价核对(重点 1)
|
||||
|
||||
六个脚本均声明 `set -euo pipefail`(svc.sh:134、task.sh:4、e2e.sh:24、lint-boundaries.sh:4、verify-env.sh:2、run_pgbench.sh:10),隐患前提成立。共 34 处变更,按变换形态归类核对:
|
||||
|
||||
### A. 变量 → here-string(`grep … <<<"$var"`,共 15 处)
|
||||
|
||||
sites:verify-env.sh:34、48;lint-boundaries.sh:76;task.sh:23、26、29、214、219、315、316、1070;svc.sh:1067、1703、3577、4923、4955、5040、5100。
|
||||
|
||||
原形均为 `echo/printf "$var" | grep …` 或 `printf '%s' "$var" | head -c N`。实测(/tmp/pipefail-review):here-string 会在串尾补 `\n`,`echo`/`printf '%s\n'` 同样补 `\n`;`printf '%s'` 不补但 grep 按行匹配对尾随换行不敏感,且 task.sh:1070 经命令替换取值时尾随 `\n` 被剥除——实测 `$(head -c 120 <<<"$short")` 与 `$(printf '%s' "$short" | head -c 120)` 输出逐字节一致(含 <120 字节与 >120 字节两例)。命中/未命中/多行/退出码全部一致。此处原"生产者"是 shell 内建 echo/printf,不存在可丢失的生产者退出码。**等价,成立。**
|
||||
|
||||
### B. 文件输入 sed:`sed -n 's/^K=//p' f | head -1` → `sed -n '/^K=/{s///p;q;}'`(共 17 处)
|
||||
|
||||
sites:svc.sh:900、906、1067、2432、3320、5449、5506-5509、5676、5939、5977、6086;task.sh:643、644、647、648、649;run_pgbench.sh:186。
|
||||
|
||||
实测(GNU sed 4.9,/tmp/pipefail-review/kv.txt 多值键/无键/空值用例):多行匹配取首行、未命中输出空、`k=`(空值)输出空——新旧输出一致;`s///` 空正则复用地址正则(POSIX 语义),替换结果与原 `s/^K=//p` 一致。退出码:未命中旧管道(pipefail)=sed 的 0=新式 0;文件不可读旧=2(pipefail 传播 sed)=新式 2。`sed q` 只在匹配后退出,输入是文件而非管道,无 SIGPIPE 面。**等价,成立。**
|
||||
|
||||
### C. 全量消费替代早退:`| head` → `| sed -n 1p` / `sed -n '1,12p'`(3 处)
|
||||
|
||||
- verify-env.sh:41:`find … | sed -n 1p`——sed 无 q 会读尽输入,输出首行,实测与 `head -n 1` 输出一致(含空输入)。
|
||||
- run_pgbench.sh:180:`ps … | sed -n '1,12p'`——同上,实测输出一致。
|
||||
|
||||
### D. 进程替换 `< <(producer)`(6 处)
|
||||
|
||||
svc.sh:1476(find -print -quit)、3943(strings)、4415(pg_psql)、4949(serve -h)、5087(pg_run)、5156/5169(head -c 15)。退出码语义变化见第二节。
|
||||
|
||||
### E. e2e.sh:211:`grep -o '装载(.*)' f | head -1 || true` → `grep -m1 -o … || true`
|
||||
|
||||
实测:`.*` 贪婪使每行至多产生一个匹配,故 `head -1`(第一个匹配)≡ `-m1 -o`(首个匹配行的全部匹配)——单行/单匹配/整行贪婪匹配三例输出一致。文件输入无管道 SIGPIPE;`|| true` 为原有写法(旧式同样以 `|| true` 收尾),非新增掩盖。**等价,成立。**
|
||||
|
||||
---
|
||||
|
||||
## 二、退出码语义的真实变化逐处判定(重点 2)
|
||||
|
||||
进程替换后生产者退出码不再参与判定。前提复现:`bash -c 'set -euo pipefail; seq 1 200000 | grep -q 1'` 实测判为 MISS、裸管道状态 141(隐患为真)。「生产者失败但输出恰好命中」实测确实由旧=失败翻转为新=成功(`f(){ echo X; return 3; }`:旧管道判 FAIL(3),新 procsub 判 HIT)。逐处判定:
|
||||
|
||||
| site | 原行为 | 新行为 | 判定 |
|
||||
|---|---|---|---|
|
||||
| svc.sh:4949 `serve -h 2>&1 \| grep -Eq config` | `-h` 以非零退出(真实 CLI 常见)时 pipefail 使判定翻为「不识别」→ 假警报 | 仅看 help 文本是否含 `--config`(实测 2×2 矩阵:exit0+命中=静默、exit0+未命中=告警、exit2+命中=旧假警报/新正确静默、exit2+未命中=告警) | **修正**。判定目标本就是「help 文本内容」,忽略 `-h` 退出码语义正确;未发现有害翻转场景 |
|
||||
| svc.sh:3943 `strings \| grep -q AGENT_SYSTEM_DSN` | 大二进制(串靠前、残余 >64KiB)时 strings 吃 SIGPIPE→141→`&& return 0` 失败→假「不支持」(实测复现:合成用例旧判 NOT supported) | 命中即真支持;strings 半途 I/O 失败且部分输出恰含该串才会假「支持」——部分输出含串即证明产物确实内嵌该串 | **修正**(PR #657 同类缺陷的真实翻版) |
|
||||
| svc.sh:4415/5087 `pg_psql/pg_run "SELECT 1 …" tA \| grep -q 1` | psql 连接失败(空输出)→ 判「库不存在」→ 转 CREATE → 二次失败报错(误导);「失败但 stdout 恰有 1」对单值 SELECT 不成立 | 失败(空输出)同样判「不存在」转 CREATE,下游可见失败,与旧一致 | **中性偏修正**;无真实「失败但命中」场景 |
|
||||
| svc.sh:1476 `find … -print -quit \| grep -q .` | 单行输出,无缓冲面;目录不存在时新旧均判空(实测均 status 1) | 同 | **中性(防御性)** |
|
||||
| svc.sh:5156/5169 `head -c 15 \| grep -qa` | head 只写 15 字节(<64KiB),无 SIGPIPE 面;文件可读性由前置 `[ -f ]` 保证 | 同 | **中性** |
|
||||
| verify-env.sh:41、run_pgbench.sh:180 | find/ps 输出 >64KiB(大型 playwright 安装、>~700 进程)或 ps 偶发 exit 1 时,旧式在 `set -euo pipefail` 下直接中止(141 传播)| sed 读尽输入,状态恒 0,失败不再中止诊断/汇总块 | **修正**;ps 失败被掩盖属诊断块可接受(已在旧代码 flaky 风险与本次修复间取舍,记录在案) |
|
||||
| 其余 here-string/sed 文件输入各处 | — | 生产者为内建或 sed 自身,无生产者退出码可丢失 | **等价** |
|
||||
|
||||
结论:所有真实变化方向均为「修正旧判定翻转」或「中性等价」,未发现「生产者失败但输出命中被新写法误判成功且旧写法正确拒绝」的成立场景。
|
||||
|
||||
## 三、二进制与边界(重点 3)
|
||||
|
||||
svc.sh:5156/5169 `grep -qa '^SQLite format 3$' < <(head -c 15 …)`:
|
||||
|
||||
- 进程替换是 fd 直通,不经过 bash 变量,**无 NUL 截断面**;`-qa` 保留,按字节读取(实测 `head -c 15 real.db | od -c` = `S Q L i t e f o r m a t 3`,15 字节,NUL 是第 16 字节本就不在窗口内)。
|
||||
- 无尾随换行的"末行"命中语义新旧一致:实测真 SQLite 头两式均检出、非 SQLite 文件两式均不检出。
|
||||
- `head -c 15` 产出 ≤15 字节,新旧均无 SIGPIPE 面。**无回归。**
|
||||
|
||||
## 四、守护测试质量(重点 4)
|
||||
|
||||
tests/test_shell_pipe_hazard.py(311 行):词法扫描器 + 4 个自测。
|
||||
|
||||
- **真实可失败**:`PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_shell_pipe_hazard -v` → 4 tests OK;检测器对合成隐患 `seq 1 200000 | grep -q 1`、`if ! … | grep -q`、`sed|head` 命令替换、`-m1`、续行管道、`sed q`、`grep -qiE || exit` 七种形态全部命中(test_detector_flags_* 断言存在,检测器失效即测试红)。
|
||||
- **注释/heredoc/case/引号覆盖**:实测探针 14 项全过——注释行、heredoc 正文、case 模式 `|`、双引号内管道文本均不误报;`<<<`、`< <()`、`sed -n 1p` 安全形态不误报;`grep -q/-m`、`head`、`sed q` 尾级全部命中。
|
||||
- **已知盲区(实测确认,当前树未触发恶性形态)**:
|
||||
1. 只查「管道末级」:中段早退读取器不查——svc.sh:3804/5482 `grep -oE '"dsn"…' | head -1 | sed -E …`(两处)原样存留且守护通过;
|
||||
2. 不建模 awk/read:svc.sh:1058 `ss -ltnp | awk '{print; exit}'` 为尾级早退读取器,未命中;
|
||||
3. 不识别前缀化 grep:`producer | LC_ALL=C grep -q x`、`command grep -q` 可绕过(当前六脚本无此写法,实测确认)。
|
||||
- **311 行是否失衡**:判定为**可接受、不失衡**。svc.sh 含 heredoc、case 模式、嵌套 `$()`、续行管道与多种引用,逐行正则式简化实现必然双向误报/漏报(其漏报恰是本缺陷类);扫描器自带合成自测可失败,复杂度换来的是零误报地通过当前树。失衡风险的代价是上述盲区,见非阻塞建议。
|
||||
|
||||
## 五、范围与遗漏(重点 5)
|
||||
|
||||
- 变更范围:`git diff --name-only 7e19cf3..f87d63f` 恰为 7 个目标文件 + 任务卡(`.task/**` 豁免),无越界改动。
|
||||
- 残留早退读取器(runtime/ 已确认为 ignored 陈旧克隆:`git check-ignore -v runtime/` 命中 .gitignore:9,且其下 0 个 tracked 文件,未据此判定):
|
||||
- 受管脚本内(先于本分支存在,git show 7e19cf3 证实):svc.sh:3804、5482(中段 `head -1`)、svc.sh:1058(尾级 `awk … exit`)。现实触发需 >64KiB 的 dsn 匹配行/海量监听套接字,且失败形态是 `$(…)` 赋值非零→`set -e` 中止(响亮失败),非静默翻转——**不阻塞**。
|
||||
- 受管清单外(本单元范围外,仅记录):e2e-duties.sh:87(`ps -p | grep -Fq`,单行输出,bounded)、:128(`curl | head -c 160`);tests/integration/rdm-increment/acceptance_scenarios.sh(`set -u` 无 pipefail,echo 内建生产者);tests/ops/lib.sh:82、146(无 pipefail)。
|
||||
- 掩盖写法:六脚本无 `set +o pipefail`;本分支新增的 `|| true` 仅 e2e.sh:211 一处且为原有收尾写法的保留,非掩盖新增。PR #657 式「全量消费」替代(awk)未用于本批,改用 here-string/procsub/sed-q,方向一致且更彻底。
|
||||
|
||||
## 非阻塞建议(不阻塞合入)
|
||||
|
||||
1. **守护测试盲区**:把检测范围扩到中段读取器(`| head`、`| sed q` 出现在任意非首段即报),并建模 `awk '…exit'`、`| read`、前缀化 grep(`LC_ALL=C grep`);或至少在 docstring 声明「仅末级」契约的同时为 svc.sh:3804/5482/1058 立即整改。当前两处 dsn 残留建议改 `sed -n '/"dsn"/{s//…/p;q;}'` 式文件直读。
|
||||
2. **SCRIPTS 元组硬编码**:新增受管脚本不会被自动纳入扫描,建议 glob(如 scripts/*.sh + 白名单豁免)。
|
||||
3. **守护测试纯词法**:可加一条行为级自检(`bash -c 'set -o pipefail; seq 1 200000 | grep -q 1; test $? -eq 141'`)把 141 前提钉进测试,防 bash 语义漂移。
|
||||
4. **run_pgbench/verify-env 的 sed 替代**掩盖了 ps/find 的真实失败状态:诊断块可接受,但若未来该块内命令失败需要可见,应显式记录状态。
|
||||
|
||||
## 已运行的验证命令与结果
|
||||
|
||||
| 命令(只读) | 结果 |
|
||||
|---|---|
|
||||
| `git log --oneline 7e19cf3..f87d63f` / `git diff --stat` 同范围 | 2 commits;8 文件(7 目标 + 任务卡),+448/−47 |
|
||||
| `git status --porcelain`(开工时) | 干净;HEAD=f87d63ff(detached) |
|
||||
| 六脚本 `set -` 声明检查 | 全部 `set -euo pipefail`(svc.sh:134 等) |
|
||||
| /tmp/pipefail-review 语义实验(sed 新旧式多值/未命中/空值/不可读;here-string vs printf 含 `head -c 120`;SIGPIPE 复现 141;procsub 退出码翻转矩阵;serve -h 2×2 矩阵;`grep -m1 -o` vs `head -1` 贪婪匹配;SQLite 头 15 字节两式对照;find -quit 有/无目录) | 全部如上文引用,行为与判定一致 |
|
||||
| `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_shell_pipe_hazard -v` | 4 tests OK(0.083s) |
|
||||
| `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_svc tests.test_svc_backup` | 34 tests OK(34.3s) |
|
||||
| 检测器 14 项探针(含中段 head、awk exit、env 前缀 grep、`read`、注释/heredoc/case/引号) | 14/14 符合设计;盲区 3 类确认(见第四节) |
|
||||
| 22 个 tracked `*.sh` 独立残留扫描(grep 形态匹配) | 残留清单见第五节 |
|
||||
| `bash -n` 六脚本 | 全部 OK |
|
||||
| `git check-ignore -v runtime/`;`git ls-files runtime/ \| wc -l` | ignored(.gitignore:9);0 个 tracked 文件 |
|
||||
|
||||
## 未验证项(如实标注)
|
||||
|
||||
- 仅在 GNU sed 4.9 / 当前 bash 验证;macOS BSD sed 的 `s///` 复用地址正则与 `{…q;}` 行为**未验证**。
|
||||
- `strings` 大二进制路径为合成复现,未在真实制品上运行(NOT_RUN)。
|
||||
- 完整测试套件与 CI 未运行(本单元只读约束内的过滤子集已全绿)。
|
||||
- e2e-duties.sh、acceptance_scenarios.sh、tests/ops/lib.sh 的残留未整改(范围外,仅登记)。
|
||||
@@ -0,0 +1,97 @@
|
||||
# 独立代码审查:task.sh finish 的 gitlink 检测 pipefail 修复
|
||||
|
||||
- unit_id:`tasksh-force-independent-review`
|
||||
- 审查对象:`8a8f9f57a58b4ec8701b576a98c5f0c0c5be5cd5..d755fa0f6d112b6cc488b60d937d8419696e1edb`(范围内唯一提交 d755fa0,仅 `scripts/task.sh` +3/-1 与 `tests/test_task.py` +20)
|
||||
- 审查者身份:全新独立会话,未参与实现;除本报告文件外未修改任何文件,未执行 git add/commit/push。
|
||||
- 契约依据:policy/governance.md §回收(L141-143「Git 对含子模块的工作区需要显式删除选项时,只有全部现场检查通过才允许该路径;它不豁免任何脏状态……普通删除失败不转入强删兜底」、L145「远端分支删除与本地回收分别报告」);policy/delivery.md §清理与恢复(L449-451)。
|
||||
|
||||
## 结论:PASS
|
||||
|
||||
无阻塞项。修复正确、意图保持完整、回归测试真实复现生产故障且在未修复脚本上失败、范围未越界。非阻塞建议见文末。
|
||||
|
||||
---
|
||||
|
||||
## 重点 1:修复正确性 —— 新检测式
|
||||
|
||||
变更(scripts/task.sh:1133-1136):
|
||||
|
||||
```bash
|
||||
# Read the whole listing: an early-exit reader SIGPIPEs git on large indexes and pipefail drops --force.
|
||||
if git ls-files --stage | awk '$1 == "160000" { found = 1 } END { exit !found }'; then
|
||||
removal+=(--force)
|
||||
fi
|
||||
```
|
||||
|
||||
依据与结论:
|
||||
|
||||
1. **读取全部输出、与索引大小无关**:awk 主规则只赋值不 exit,天然消费到 EOF。实测本工作区(822 行索引)`git ls-files --stage | awk '{count++} END {print count}'` 输出 822,与 `wc -l` 一致。
|
||||
2. **仅索引含 mode 160000 时为真**:`ls-files --stage` 每行格式 `<mode> <oid> <stage>\t<path>`,`$1` 恒为 mode(路径在 TAB 之后,含空格/引号路径不影响 `$1`;git 对含换行等控制字符的路径按 core.quotePath 默认 C-quoted 输出,不产生裸换行行)。`160000` 只有 gitlink;普通文件 100644/100755、符号链接 120000 均不匹配。冲突 stage 1/2/3 的 gitlink 行 `$1` 同为 160000,与旧 `grep '^160000 '` 语义一致。
|
||||
3. **真值等价且消除 SIGPIPE**:`END { exit !found }` 在 found=1 时 exit 0、否则 exit 1,与 `grep -q` 的 0/1 真值一致;差异仅在于不再早退。
|
||||
4. **两种仓库实测**(临时目录 /tmp/review-gitlink-GNWq,`git init` + 3000 个文件 ≈178KB listing):
|
||||
- 无 gitlink:新检测式 exit=1(不加 `--force`,正确)。
|
||||
- 有 gitlink 且排在前、其后 178KB 输出:新检测式 exit=0;同一索引上旧式 `git ls-files --stage | grep -q '^160000 '` 在 `set -o pipefail` 下 pipeline exit=**141**(git 吃 SIGPIPE)——即生产故障机理的直接复现,证明修的是真问题。
|
||||
5. **可移植性**:`$1 == "160000"`、变量赋值、`END { exit expr }` 均为 POSIX awk;gawk/mawk/busybox awk/BSD awk 通吃。bash -n 通过。shellcheck 未安装,未运行(如实标注 NOT_RUN)。
|
||||
|
||||
## 重点 2:意图保持 —— 门禁未被削弱
|
||||
|
||||
逐条对照 d755fa0 的 cmd_finish(scripts/task.sh:1092-1166),diff 仅替换 L1135 一行检测式与新增 L1134 注释,其余全部未动:
|
||||
|
||||
- 写者确认:L1095 `TASK_WRITERS_STOPPED=1` 原样。
|
||||
- 分支类型门禁:L1096-1097(symbolic-ref + feature/fix/hotfix/release 白名单)原样。
|
||||
- 合入祖先校验:L1116 `merge-base --is-ancestor`(含 immediate 父任务解析 L1110-1114)原样。
|
||||
- 远端 lease:L1117 `ls-remote` → L1119 fetch → L1120 rev-parse FETCH_HEAD → L1122 `--is-ancestor`,原样。
|
||||
- fetch 后二次 `check_worktree finish`(registered-root/owner/锁/递归脏检查):L1132 原样;`--force` 仍只在全部现场检查通过后的这一个分支点追加(governance §回收「只有全部现场检查通过才允许该路径」得到保持)。
|
||||
- Git 拒绝时不得 rm 兜底或追加 force 等级:L1138-1139 `die "…no rm fallback or additional force level is permitted."` 原样。
|
||||
- 分支与远端分别报告:L1152-1164 的 `[recycled]/[partial]` 四分支与 `--force-with-lease` 删除租约(L1156)原样。
|
||||
- **无误判 force**:无 gitlink 仓库实测检测式为假(重点 1.4),不会给普通仓库追加 `--force`;有 gitlink 时追加行为与修复前语义一致(只是不再被 SIGPIPE 误翻为假)。
|
||||
|
||||
## 重点 3:回归测试有效性(tests/test_task.py:496-515)
|
||||
|
||||
1. **复现生产故障机理**:夹具含 gitlink(`component`)+ 其后 2000 个 `zz-bulk/entry-*.60字符` 文件(每行 listing ≈111B,共 ≈217KB)。gitlink 行在 `zz-bulk` 之前由 'c'(0x63) < 'z'(0x7a) 的字节序决定——测试不是靠假设,而是用 `split("\tcomponent\n")` + `before_gitlink.rsplit("\n",1)[-1].startswith("160000 ")`(L510)显式断言了 gitlink 行位置,排序假设被破坏时测试会响亮失败而非静默失效。
|
||||
2. **64KiB 断言稳健**:L512-513 断言全 listing >64KiB、gitlink 之后 >2×64KiB。>2× 的余量覆盖默认 64KiB 管道缓冲 + grep 单次读缓冲的提前消费,在 Linux 默认管道容量(64KiB)下判定确定性充分。残余风险:若某环境的管道容量或读者缓冲合计超过 ~217KB(非默认配置),旧脚本可能不再触发 SIGPIPE——见非阻塞建议 N1。
|
||||
3. **修复前必失败(已实测)**:在 /tmp 一次性 clone(不触碰本工作区)中,将 scripts/task.sh 还原为 `git show 8a8f9f5:scripts/task.sh` 后运行该用例:**FAIL**,失败点即生产故障原文——`fatal: working trees containing submodules cannot be moved or removed` → `ERROR: Git refused worktree removal; … no rm fallback …`,exit 1≠0。同一 18 个 `-k finish` 用例在旧脚本下恰好只有这 1 个失败,证明该用例是唯一判别用例、修复未改变其它 finish 行为(旧的 `test_finish_recycles_clean_submodule_without_shared_config_changes` 因小索引不触发 SIGPIPE,在新旧脚本下均通过——这正是该缺陷此前潜伏的原因)。
|
||||
4. **修复后通过(已实测)**:本工作区运行该用例 `ok`(0.512s);`-k finish` 全部 18 用例 `OK`(10.108s)。
|
||||
5. **无偶发/网络依赖**:夹具远端是本地 bare 仓(push 为本地文件传输),triage 桩不参与 finish 路径;同一用例在两个环境共运行 4 次全部稳定。
|
||||
|
||||
## 重点 4:范围与规范
|
||||
|
||||
- `git diff --stat` 范围内仅 `scripts/task.sh`(+3/-1)与 `tests/test_task.py`(+20),无其它文件,无隐性扩围。
|
||||
- 风格一致性:仓库已有同类先例——task.sh:113-114(`card_merged_into`「以完整消费输出取首行,避免 grep -q 早退令 git log 吃 SIGPIPE(141 under pipefail)」)。本次修复(全文消费 + 英文注释说明 SIGPIPE 机理)与该先例同构,注释语言与 cmd_finish 周边英文注释一致;测试注释与相邻 finish 用例一致。
|
||||
- `set -euo pipefail`(task.sh:4)是缺陷前提,本次未改动全局 set,改动局部于单行检测式,最小充分。
|
||||
|
||||
## 重点 5:残余风险(非阻塞,不在本任务修复)
|
||||
|
||||
`grep -rnE "\| *grep -q" scripts/` 排除内建生产者(printf/echo)后,`set -euo pipefail` 的 scripts/svc.sh 中存在同类「外部生产者 + 早退读者」管道:
|
||||
|
||||
- **svc.sh:3943** `strings "$1" 2>/dev/null | grep -q 'AGENT_SYSTEM_DSN' && return 0`:大二进制上 strings 输出可超 64KiB,匹配靠前时 strings 吃 SIGPIPE →141 → `&&` 不成立 → 携带 DSN 的二进制被误判为无 DSN。与本次修复的缺陷同类,最值得后续独立任务处理。
|
||||
- **svc.sh:1476** `find "$backup" … -print -quit | grep -q .`:双侧早退存在理论竞态,但单行输出远小于管道缓冲,实际风险低。
|
||||
- svc.sh:4415/5087(psql `-tA` 单行输出)、5156/5169(`head -c 15` 有界)实际安全。
|
||||
|
||||
以上按单元要求仅记录为非阻塞建议,本任务不修。
|
||||
|
||||
## 已运行的验证命令与结果
|
||||
|
||||
| # | 命令 | 结果 |
|
||||
|---|---|---|
|
||||
| V1 | `git diff --stat 8a8f9f5..d755fa0` | 仅 scripts/task.sh +3/-1、tests/test_task.py +20 |
|
||||
| V2 | `git show 8a8f9f5:scripts/task.sh`(对照 L1134 旧式 grep -q) | 旧检测式确认为 `git ls-files --stage \| grep -q '^160000 '` |
|
||||
| V3 | `bash -n scripts/task.sh` | 通过 |
|
||||
| V4 | `which shellcheck` | 未安装 → shellcheck NOT_RUN |
|
||||
| V5 | 临时仓(/tmp/review-gitlink-GNWq):无 gitlink 跑新检测式 | exit=1(不加 force) |
|
||||
| V6 | 同仓加入 gitlink(排前 + 178KB 后续输出)跑新检测式 | exit=0 |
|
||||
| V7 | 同仓 `bash -c 'set -o pipefail; git ls-files --stage \| grep -q "^160000 "'` | exit=141(SIGPIPE,生产机理复现) |
|
||||
| V8 | 同上换新检测式并读 PIPESTATUS | pipeline exit=0、git exit=0(全量消费) |
|
||||
| V9 | 本工作区 `git ls-files --stage \| awk`(工作区含 gitlink) | exit=0;awk 行数 822 == wc -l 822 |
|
||||
| V10 | `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_task.TaskLifecycleTests.test_finish_recycles_submodule_when_index_listing_exceeds_pipe_buffer -v`(工作区=修复后) | ok,Ran 1 test in 0.512s |
|
||||
| V11 | `… -k finish -v`(工作区) | Ran 18 tests, OK (10.108s) |
|
||||
| V12 | /tmp 一次性 clone + `git show 8a8f9f5:scripts/task.sh > scripts/task.sh` 后运行新用例 | FAIL:`fatal: working trees containing submodules cannot be moved or removed` → exit 1≠0(生产故障原文) |
|
||||
| V13 | 同 clone `… -k finish` | Ran 18 tests, FAILED (failures=1),唯一失败即新用例 |
|
||||
| V14 | `git status --short`(收尾复核) | 空(工作区除本报告外无修改) |
|
||||
|
||||
未验证事项:shellcheck 静态分析未运行(工具未安装);>2×64KiB 管道余量仅覆盖 Linux 默认管道容量(V10/V12 在本机 Linux 通过/判别成立),非默认管道容量的 CI 环境未实测(见 N1)。
|
||||
|
||||
## 非阻塞建议
|
||||
|
||||
- N1(测试微硬化,可选):`assertGreater(len(after_gitlink.encode()), 2 * 64 * 1024)` 可再留一档余量或对夹具条目数写明容量假设;当前 2× 在 Linux 默认 64KiB 管道下已充分(V10/V12 实证),不改不阻塞。
|
||||
- N2(后续独立任务):修复 svc.sh:3943 `strings | grep -q` 的同类 pipefail 早退隐患(全量消费或去掉早退读者)。
|
||||
- N3(后续独立任务):评估 svc.sh:1476 `find -print -quit | grep -q` 的双侧早退竞态(当前实际风险低)。
|
||||
@@ -0,0 +1,90 @@
|
||||
# Independent Code Review — feature/agent-stack-audit-remediation-b1
|
||||
|
||||
- Reviewer: independent CLI review session (agent-stack-b1-independent-review unit)
|
||||
- Date: 2026-10-21
|
||||
- Review object: Dev worktree branch `feature/agent-stack-audit-remediation-b1` @ `d55e57dd` (merged tip) versus main @ `b25846d2`; five pinned components diffed old-pin → new-pin:
|
||||
- deps/agent-native `2fc66be6` → `ac91b843`
|
||||
- deps/agent-squad `c7121bda` → `0f1daa8b`
|
||||
- deps/agent-intent `651a2e1e` → `765a7e71`
|
||||
- deps/agent-command `4f583ca3` → `fe7987dc`
|
||||
- modules/backend `528e3a4c` → `b6141800`
|
||||
- Plan: docs/superpowers/plans/2026-10-21-agent-stack-audit-remediation-plan.md (audit baseline d9e9c0560a)
|
||||
- Claimed scope: batch 1 = U1, U2, U3a/U3b, U4, U5, U6, U7a/U7b, U10.
|
||||
- Method: read-only review of `git diff` per component plus Dev-level files; focused package tests where this worktree permits (see Verification runs). This review made no changes other than this report file.
|
||||
|
||||
## Findings
|
||||
|
||||
### Blockers
|
||||
|
||||
None.
|
||||
|
||||
### Major
|
||||
|
||||
None.
|
||||
|
||||
### Minor
|
||||
|
||||
- **M1 — SessionScope identity string can collide (native).** `adapters/native/contract_bind.go:60-66` builds the scope identity as `tenant+"/"+project+"/"+session`. Distinct scopes containing `/` can collide (tenant `a/b` + project `c` vs tenant `a` + project `b/c`), which would bind two identities to the same hashed transcript file (contract_bind.go:72-77) and pass the exact-match check in `bindSessionScope` (adapters/native/backend.go:645-669). Today product identifiers do not contain `/`, so no legal path regresses; recommend length-prefixed or JSON-encoded identity in a follow-up.
|
||||
- **M2 — Reflection/JSON "probe" of statically present fields (squad).** `adapters/native/register.go:217-247` marshals the native result through JSON to discover `SideEffects/Canceled/Aborted`. The squad module is pinned to native `ac91b843`, where these fields exist at compile time; direct field copy is identical, simpler, and the "deps 尚未跟进" rationale in the comment cannot occur in a statically linked Go build. Behaviorally equivalent today; flagged as avoidable indirection that can silently mask future field renames (JSON key drift would quietly fall back to "possible").
|
||||
- **M3 — `trustedUnknownOpID` does not survive JSON roundtrip (command).** `result.go:44-49` (unexported flag) + `result.go:76-95` (Validate): an Unknown receipt carrying a trusted OperationID fails `Validate()` after a marshal/unmarshal cycle. Verified by grep that no in-repo path re-validates a deserialized receipt (backend persists audit *envelopes*, not receipts; `internal/cmdbus` has no ExecReceipt unmarshal), so nothing breaks today — but the library contract is sharper than its docs admit. Suggest documenting the roundtrip limitation or providing a rehydration constructor.
|
||||
- **M4 — Stale recovery header comment (squad).** `squad/engine.go:1053-1069` still states "auto 允许一次重试", but `HitlAuto` now falls into the `default:` reconciliation branch (engine.go:1137-1178) and no longer auto-retries running steps without a trusted record. The code is the intended F05 tightening; only the comment contradicts it.
|
||||
- **M5 — Writable preflight does not cover `delegate_task` (native).** U2 preflight (adapters/native/backend.go:213-227) rejects non-empty Writable with exec/MCP/host tools, but the workspace registry still registers `subagent.Register` (backend.go:278) and code orchestration (`run_tool_script`). `run_tool_script` only invokes registry tools (which enforce the allowlist, runtime/tools/registry.go:369-388) and bans the interpreter, so the residual exposure is a spawned sub-agent not inheriting the parent's write-set. The plan scoped this batch's preflight to "进程/MCP/宿主工具"; a follow-up decision on subagent scoping is warranted, not a blocker here.
|
||||
- **M6 — Canceled mapping keyed on ctx, not on the result (native).** `adapters/native/contract.go:100-105` maps to `OutcomeCanceled` only via `errors.Is(ctx.Err(), context.Canceled)`. A `runRes.Canceled`/`StopAborted` run that is *not* caused by ctx cancellation falls to the failed branch (with `Retryable=false` because backend.go:385-396 already clears it, so no unsafe retry — only the outcome label is less precise). Edge case; native abort paths set ctx.
|
||||
|
||||
### Notes
|
||||
|
||||
- **N1 — Squad `AGENT-EXECUTION.md` disables cursor/antigravity adapters** (component diff, +10/-2). Operational agent-execution config, not product code; documented on the task card with fail-stop rationale. It will ride the component branch into the component repo — restore `enabled: true` as the file itself instructs.
|
||||
- **N2 — Command `AGENT-EXECUTION.md` added (+67).** Host-documented deviation (component lacked one; template parity with agent-intent), recorded on the task card.
|
||||
- **N3 — Executor failures without `SideEffects=none` lose auto-retry.** `squad/workflow.go:455-459` + `squad/port.go:99-117` intentionally default missing qualifier to "possible" (no bounded retry). This is the specified KTD4/U3 behavior change ("缺少副作用证明的失败默认禁止自动重放"), covered by `TestSideEffectsPossibleNotRetried` / `TestSideEffectsNoneRetriedWithinBounds`; third-party executors must now declare `none` to keep transient retry. Listed so the API change is visible to integrators.
|
||||
- **N4 — Generated-command ActionRef now resolves against the full snapshot**, not only the top-k catalog (`intent/recognizer.go:384-437` `resolveActionRef`, replacing `generatedSpecOf`). Deliberate widening that fixes F08's "合法异名目标被拒"; slot keys/values and dispatch still gated by `validateProvidedSlots` (intent/slots.go:174-214) and backend admission (internal/server/intent_admission.go:41-63).
|
||||
- **N5 — Pin coherence verified.** Nested gitlinks agree at every level: intent `765a7e71` (native→deps, squad→deps, backend→deps), native `ac91b843` (squad→deps, backend→deps), command `fe7987dc` (backend→deps), squad `0f1daa8b` (backend→deps).
|
||||
- **N6 — Audit-failure unknown receipts keep the trusted ID** via `unknownReceipt`/`NewUnknownAfterExecution` (internal/cmdbus/cmdbus.go:690-712, deps/agent-command/result.go:64-71); rejected never carries an ID (cmdbus.go:706-712). Confirms U7b's F10 requirement end-to-end with `TestCommittedWithAuditFailureKeepsOperationID`.
|
||||
|
||||
## Unit-by-unit review (spec lens, with trigger-condition evidence)
|
||||
|
||||
| Unit / Finding | Trigger in plan | Blocking change in diff (file:line) | Verdict |
|
||||
|---|---|---|---|
|
||||
| U1 / F01 | reasoning Targets with `../`, absolute, or escaping symlink wrote files before delivery validation | Authorization pass before any write: `pathInWriteSet` + `tools.Resolve` per target, then a second write loop (adapters/native/backend.go:336-378); zero-write asserted by `TestBackendReasoningProfileRejectsPathEscape` (traversal/absolute/symlink, safe.md also absent) | blocked |
|
||||
| U1 / F03 | ExecConfig env nil → host env inherited | `normalizeExecEnv` nil→empty whitelist at registration and per-process (runtime/tools/exec.go:42-51, 165); tests nil/explicit-empty/whitelist | blocked |
|
||||
| U1 / F15a | CombinedOutput unbounded before 64KiB trim | Continuous drain into fixed head+tail ring (`headTailBuffer`, exec.go:192-276; `runProcess` 157-175); bounded-memory + cancel-drain tests | blocked |
|
||||
| U2 / F02 | Same WorkDir reused across SessionScopes; Writable set on workspace | Scope-keyed transcript `transcriptPathForScope` (contract_bind.go:72-77); fail-closed identity binding incl. refusal to adopt identity-less stores (backend.go:645-669); Writable enforced on write/edit/multi_edit (runtime/tools/registry.go:356-388, builtin.go:106-109/155-158/339-342) with exec/MCP/host preflight rejection (backend.go:213-227); empty scope keeps legacy path (compat verified) | blocked |
|
||||
| U2 / F04 | Torn JSONL tail then append → mid-file corruption | `repairTailLocked` final-line-only repair, valid fragment gets newline, invalid truncated, mid-file untouched (runtime/session/tree.go:156-234); torn/missing-newline/mid-file/append-then-load tests | blocked |
|
||||
| U2 / F15b | CapText declared but Text empty | Contract captures final assistant text and surfaces it; pure-text success no longer forces implicit file delivery (`withAllowEmptyTargets`, contract.go:62-66, 88-96); `TestContractPureTextSuccessCarriesText` | blocked |
|
||||
| U3a | canceled treated as retryable; side-effect honesty | Result gains Canceled/Unknown/SideEffects with `EffectiveSideEffects` (exec/backend.go:79-105); loop propagates ToolCalls and Canceled, clears Retryable on cancel (runtime/loop.go:202-213, 343-361); adapter maps definite-preflight → none, tool dispatch → possible, run-started transport failure → Unknown+possible (backend.go:334-341, 381-409, sideEffectsForTools 593-599) | blocked |
|
||||
| U3b / F05 | success=patches-only; failed-with-patches promoted; file-presence completion on recovery; new ExecutionID per retry | `(nil,nil)`/nil-result stable failure (workflow.go:429-439); promotion path removed — patches materialize only after explicit Success, or as diagnostics without flipping status (workflow.go:444-461); canceled/aborted never retried (workflow.go:440-452, port.go:121-123); recovery trusts only `step_terminals.json` records, otherwise `needs_reconciliation` (engine.go:1070-1180, models.go:299-314); `RunWaitingReconciliation` resume inspects without advancing (engine.go:1199-1206); ExecutionID reused across attempts and recovery (workflow.go:402-409, engine.go:1130-1133; `TestRetryReusesExecutionIDAndDeadline`) | blocked |
|
||||
| U4 / F06 | config update reuses stale executor cache | Cache key adds `ConfigIdentity(role)` + factory generation (squad/backend.go:171-175, config_identity.go:14-33 incl. BashPermission); Register/RegisterInstance invalidate per-backend cache (backend.go:62-75); concurrency gates persist across generations and update max on resolve (backend.go:182-285); allow→deny / factory swap / gate-cap tests | blocked |
|
||||
| U5 / F07(intent) | caller mutates registry input or returned nested objects | Deep clone at Register/compile/Snapshot/Options/Catalog boundaries (intent/fold.go:57-129, registry.go:488, 520-528, 554, 588-592, catalog.go:36-60,142); isolation asserted in u5 tests | blocked |
|
||||
| U5 / F08 | generated fallback bypasses slot validation / loses risk metadata; valid alias rejected | Generated Top built from authoritative compiledSpec with full slot validation, missing/invalid projection, risk/confirm/action metadata (recognizer.go:384-437, slots.go:174-214); classifier scores NaN/Inf/out-of-range rejected and quantized before threshold (recognizer.go:176-201, 322-330); non-finite/low-confidence keep ambiguous | blocked |
|
||||
| U6 / F09 | InvalidSlots dropped; router only sees Start; text-only params | Gate projects Missing *and* Invalid with reasons + risk/confirm/action (intentgate/gate.go:42-52, 198-232); router blocks dispatch unless `Complete()` and structured Clarification (route/route.go:60-72, 173-180); unsupported cancel/set_slot honestly fall back to turn without dispatching same-sentence Start (route/route.go:83-90, 121-131); params mapped per declared schema with type casts and required-param admission (server/intent_admission.go:41-113); high-risk still confirmed by cmdbus (`TestHighRiskCommandStillRequiresConfirmation`) | blocked |
|
||||
| U7a / F07(command) | ParamsSchema/ExposeAsTool shared references | Deep copy in/out of Register/Resolve/List (registry.go:37-47, 62-66, 117, 124-130); mutation conformance test | blocked |
|
||||
| U7b / F10 | any handler error → "no side effects rejected"; audit failure drops op ID | `ErrNotExecuted`/`NotExecuted()` marker; unmarked handler errors → Unknown with trusted op ID (cmdbus.go:75-118, 536-553); audit-failure path preserves ID for committed/accepted/unknown and never for rejected (cmdbus.go:687-712); confirm-card unknown lands warn terminal with reconciliation note (cmdbus.go:615-640); owner/project-scoped bounded operation query + endpoint (cmdbus.go:263-296, server/operation_query.go) | blocked |
|
||||
| U10 / F13 | squad resolves model/base/key from default account/project | Factory resolves entire route from the run's effective snapshot + profile via the same `ResolveChatModel`/`OpenRouteClient` rules as direct path (app/squad_native.go:57-84, runtime/run_config.go:17-62, squad_runner.go:83-86, llm.go:255/604); fallback to default only when host lacks the Settings port; owner-crossing secret tests (`TestSquadNativeOptionsNeverBorrowsAnotherOwnerSecret`), route-contract EOF parity test | blocked |
|
||||
|
||||
## Regression lens
|
||||
|
||||
- **Empty SessionScope:** legacy fixed transcript path, no identity checks, `Targets` defaulting preserved (backend.go:144-146, contract_bind.go:72-74). Verified by `TestLegacyTranscriptStillLoads` and empty-scope branches in regression tests.
|
||||
- **Zero-value results:** `ExecResult{}` zero value = Success false, SideEffects empty → read as "possible" (port.go:110-117) → stable failure, no panic (`TestNilNilResultIsStableFailure`, `TestSideEffectsZeroMeansLegacy`).
|
||||
- **Default configs:** U10 keeps default-account fallback only for hosts without the run Settings port (squad_native.go:60-66); `routeContractFor` logic untouched, only re-exported (`llm.go:604`).
|
||||
- **Retry semantics change is intentional and covered** (Note N3): transport failures from executors that do not declare `SideEffects=none` now fail fast instead of text-heuristic retry — exactly the F05/KTD4 remedy.
|
||||
- **Route fallback for legal matched intents:** `unconsumedCommandKinds` only fires when cancel/set_slot/chitchat/handoff commands are present; the rule funnel emits those only on expectation-hit/ambiguous branches, and matched+complete emits Start alone (intent/command.go:88-113), so normal command dispatch is unaffected (`TestValidSlotsReachHandlerMappedPerSchema`).
|
||||
|
||||
## Scope lens
|
||||
|
||||
All component diffs map to declared units: native (U1/U2/U3a), squad (U3b/U4), intent (U5, generator.go comment-only), command (U7a), backend (U6/U7b/U10 + mechanical `OpenRouteClient` rename and nested gitlinks). Dev-level changes are the task card, the plan document, and the five gitlinks. Out-of-unit but documented: squad `AGENT-EXECUTION.md` adapter disablement and command `AGENT-EXECUTION.md` addition (Notes N1/N2 — execution-infra config, explicitly recorded on the task card; no product code outside units). No undeclared product behavior changes found.
|
||||
|
||||
## Test lens
|
||||
|
||||
New tests assert the original trigger conditions, not just happy paths: F01 asserts zero writes for all three escape classes; F02 asserts no shared transcript, resume-with-matching-scope, and refusal to adopt identity-less stores; F04 covers torn tail, missing newline, mid-file error, repair-then-append; F05 covers patches-only success, failed-with-full-patches stays failed, canceled/aborted not retried, side-effect-gated retry bounds, all four recovery quadrants (record/files × present/missing), ExecutionID+deadline reuse; F06 covers tighten/loosen/factory-swap/tool-change/cache-reuse/parallel-first/in-flight-cap; F07 mutation attempts on both registries; F08/F09 missing vs invalid vs unknown-key vs required-param blocks with reasons; F10 unknown-with-ID, not-executed-stays-rejected, audit-failure-keeps-ID, repeated-confirm-once, cross-owner query scope; F13 owner-secret isolation, project layer, direct-route parity, default fallback.
|
||||
|
||||
## Verification runs (this review worktree)
|
||||
|
||||
Focused tests with `GOWORK=off`, per-directory, foreground:
|
||||
- deps/agent-command: `go test ./...` — ok (2 packages, includes new registry/result/conformance tests).
|
||||
- deps/agent-native: `go test ./runtime/session/ ./runtime/tools/` — ok (torn-JSONL, env whitelist, bounded buffer, writable allowlist suites). `adapters/native` — NOT_RUN here: nested deps (error-code-system etc.) are uninitialized in this review worktree and cannot be materialized offline; covered by the recorded green combination run.
|
||||
- deps/agent-intent, deps/agent-squad, modules/backend: NOT_RUN here for the same missing-nested-deps reason (backend imports all components). Covered by the host-recorded combination `make test` PASS on the batch candidate (verify-combo-make-test4.json, exit=0), which this review did not re-run per packet instruction.
|
||||
|
||||
## Verdict
|
||||
|
||||
REVIEW VERDICT: APPROVE
|
||||
|
||||
Blocking reasons: none. The diff implements the declared batch-1 units with trigger-condition regression coverage; the six minor findings (M1-M6) are quality follow-ups that do not block merge: none of them breaks a legal existing path, all have safe defaults (fail-closed), and the intentional behavior changes (retry tightening, cancel/no-retry semantics) are the specified F05 remedies with test coverage. Recommend filing M1-M5 as follow-up increments and restoring the temporarily disabled agent-execution adapters (N1) per the file's own instruction.
|
||||
@@ -0,0 +1,94 @@
|
||||
# 独立代码审查:fix/multi-instance-test-failure-diagnostics(多实例双进程验收的测试卫生修复)
|
||||
|
||||
- 审查对象:`5360884e9283d962caf945d944df05b5e5059177..2eaa7510848ba1fc13013f5b0a9ff1fd548b2071`(R0..HEAD,分支 fix/multi-instance-test-failure-diagnostics;测试侧变更落在 transport 提交 2eaa751)
|
||||
- 涉及文件(审查范围内):仅 `tests/integration/multi-instance/multi_instance_test.go`(+95/−17);范围内的另一文件 `.task/fix-multi-instance-test-failure-diagnostics.md` 属任务卡豁免范围,不在本审查内
|
||||
- 审查者身份:全新会话独立审查,未参与实现;本会话除本报告外不写入任何文件,全部只读
|
||||
- 背景核对:范围内 `git diff 5360884e92..HEAD -- Makefile .gitea/` 为空(0 行)——包级 `go test -count=1 -timeout 15m .`(Makefile:190)未被改动
|
||||
|
||||
## 结论:PASS(无阻塞项;6 项非阻塞发现)
|
||||
|
||||
两处修复意图均达成:owner_crash_takeover 的轮询从「到点静默退出 → POST 落到误导性 409」改为显式 `reconciled` 硬要求 + 定点失败;restart_invariants 在断言前先等恢复视图。逐条核对未发现断言被削弱,未发现超时预算放大,未发现实际的 e.a/e.b 解引用风险。
|
||||
|
||||
---
|
||||
|
||||
## 一、断言是否被削弱(重点 1)
|
||||
|
||||
逐 hunk 核对新旧文件(旧:5360884e92 版,新:HEAD 版):
|
||||
|
||||
1. **cross_event_sse**:仅 `t.Fatal(...)` → `t.Fatalf(..., lastSeq, instanceOf)`,select 的 8s 预算与事件分支条件(`!ok`、`ev.seq <= lastSeq`)逐字未动。**等价,仅增强诊断。**
|
||||
2. **owner_crash_takeover 轮询**:旧代码 `break` 后落到 POST,超时静默继续;新代码引入 `reconciled` 标志,未观察到即 `t.Fatalf`。判定语义与旧版一致(最后一个 run 的 status == "interrupted",经 `runStatuses` 取末位;类型断言失败时旧为 nil map 比较、新为空串,均判 false,等价)。**这是收紧而非削弱**:旧版存在一个能「未观察 interrupted 仍通过」的窄缝——最后一次 300ms 轮询之后、POST 之前对账恰好收口,POST 得 200 而通过,测试从未显式观察到 interrupted;新版关闭该缝,通过必须以观察到 interrupted 为前提。轮询粒度(300ms)与预算(`httpWait`=15s)不变。
|
||||
3. **restart_invariants**:新增前置等待,其后的既有断言(`msgs >= 5`、`lastSeq != 0`、SSE 全量重放 seq 严格连续、重启后 postMessage)逐字未动。**无削弱。**
|
||||
4. **waitConversationIdle / waitActiveHITL / waitRunStatus / waitHealthy**:成功条件逐字等价(idle 的 busy 白名单 pending/running/waiting_hitl 不变;HITL 的 `ok && len>0 && id!="" && runID!=""` 不变;waitRunStatus 的 `s == status` 不变;waitHealthy 的 200/401 不变);改动仅是新增 statuses/cardCount/observed/last 探针跟踪与更详细消息。`runStatuses` 把 `runs, ok := …` 的 ok 守卫换为 `runs, _ := …`,缺失/类型不符时返回空切片,与旧「ok=false 则不进循环」语义一致。
|
||||
5. **新增等待是否掩盖真实缺陷**:核对了「run 卡在 running」这一类真 bug 的走向——新等待会超时并在 Fatalf 中给出完整 statuses 列表(含卡住的 running),缺陷以测试失败形式暴露且可定位;旧代码同场景会落到后面 postMessage 的 `waitConversationIdle(20s)` 以笼统的 "still busy" 失败。**不存在把失败变成通过或无限等待的路径**(所有等待都有界且超时即 Fatalf)。唯一「弱化形状」见第二节发现 1:恢复视图判据的 interrupted 半边被历史 run 平凡满足,对「B 重启对账」回归不敏感——但该回归在改动前同样不被任何断言覆盖,覆盖无回退。
|
||||
|
||||
## 二、新增失败路径的信息是否足以定位(重点 2)
|
||||
|
||||
逐处核对新增 Fatalf 的字段:
|
||||
|
||||
- owner_crash_takeover:点名被轮询实例(`instanceOf(uc.api)` → "miA (http://addr)")、实际耗时 vs 预算(15s)、完整 statuses 序列、重启后 A 的日志。**足以定位。**
|
||||
- restart_invariants:点名轮询实例(miB)、耗时/预算、statuses、logA+logB 双日志。**足以定位。**
|
||||
- waitConversationIdle / waitActiveHITL / waitRunStatus / waitHealthy / 两处 SSE 超时:均点名实例与地址、给出耗时 vs 预算与最后观测值(statuses / cardCount / observed status / last probe / 期望 seq)。**充分。**
|
||||
|
||||
非阻塞发现:
|
||||
|
||||
- **发现 1(判据语义弱于注释声明)**:restart_invariants 等待注释称「对账收口的 interrupted run 可见」,但此刻会话里的 interrupted run 来自 owner_crash_takeover 中 **A** 重启时对 A 遗留 run 的收口(该子测试已硬性要求其存在并保持终态),而 B 被杀时并未持有任何 in-flight run(自 owner_crash_takeover 起全部活动在 A 上),**B 的重启对账在本场景无事可做**。因此该判据实际语义是「会话无 pending/running/waiting_hitl(且历史上存在 interrupted run)」,不能检出「B 启动对账回归」类缺陷。无害但注释高估了判据的覆盖面;且与 owner_crash_takeover 存在顺序耦合——未来若删改该子测试,本等待将在正常路径必然空转 15s 后失败(见发现 5 的脆弱性)。
|
||||
- **发现 2(非阻塞)**:waitActiveHITL / waitRunStatus 轮询 uc.apiB 时消息正确点名 B,但日志只附 logA;被轮询实例 B 的日志往往更相关。与改动前「只附 logA」的旧风格一致,非回归,建议后续补 logB。
|
||||
- **发现 3(非阻塞)**:restart_invariants 失败路径的 logA 是 owner_crash_takeover 重启后的 A 的日志;原 A(持有崩溃 run 时期)的日志在重启时已丢弃。此处崩溃 run 早已收口,影响有限,仅作知悉。
|
||||
|
||||
## 三、恢复视图判据本身是否正确(重点 3)
|
||||
|
||||
判据:∃ status == "interrupted" 且 ∀ status ∉ {pending, running, waiting_hitl}。
|
||||
|
||||
- **正常路径误判为未恢复的风险**:在本测试流内不存在——interrupted run 由 owner_crash_takeover 硬性保证存在且终态不可逆;唯一可能 busy 的 run 是「A resumes after restart」(mock runner,秒级收口),15s 预算充裕。当前流内**无误判风险**;跨子测试顺序耦合的脆弱性见发现 1/5。
|
||||
- **真实卡死误判为已恢复的风险**:卡死的 run 必处于 pending/running/waiting_hitl 之一 → busy=true → 不会误判。**但该 busy 集是白名单**:若 backend 未来引入新的非终态 status 字符串(如 finalizing/cancelling 之类),白名单外的非终态会被当作空闲而误判已恢复。这是与 waitConversationIdle 完全相同的既有语义(非本次引入),两处共享该潜在弱点。status 词汇表(pending/running/waiting_hitl/completed/failed/interrupted/cancelled)取自测试自身与改动前代码,未对照 backend 源码核验,**[未经证实]**。
|
||||
- 轮询经 `e.do`:非 200(含启动后瞬态 5xx)直接 Fatalf 终止轮询——与改动前该子测试的单次 GET 行为一致,失败是响亮且有信息的,可接受。
|
||||
|
||||
## 四、e.a/e.b 解引用风险(重点 4)
|
||||
|
||||
- `instanceOf`:先 `e.baseFor(c)` 再比较。`baseFor` 在 e.uc 非空且 c 为 apiB/streamB 时即解引用 `e.b.base`——因此 `instanceOf` 里的 `e.b != nil` 守卫实际不可达(e.b 为 nil 时 baseFor 先行 panic)。**实践无风险**:e.b 由 startPair 在任何 client 使用前赋值,两处重启均为非空重赋值;守卫属无害死代码(发现 4,非阻塞)。
|
||||
- base 串等值判定:`freePort` 保证 A/B 地址不同,且重启复用原 addr 字符串(`strings.TrimPrefix(e.b.base, "http://")`),重启后 `instanceOf` 对 apiB/streamB 仍正确映射到**新的** B。无误判面。
|
||||
- restart_invariants 中 `killNine(e.b)` → `startInstance` 之后 **e.a 仍有效**:e.a 未被触碰,指向 owner_crash_takeover 重启后的存活 A 进程;`uc.api` 经 baseFor 取 `e.a.base` 正确;`e.a.log` 是当前 A 实例的 lockedBuffer,非悬垂。无解引用风险。
|
||||
- `runStatuses` 对 nil map / 缺键 / 元素非 map 全部安全(带 ok 断言),失败路径打印不会引入二次 panic。
|
||||
|
||||
## 五、超时预算与包级 timeout(重点 5)
|
||||
|
||||
- `httpWait`(15s)、waitActiveHITL(15s)、waitHealthy(60s)、两处 SSE select(8s/15s)、waitConversationIdle 调用方(20s)全部未动。新增的 restart_invariants 等待复用 `httpWait`(≤15s,正常路径一次性成本),owner_crash_takeover 失败路径与旧版同界(旧:15s 空转 + POST 409;新:≤15s 定点失败)。**无预算悄悄放大。**
|
||||
- 包级 `go test -count=1 -timeout 15m .`(Makefile:190,由 CI test-pg job 调用)不在 diff 内:`git diff 5360884e92..HEAD -- Makefile .gitea/` 为 0 行。**未被改动。**
|
||||
|
||||
## 六、检测器可失败性验证(packet 约束)
|
||||
|
||||
按约束在 /tmp 构造 3 个测试语料未覆盖的早退读取器形态,直接调用 `tests.test_shell_pipe_hazard.hazards()` 并以真实 bash(`set -euo pipefail`)验证危害性:
|
||||
|
||||
| 形态(均不在既有 12 个测试的语料内) | hazards() | bash 实测 |
|
||||
|---|---|---|
|
||||
| `seq 1 200000 \| sed '5q;d'`(数字地址前缀的 sed q) | `[(1, "sed '5q;d'")]` 命中 | rc=141,无 "survived" |
|
||||
| `seq 1 200000 \| grep --max-count=3 1`(长选项 =值 形式) | `[(1, 'grep --max-count=3 1')]` 命中 | rc=141,无 "survived" |
|
||||
| `seq 1 200000 \| nice -5 head -1`(nice 旧式捆绑 -5 包装 head) | `[(1, 'nice -5 head -1')]` 命中 | rc=141,无 "survived" |
|
||||
|
||||
检测器对全部 3 个新形态均报出,且实测确为 SIGPIPE(141)——**检测器真能失败,非阻塞项**。配套只读套件 `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_shell_pipe_hazard`:Ran 12 tests, OK。探针脚本仅在 /tmp/mi-review-probe,仓库内无残留。
|
||||
|
||||
## 七、执行过的只读命令与结果(证据)
|
||||
|
||||
| 命令 | 结果 |
|
||||
|---|---|
|
||||
| `git status --porcelain=v2 --branch`、`git log --oneline -8` | detached HEAD 2eaa751;范围含 fd3ccd0(任务卡)、2eaa751(transport,含测试变更) |
|
||||
| `git diff 5360884e92..HEAD --stat` / `--name-only` | 仅 2 文件:任务卡 + multi_instance_test.go(+95/−17) |
|
||||
| `git diff 5360884e92..HEAD -- Makefile .gitea/ \| wc -l` | 0(包级 -timeout 15m 未动) |
|
||||
| `git show 5360884e92:tests/…/multi_instance_test.go`(对照读) | 新旧逐 hunk 等价性核对依据 |
|
||||
| `gofmt -l .`(tests/integration/multi-instance) | 无输出(格式干净) |
|
||||
| `go vet .`(tests/integration/multi-instance) | exit 0,无输出(可编译可 vet) |
|
||||
| `PYTHONDONTWRITEBYTECODE=1 python3 -m unittest tests.test_shell_pipe_hazard` | Ran 12 tests, OK |
|
||||
| /tmp/mi-review-probe/probe.py(hazards() + bash 141 实测) | 3/3 新形态命中,3/3 rc=141 |
|
||||
|
||||
**未验证项**:集成测试本体未运行(需 AGENT_PG_TEST_DSN + AGENT_BIN + 构建 backend,超出本只读审查范围)——两处修复在真实双进程下的运行时行为 NOT_RUN;status 词汇表未对照 backend 源码核验,标注 [未经证实]。
|
||||
|
||||
## 非阻塞发现汇总
|
||||
|
||||
1. restart_invariants 恢复视图判据的 interrupted 半边由历史 run 平凡满足,注释高估其覆盖面(不检出 B 重启对账回归;改动前同样不检出,覆盖无回退),并与 owner_crash_takeover 存在顺序耦合。
|
||||
2. busy 白名单(pending/running/waiting_hitl)对 backend 新增非终态 status 不设防——与 waitConversationIdle 相同的既有共享弱点,非本次引入。
|
||||
3. waitActiveHITL / waitRunStatus 轮询 B 时只附 logA,建议后续补 logB。
|
||||
4. `instanceOf` 的 `e.b != nil` 守卫不可达(baseFor 已先行解引用);无害死代码。
|
||||
5. restart_invariants 对 owner_crash_takeover 的顺序依赖:删改前者会使本等待在正常路径必然空转 15s 后失败(当前流内不触发)。
|
||||
6. 轮询经 `e.do` 对任意非 200 立即 Fatalf(含瞬态 5xx)——与改动前行为一致的响亮失败,可接受。
|
||||
|
||||
以上均不阻塞交付;本次变更可以进入 Review/合入流程(合入门禁按 policy/delivery.md 另行满足)。
|
||||
Vendored
+1
-1
Submodule deps/development-manager updated: 1e5ee55a28...2c2e372558
@@ -31,20 +31,20 @@ fi
|
||||
id dev >/dev/null 2>&1 || fail_expect "用户 dev 存在" "用户 dev 不存在"
|
||||
|
||||
sudo_list="$(sudo -l -U dev 2>&1 || true)"
|
||||
echo "${sudo_list}" | grep -q "NOPASSWD" || fail_expect "sudo -l -U dev 含 NOPASSWD" "${sudo_list}"
|
||||
grep -q "NOPASSWD" <<<"${sudo_list}" || fail_expect "sudo -l -U dev 含 NOPASSWD" "${sudo_list}"
|
||||
|
||||
if [ "${PLAYWRIGHT_BROWSERS_PATH:-}" != "/opt/ms-playwright" ]; then
|
||||
fail_expect "PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright" "${PLAYWRIGHT_BROWSERS_PATH:-<未设置>}"
|
||||
fi
|
||||
|
||||
chromium_path="$(
|
||||
find /opt/ms-playwright -type f \( -name headless_shell -o -name chrome \) 2>/dev/null | head -n 1
|
||||
find /opt/ms-playwright -type f \( -name headless_shell -o -name chrome \) 2>/dev/null | sed -n 1p
|
||||
)"
|
||||
|
||||
[ -n "${chromium_path}" ] || fail_expect "在 /opt/ms-playwright 下存在 headless_shell 或 chrome" "未找到可执行文件"
|
||||
[ -x "${chromium_path}" ] || fail_expect "chromium 可执行文件具有执行权限" "${chromium_path} 不可执行"
|
||||
|
||||
chromium_dom="$("${chromium_path}" --headless --no-sandbox --disable-gpu --dump-dom about:blank 2>&1 || true)"
|
||||
echo "${chromium_dom}" | grep -q "<html" || fail_expect "chromium --dump-dom 输出包含 <html" "${chromium_dom}"
|
||||
grep -q "<html" <<<"${chromium_dom}" || fail_expect "chromium --dump-dom 输出包含 <html" "${chromium_dom}"
|
||||
|
||||
echo "verify-env: PASS"
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
---
|
||||
title: feat: Clarify project access and lifecycle management
|
||||
date: 2026-10-19
|
||||
type: feat
|
||||
artifact_contract: ce-unified-plan/v1
|
||||
product_contract_source: ce-brainstorm
|
||||
execution: code
|
||||
---
|
||||
|
||||
## Goal Capsule
|
||||
|
||||
- **Objective:** Make project access understandable and enforceable: administrator authority stays outside job roles; the sole role permission is project access, which permits project creation; project creators with that permission manage their own project; members and project states determine whether work can continue; all fixed company templates are available to projects.
|
||||
- **Means:** Keep Backend authoritative for authenticated identity and job-role permission, and RDM authoritative for canonical project ownership, membership, project state, and template catalog. Recheck authorization at every read/write and asynchronous execution boundary.
|
||||
- **Authority:** The user's request in this conversation. Preserve the existing distinction between system administrator and RDM domain administrator. Product rules below govern when existing implementation or older proposals differ.
|
||||
- **Execution profile:** Split implementation into bounded Backend, RDM, and Frontend units, with host-owned integration, verification, review, and delivery under the repository's agent-per-unit rules.
|
||||
- **Stop conditions:** Stop when role permissions show only project access, project creator and state gates work for synchronous and in-flight operations, the complete fixed-template catalog is available, and focused verification passes. Do not implement document-duty assignment, production data changes, deployment, or unrelated permission redesign.
|
||||
- **Delivery owner:** Host owns cross-repository planning, scope review, authoritative verification, review, PR/CI, merge scheduling, and safe closure. Component work begins only after fresh source and branch checks.
|
||||
|
||||
## Product Contract
|
||||
|
||||
### Summary
|
||||
|
||||
The system's role editor exposes job-level system settings and user-management actions alongside project access, which makes role permission sound broader than the intended project boundary. In this product, administrator and super-administrator authority governs system settings, accounts, roles, and platform permissions. A job role has one product permission: project access. Users with effective project access may create projects, and only the creator with effective project access may administer a project. Project membership and project/member state separately govern conversation work. Projects expose the complete fixed company-template library; assignment of document authors, reviewers, and approvers is outside this increment.
|
||||
|
||||
### Problem Frame
|
||||
|
||||
Current code has a broader action catalog, Backend role-permission records, RDM membership rows, creator/owner checks, project archive/close fields, and a 44-item published DHF catalog. These mechanisms do not yet express one product rule: system administration is distinct from role permission; member removal is currently represented by deleting membership; project-wide and per-member work suspension are not the same as archival; and a complete template catalog is not the same as project-level template access. The result is confusing role configuration and inconsistent expectations about who can create or continue work.
|
||||
|
||||
### Actors
|
||||
|
||||
- **Super-administrator:** Holds the highest protected system authority according to the existing super-administrator contract.
|
||||
- **System administrator:** Manages the system actions explicitly assigned by the existing administrator contract. System administrator status alone does not grant project content access or project management.
|
||||
- **Project creator:** The immutable `createdBy` identity for one canonical RDM project. It can manage that project only while its effective project-access permission and account are valid.
|
||||
- **Active project member:** Can discover the project and create/use personal conversations while both project and member states permit work.
|
||||
- **Read-only former or suspended member:** Can read the historical material allowed by the applicable read boundary, but cannot create or continue work.
|
||||
|
||||
### Requirements
|
||||
|
||||
- **R1. Separate system authority from job roles.** Role configuration must not expose or grant system settings, account/user management, role administration, administrator appointment, or platform-permission management. Those actions remain in protected administrator/super-administrator authorization paths. Preserve the existing distinction between administrator levels; do not infer authority from an RDM domain administrator, project creator, or project member.
|
||||
- **R2. Reduce role permissions to project access.** Each role has one boolean project-access setting, whose meaning is permission to create a project. Unknown, disabled, unassigned, or denied roles cannot create projects. System administrator status by itself does not substitute for project access.
|
||||
- **R3. Enforce creator-scoped project management.** Project management requires a valid account, effective project access, and the exact authenticated actor matching the canonical project's immutable creator. These two project conditions are sufficient for the in-scope management actions: project settings, membership, and project/member work states. Existing per-action member/project overrides must not separately grant these management actions to another actor or block the eligible creator. A member with role `owner`, a system administrator, a caller-supplied owner field, or a host-project owner does not bypass the gate.
|
||||
- **R4. Preserve active-member work.** An active project member can see the project and create personal conversations. Existing conversations remain private according to the read boundary; membership does not itself publish another user's private uploads or drafts.
|
||||
- **R5. Make removal revoke work but retain allowed history.** Removing a member revokes project work immediately. The former member cannot create conversations, send messages, resume/retry work, approve an execution that would continue work, upload or mutate project data, or continue an existing conversation. The former member retains read-only access to historical material within the agreed historical visibility boundary. Preserve the revocation record so history remains auditable and re-adding does not erase prior removal.
|
||||
- **R6. Add project-wide work state.** An active project permits work only for members whose member state is active. A closed project permits no member to create or continue conversation work; existing readable conversations and documents remain available. The creator can still use project-management actions needed to reopen or administer the project.
|
||||
- **R7. Add per-member work state.** The creator can suspend or reactivate an individual member without removing membership. A suspended member remains discoverable/read-only within the same historical boundary but cannot create or continue conversation work. Project-wide closed state overrides an individually active state.
|
||||
- **R8. Enforce gates at every execution boundary.** Authorization must be rechecked before writes and before queued, resumed, confirmed, or delayed work causes side effects. A permission or state change that wins the race prevents the later operation. Read-only access must not accidentally resume a stream, run, or HITL action.
|
||||
- **R9. Integrate the complete fixed-template library.** Inventory and expose every fixed company template used in R&D and business phases, including the existing 44-item DHF source batch and any additional fixed-template source found during inventory. Preserve source identity, version/hash, phase, applicability, and format. A template can be selected and instantiated into a project where generation is supported; a project is not prepopulated with every template automatically.
|
||||
- **R10. Defer document-duty assignment.** Do not add, connect, or change assignment of document author/reviewer/approver duties for these templates in this increment. Do not alter existing duty history or make a template unavailable solely because new duty UI is deferred. Keep any existing behavior outside the changed paths intact.
|
||||
- **R11. Migrate without privilege expansion.** Existing administrator grants remain protected administrator authority. Existing role assignments that grant system/user actions cease to be effective through roles; they must not silently promote users to administrators. Existing valid project-access assignments are preserved. Any ambiguous legacy owner, membership, or history visibility remains explicit and fails closed rather than being guessed.
|
||||
- **R12. Keep project data and lifecycle history.** Closing a project or suspending a member is reversible and does not delete projects, conversations, documents, or membership history. Removal is not archive or account deletion.
|
||||
- **R13. Let authorized role managers configure a new role's project permission.** An actor allowed to manage roles can edit the role's single project-access permission immediately after creation. The role editor must not require the separate `system.permissions.manage` authority to set that one job-role permission. Actors without role-management authority remain read-only, and direct API writes remain protected. System grants and administrator-owned platform limits continue to require `system.permissions.manage`.
|
||||
|
||||
### Scope Boundaries
|
||||
|
||||
**In scope:** role editor and server role validation; system/admin action sources; project creation and creator authorization; creator-only project settings, membership, and work-state management; project membership active/suspended/removed states; project active/closed state; historical read-only boundary after suspension/removal; all fixed-template inventory and project availability; API, UI, migrations, audit, and tests required for these behaviors.
|
||||
|
||||
**Out of scope:** implementing or newly integrating document-duty assignment; redesigning document author/review/approval workflow; exposing private content to system administrators; changing archive/restore behavior; project transfer to another creator; project deletion; automatic creation of every template instance; changing global project-name uniqueness; production migration, release/tag publication, deployment, and live-data repair.
|
||||
|
||||
**Deferred to Follow-Up Work:** assigning author/reviewer/approver responsibilities per fixed template; transfer of project creator ownership; richer per-member project action matrices beyond active/suspended work state; template fidelity remediation beyond current supported generation contracts.
|
||||
|
||||
### Key Flows
|
||||
|
||||
- **F1. Create a project:** An enabled user with effective project access creates a canonical project. The server records the authenticated creator; the client cannot choose or replace the creator.
|
||||
- **F2. Manage a project:** The creator with effective project access changes settings, membership, or state. A non-creator, a creator whose project access has been removed, or an administrator without project access is denied.
|
||||
- **F3. Add a member:** The creator adds an enabled account. The member sees the project and can create personal conversations only when the project and member are active.
|
||||
- **F4. Suspend a member:** The creator suspends the member. Reads remain within the historical boundary; every new or continued work action is denied. Re-activation restores the active-member path without erasing history.
|
||||
- **F5. Remove a member:** The creator removes the member. The work gate closes and removal is retained as history; the former member can only read permitted historical material.
|
||||
- **F6. Close or reopen a project:** The creator closes the project; all members become read-only for project work. Reopening restores work to members whose own state is active.
|
||||
- **F7. Use a fixed template:** A project member browses the complete company-template catalog, selects a template, and creates an instance only through supported generation. Duty assignment is not part of this flow.
|
||||
|
||||
### Acceptance Examples
|
||||
|
||||
- **AE1.** A normal user without project access receives a denial when creating a project; a user with project access succeeds and is recorded as creator.
|
||||
- **AE2.** A user with system-admin rights but without project access cannot create or administer a project. The same user can perform only the system actions allowed by the administrator contract.
|
||||
- **AE3.** A member with project access but who did not create project A cannot manage A or project B. Project A's creator cannot manage B.
|
||||
- **AE4.** An active member sees the shared project and creates a personal conversation. Other members do not gain access to that conversation merely through membership.
|
||||
- **AE5.** After removal or suspension, a member can read allowed existing history but every new or continued work action is rejected, including retry, resume, queued continuation, and confirmation that would cause execution.
|
||||
- **AE6.** Closing project A blocks work for all members and preserves read access; reopening A restores work only for active members.
|
||||
- **AE7.** Suspending one member blocks only that member's work; other active members continue while the project is active. Closing the project blocks all members regardless of individual state.
|
||||
- **AE8.** The project catalog accounts for every fixed company template from the approved source inventory, including all 44 DHF assets. Duplicate bytes with distinct source identities remain distinct catalog entries. No project is silently seeded with 44 document instances.
|
||||
- **AE9.** Role configuration cannot set a system setting, user-management, administrator, or platform-permission action. These actions remain available only through their protected admin paths.
|
||||
- **AE10.** SQLite and PostgreSQL produce equivalent authorization, concurrency, and migration outcomes; cross-project and post-revocation negatives remain denied.
|
||||
- **AE11.** An authorized role manager creates a role and can immediately enable or disable its single project-access permission. A role manager who lacks `system.permissions.manage` still succeeds; an actor without `system.roles.manage` cannot edit the permission through the UI or API. System-grant and platform-limit writes remain denied without `system.permissions.manage`.
|
||||
|
||||
### Success Criteria
|
||||
|
||||
- Role configuration has one understandable project-access choice and no configurable system/user actions.
|
||||
- A role manager can create a role and configure its project-access choice without a separate platform-permission-management grant.
|
||||
- Project creation and management follow the exact effective-project-access plus creator rule.
|
||||
- Project/member state transitions have predictable, reversible read-only behavior, including for in-flight and queued work.
|
||||
- Every fixed company template is discoverable with traceable source/version/phase metadata; supported selection creates a project instance without introducing duty assignment.
|
||||
- Existing private-content, project-isolation, system-admin, and super-admin boundaries remain intact.
|
||||
|
||||
## Planning Contract
|
||||
|
||||
### Key Technical Decisions
|
||||
|
||||
- **KTD1. Keep admin authority outside roles.** Keep super-administrator and system-administrator checks in the existing trusted system-authority path. Role permission APIs and UI expose only the project-access capability. Reuse current administrator-level distinctions rather than creating role-based substitutes.
|
||||
- **KTD2. Define one effective project-access capability.** A single role boolean determines whether an enabled account with an enabled assigned role can create projects. The legacy `allowed`/`capAllowed` pair is only a storage compatibility shape for this action: normalize old records from their prior effective result (`allowed && capAllowed`), and write both fields from the new single boolean. Do not show or authorize an independent role cap for project creation. Administrator status alone is not an implicit project grant. The same effective capability and exact creator identity are the complete authorization rule for in-scope project management; existing per-action project overrides cannot add another manager or impose a second management gate. Preserve explicit deny/fail-closed behavior when role/provider data is missing or stale.
|
||||
- **KTD3. Separate project membership, member work state, and project work state.** Retain canonical RDM ownership/membership as domain authority. Represent `active`, `suspended`, and `removed` member history separately from `active`/`closed` project work state. Effective work requires an enabled account, active membership, active member state, and active project state.
|
||||
- **KTD4. Preserve read and work authorization as different checks.** Membership revocation/suspension and project closure block all work paths immediately while leaving only the explicitly allowed read paths. Recheck current state at execution/side-effect boundaries rather than relying on a UI state or a conversation's creation-time authorization.
|
||||
- **KTD5. Use the published company-template catalog as the source of truth.** Include all inventoried fixed company templates in the project catalog. Keep source assets and template instances distinct. Instantiate on explicit selection, not by bulk-seeding every project. Preserve unsupported fidelity/generation states as visible limitations; catalog presence alone does not claim full-fidelity rendering.
|
||||
- **KTD6. Use conservative historical visibility.** Unless the user chooses a broader boundary during this planning conversation, retain only the history the member could already read before suspension/removal; do not make another member's private conversation or upload newly visible. Capture the chosen boundary in the final plan and tests.
|
||||
- **KTD7. Keep this increment reversible and local.** No deletion or destructive data migration is needed. Preserve audit/history, run local verification, and keep deployment and production data repair outside this plan.
|
||||
|
||||
### High-Level Technical Design
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Authenticated actor] --> B[Backend admin authority]
|
||||
A --> C[Backend job role: project access]
|
||||
C -->|create permitted| D[RDM canonical project]
|
||||
C -->|plus exact createdBy| E[Project management]
|
||||
D --> F[Member state: active / suspended / removed]
|
||||
D --> G[Project state: active / closed]
|
||||
F --> H{Work gate}
|
||||
G --> H
|
||||
H -->|allowed| I[Create or continue conversation work]
|
||||
H -->|denied| J[Historical read-only projection]
|
||||
D --> K[Complete fixed-template catalog]
|
||||
K -->|explicit selection| L[Supported project document instance]
|
||||
```
|
||||
|
||||
Project-management authorization and project-work authorization are separate decisions. A closed project may still be administered by its eligible creator, while its members remain read-only. A suspended or removed member never gains creator-management capability through historical read access. Archival remains a separate existing lifecycle and is not changed by this plan.
|
||||
|
||||
### Implementation Notes
|
||||
|
||||
- Backend owns authenticated actor identity, role assignment, administrator actions, and effective project-access resolution. RDM owns canonical project creator, membership history, project/member work states, and template catalog consumption. Frontend is a projection, not an authorization source.
|
||||
- Existing project and conversation data may live in different stores. Any write gate crossing Backend and RDM must use the established public extension contracts and preserve account-write fencing; do not assume one transaction spans both stores.
|
||||
- RDM currently stores `rdm_members` rows by `(project_id, owner)` and removal deletes the row. It has creator checks, owner/member roles, archive/closed fields, and project action caps. The new contract must not mistake project owner role, archived state, or RDM domain-admin state for the requested rule.
|
||||
- The RDM checkout inspected for this plan is 15 commits behind its remote-tracking `main`; the Backend RDM pin and fixed-template inventory must be refreshed before implementation. Current local source is planning evidence, not a delivery candidate.
|
||||
|
||||
### Sources & Research
|
||||
|
||||
- `modules/backend/internal/authz/catalog/catalog.go`, `modules/backend/internal/authz/service.go`, and `modules/backend/internal/server/server.go`: current catalog separates protected system actions from the special `project.create` role decision; authenticated system administrators currently bypass that decision, which this plan changes.
|
||||
- `modules/backend/internal/store/role_policy.go` and `modules/frontend/src/components/RolePermissionsPanel.tsx`: current role policy storage and role-permission editor to be narrowed to project access.
|
||||
- `modules/backend/internal/server/roles.go` and `modules/frontend/src/components/RolePermissionsPanel.tsx`: role creation/listing use `system.roles.manage` / `system.roles.view`, but role permission catalog and writes plus checkbox enablement currently require `system.permissions.view` and `system.permissions.manage`. This lets a delegated role manager create a role while leaving every permission checkbox disabled. This source path grounds R13; live account grants were not inspected.
|
||||
- `modules/documents/docs/proposals/2026-10-13-system-project-membership.md`: prior approved boundary for RDM project identity, Backend projections, member-created private conversations, and project/resource privacy. Its privacy rules remain unless the user explicitly changes the historical scope in this plan.
|
||||
- `modules/documents/docs/proposals/2026-10-15-iam-focused-role-permission-spec.md`: current administrator, role, and project permission contracts; the present role model is broader than the user's requested product model.
|
||||
- `modules/documents/docs/records/2026-10-14-rdm-two-trees-latest-templates/rdm-two-trees-requirements-r1.md` and `modules/documents/docs/proposals/2026-10-14-rdm-two-trees-latest-templates.md`: 44-source DHF inventory and current known distinction between catalog coverage and rendering fidelity.
|
||||
- `/home/weichen/projects/Agent-System-RDM/internal/members/members.go`, `internal/rdmapp/projects_api.go`, `internal/rdmapp/system_projects.go`, and `internal/rdmapp/templates/dhf/catalog.json`: inspected RDM membership deletion, creator management, active work checks, and template catalog. This checkout is behind remote `main` and must be refreshed before implementation.
|
||||
- No external research was used; existing Backend and RDM authorization and template contracts provide direct local patterns.
|
||||
|
||||
### Alternatives Considered
|
||||
|
||||
- **Keep all existing system actions in the role matrix but lock them:** Rejected because it preserves the misleading product model and makes role configuration look capable of controlling admin authority.
|
||||
- **Use project membership alone as management authority:** Rejected because a member or delegated project-owner role must not acquire the creator's project-management right.
|
||||
- **Delete membership and infer history from conversation ownership:** Rejected because it loses explicit revocation history and cannot represent per-member suspension cleanly.
|
||||
- **Close a project by archiving it:** Rejected because closure must be reversible and read-only while preserving a distinct archival lifecycle.
|
||||
- **Instantiate every template at project creation:** Rejected because it creates unused documents and duties/approval expectations the user explicitly deferred.
|
||||
|
||||
### Deferred Planning Questions
|
||||
|
||||
- The user's answer on removed-member historical visibility is pending. The plan currently assumes only pre-revocation visible history remains readable. If the user wants all project conversations/documents visible after removal, update the privacy requirements and acceptance tests before implementation.
|
||||
- Exact fixed-template source inventory beyond the 44-item DHF batch must be verified against the current RDM source and company template directory when component baselines are refreshed. Missing, duplicate, unsupported, or unreviewed entries must be listed rather than silently omitted.
|
||||
- Implementation must verify how active runs, delayed commands, streamed events, uploads, and HITL confirmations map onto the current Backend/RDM public contracts; the required outcome is specified, but exact handler names are implementation-time discovery.
|
||||
|
||||
## Implementation Units
|
||||
|
||||
### U1. Restrict role permissions to project access
|
||||
|
||||
**Goal:** Remove system settings, account/user, administrator, and platform-permission actions from role configuration while preserving protected administrator behavior.
|
||||
|
||||
**Requirements:** R1, R2, R9, R11, R13
|
||||
|
||||
**Dependencies:** None
|
||||
|
||||
**Files:**
|
||||
- `Agent-System-Dev/modules/backend/internal/authz/catalog/catalog.go`
|
||||
- `Agent-System-Dev/modules/backend/internal/authz/service.go`
|
||||
- `Agent-System-Dev/modules/backend/internal/store/role_policy.go`
|
||||
- `Agent-System-Dev/modules/backend/internal/store/migrate.go`
|
||||
- `Agent-System-Dev/modules/backend/internal/server/roles.go`
|
||||
- `Agent-System-Dev/modules/backend/internal/server/role_authorization_test.go`
|
||||
- `Agent-System-Dev/modules/frontend/src/components/RolePermissionsPanel.tsx`
|
||||
- `Agent-System-Dev/modules/frontend/src/rolePermissions.ts`
|
||||
- Related Backend and Frontend permission tests
|
||||
|
||||
**Approach:**
|
||||
|
||||
1. Keep protected system actions in admin/super-admin authorization. Restrict job-role permission read/write/catalog views to one project-access boolean and label it by its actual meaning. The UI has one checkbox, not separate default and cap controls.
|
||||
2. Authorize this role field using role-view/manage authority. Do not require `system.permissions.manage` to edit a job-role definition. Keep system-grant and platform-limit writes under `system.permissions.manage`.
|
||||
3. Enforce the same split in the Backend API: role managers can read the project-access descriptor and replace that role value; handcrafted writes to protected system/user actions remain rejected. Map the boolean to both legacy storage columns and authorize from the canonical boolean result.
|
||||
4. Ensure system administrator status does not bypass the effective project-access check for project creation. Keep role/provider errors, disabled role/account, and unknown actions denied.
|
||||
5. Migrate old role permission rows so system/user actions are ignored for role authorization and not shown as role capabilities. Do not convert them into administrator grants. Preserve the prior effective project-access result when normalizing `allowed` and `capAllowed`; retain explicit administrator grants unchanged.
|
||||
|
||||
**Test scenarios:**
|
||||
|
||||
- A role containing old system settings/user action rows cannot authorize those actions and the role API/UI omits them.
|
||||
- An administrator without project access cannot create a project; an eligible role can.
|
||||
- A system setting/user action grant through a role-permission write is rejected atomically.
|
||||
- A user with `system.roles.manage` but without `system.permissions.manage` creates a role, enables its project access, saves successfully, reloads the role, and sees the saved value.
|
||||
- A user with `system.roles.view` but without `system.roles.manage` can read the project-access value but sees it read-only; direct role-permission writes are rejected.
|
||||
- A user without `system.permissions.manage` still cannot change system grants or platform limits.
|
||||
- A legacy role with protected rows migrates without promoting its members or losing valid project access.
|
||||
- An existing project role with `allowed=true, capAllowed=false` remains effectively denied after normalization; one with both fields true remains allowed.
|
||||
- A missing/incompatible role provider denies project creation.
|
||||
|
||||
**Verification:** Backend role-policy/catalog/authorization migration tests and Frontend role editor tests prove the separated role-management and platform-permission gates; current admin and super-admin tests pass.
|
||||
|
||||
### U2. Enforce project creator management authority
|
||||
|
||||
**Goal:** Require effective project access and exact canonical creator identity for every project-management action.
|
||||
|
||||
**Requirements:** R2, R3, R11
|
||||
|
||||
**Dependencies:** U1
|
||||
|
||||
**Files:**
|
||||
- `Agent-System-RDM/internal/rdmapp/projects_api.go`
|
||||
- `Agent-System-RDM/internal/rdmapp/system_projects.go`
|
||||
- `Agent-System-RDM/internal/perms/role_provider.go`
|
||||
- `Agent-System-RDM/internal/perms/perms.go`
|
||||
- Related project creator and permission tests
|
||||
|
||||
**Approach:**
|
||||
|
||||
1. Centralize a management check using authenticated actor, canonical project ID, immutable `createdBy`, current account/role project access, and project existence.
|
||||
2. Apply that complete check to project settings, member management, and project/member work-state management. Existing per-action overrides cannot grant management to non-creators or further restrict the eligible creator. Do not change archive/restore, which remains a separate lifecycle.
|
||||
3. Preserve separate project-resource authorization for ordinary member reads and conversation work. Do not grant management because a member has an `owner` membership label or an action override.
|
||||
4. Keep account disable and project-access revocation race-safe using existing write fences or equivalent transactional rechecks.
|
||||
|
||||
**Test scenarios:**
|
||||
|
||||
- The creator with project access manages their own project.
|
||||
- The creator after losing project access is denied management.
|
||||
- A non-creator member, project `owner` role holder, system administrator, and RDM domain administrator are each denied unless they independently meet the exact creator rule.
|
||||
- The eligible creator can manage in-scope project settings, membership, and state even when a legacy per-action override denies them; a non-creator cannot manage when an override allows them.
|
||||
- Cross-project IDs, host/RDM ID confusion, and caller-supplied owner substitutions are denied.
|
||||
- Concurrent account disable or permission revocation prevents a later management write from committing.
|
||||
|
||||
**Verification:** Focused RDM project authorization tests pass on SQLite and PostgreSQL; existing project privacy and host-projection tests remain green.
|
||||
|
||||
### U3. Preserve membership history and read-only removal
|
||||
|
||||
**Goal:** Make member removal and suspension revoke all project work immediately while retaining only authorized historical reads.
|
||||
|
||||
**Requirements:** R4, R5, R8, R12
|
||||
|
||||
**Dependencies:** U2. The historical visibility scope is a blocking product decision before U3 implementation; the current plan records KTD6 as the conservative default.
|
||||
|
||||
**Files:**
|
||||
- `Agent-System-RDM/internal/members/members.go`
|
||||
- `Agent-System-RDM/internal/rdmapp/projects_api.go`
|
||||
- `Agent-System-RDM/internal/rdmapp/system_projects.go`
|
||||
- `Agent-System-RDM/internal/rdmapp/private_doc_views.go`
|
||||
- `Agent-System-RDM/internal/rdmapp/iam_i1_privacy_test.go`
|
||||
- `Agent-System-Dev/modules/backend/internal/server/server.go`
|
||||
- Relevant conversation read/write, resource authorization, and async-work tests
|
||||
|
||||
**Approach:**
|
||||
|
||||
1. Replace hard deletion as the authorization source with retained membership/revocation state and timestamps while preserving current member-list semantics for active members.
|
||||
2. Implement the settled historical read boundary independently from work authorization. Do not expose another user's private conversation/upload unless the user explicitly chooses that scope.
|
||||
3. Recheck current membership state for conversation creation, message writes, run/retry/resume, upload/mutation, HITL continuation, queued work, and streamed side effects.
|
||||
4. On removal or suspension, invalidate or stop pending operations where they would otherwise continue producing side effects; reads must remain non-mutating.
|
||||
5. Re-adding a member creates a new active period or reactivates with an auditable transition; preserve previous removal history.
|
||||
|
||||
**Test scenarios:**
|
||||
|
||||
- Removed/suspended actor can read allowed historical items but cannot create a conversation or mutate a prior conversation.
|
||||
- Removed/suspended actor cannot retry, resume, upload, send, approve-to-execute, or cause a queued run to continue.
|
||||
- An operation authorized before revocation but attempting its side effect after revocation is rejected.
|
||||
- Another active member's private conversation and original upload remain hidden under the conservative visibility choice.
|
||||
- Re-adding restores active work without deleting or rewriting prior membership history.
|
||||
|
||||
**Verification:** Backend and RDM read/write boundary tests cover the exact revocation race and preserve private-content negatives.
|
||||
|
||||
### U4. Add project-wide and per-member work states
|
||||
|
||||
**Goal:** Support reversible project closure and per-member suspension with consistent read-only behavior.
|
||||
|
||||
**Requirements:** R3, R6, R7, R8, R12
|
||||
|
||||
**Dependencies:** U2, U3
|
||||
|
||||
**Files:**
|
||||
- `Agent-System-RDM/internal/projects/projects.go`
|
||||
- `Agent-System-RDM/internal/members/members.go`
|
||||
- `Agent-System-RDM/internal/rdmapp/projects_api.go`
|
||||
- `Agent-System-RDM/internal/rdmapp/system_projects.go`
|
||||
- `Agent-System-RDM/internal/perms/perms.go`
|
||||
- Related project/member migration and state-transition tests
|
||||
- Related Backend conversation/async execution authorization tests
|
||||
|
||||
**Approach:**
|
||||
|
||||
1. Add explicit reversible project work state `active|closed` and member work state `active|suspended`, leaving archival and historical removal distinct.
|
||||
2. Define the work predicate as enabled account AND active member AND active member state AND active project state. Historical read access is evaluated separately.
|
||||
3. Permit the eligible creator to reopen/manage a closed project while denying all member work. Per-member suspension affects only that member; project closure overrides every member state.
|
||||
4. Enforce current state before every work side effect and on in-flight/queued continuations. Persist version/audit data so stale UI requests and delayed responses cannot restore old state.
|
||||
5. Keep transitions idempotent and reversible. Do not delete or rewrite conversations, project data, or membership history.
|
||||
|
||||
**Test scenarios:**
|
||||
|
||||
- Closing a project blocks new and existing conversation work for every member while preserving existing reads.
|
||||
- Reopening restores work only for active members.
|
||||
- Suspending one member blocks only that member; other active members continue normally.
|
||||
- Closing a project blocks a member even when their individual state is active.
|
||||
- State changes concurrent with message send, queued execution, retry, and confirmation prevent post-close side effects.
|
||||
- Creator management remains available on a closed project but is denied when creator permission/account is invalid.
|
||||
|
||||
**Verification:** RDM state and migration tests pass on SQLite/PostgreSQL; Backend integration tests prove the canonical RDM state gates all conversation work paths.
|
||||
|
||||
### U5. Expose the complete fixed-template library to projects
|
||||
|
||||
**Goal:** Ensure every fixed company template across business and R&D phases is cataloged and available for explicit project use without adding document-duty assignment.
|
||||
|
||||
**Requirements:** R9, R10
|
||||
|
||||
**Dependencies:** None for inventory; project access read boundary from U3/U4 for project exposure
|
||||
|
||||
**Files:**
|
||||
- `Agent-System-RDM/internal/domain/template_asset.go`
|
||||
- `Agent-System-RDM/internal/rdmapp/dhf_catalog.go`
|
||||
- `Agent-System-RDM/internal/rdmapp/templates/dhf/catalog.json`
|
||||
- `Agent-System-RDM/internal/rdmapp/dhf_template_instance.go`
|
||||
- `Agent-System-RDM/internal/rdmapp/dhf_catalog_test.go`
|
||||
- `Agent-System-RDM/internal/rdmapp/dhf_template_instance_test.go`
|
||||
- Source inventory and generated catalog tests
|
||||
|
||||
**Approach:**
|
||||
|
||||
1. Refresh source inventory and verify the fixed-template set from company sources; retain all 44 DHF source identities and include other fixed templates if present.
|
||||
2. Reconcile source path/hash, template ID/version, business/R&D phase, applicability, format, publication/readiness, and supported generation status. Do not replace templates with the 69 legacy skeleton types or infer missing mappings from similar names.
|
||||
3. Connect the complete catalog to project-level browse/select behavior. Instantiate only a selected template and preserve exact source/version provenance.
|
||||
4. Surface blocked or unsupported fidelity/generation status honestly. Catalog inclusion does not claim that every `.doc` or render profile is fully faithful.
|
||||
5. Do not add or invoke assignment of author/reviewer/approver duties, approval flows, or new lifecycle transitions.
|
||||
|
||||
**Test scenarios:**
|
||||
|
||||
- The catalog has one traceable entry for every inventoried source, including duplicate-byte files with distinct source identities.
|
||||
- All fixed business and R&D phase templates appear in the project template browser with source/version/phase metadata.
|
||||
- An explicitly selected supported template creates one instance with exact source/version provenance.
|
||||
- Unsupported/blocked templates are visible with their real state and cannot be represented as successfully generated.
|
||||
- No project creation path automatically creates all template instances or duty assignments.
|
||||
|
||||
**Verification:** Template source-inventory, catalog, supported-generation, and project integration tests prove completeness and provenance for the refreshed source snapshot.
|
||||
|
||||
### U6. Present role, member, and project-state controls
|
||||
|
||||
**Goal:** Make the permission and read-only states clear in the existing admin, role, project, and member interfaces.
|
||||
|
||||
**Requirements:** R1-R10, R13
|
||||
|
||||
**Dependencies:** U1-U5 API contracts
|
||||
|
||||
**Files:**
|
||||
- `Agent-System-Dev/modules/frontend/src/components/RolePermissionsPanel.tsx`
|
||||
- `Agent-System-Dev/modules/frontend/src/components/AccountUsersPanel.tsx`
|
||||
- `Agent-System-Dev/modules/frontend/src/components/TaskSidebar.tsx`
|
||||
- Existing project settings/member components
|
||||
- `Agent-System-Dev/modules/frontend/test/role-permissions*.test.ts`
|
||||
- Relevant project access and browser tests
|
||||
- `Agent-System-Dev/DESIGN.md`
|
||||
|
||||
**Approach:**
|
||||
|
||||
1. Role UI shows one project-access checkbox to authorized role managers. A newly created role is selected with an editable project-access checkbox when the actor has `system.roles.manage`; unrelated `system.permissions.manage` state does not disable it. Read-only users receive a clear reason.
|
||||
2. System/admin actions appear only in the protected system-administration area and reflect the current super-admin/admin contract.
|
||||
3. Project management controls are visible and accepted only for the creator with project access. The UI uses service-provided state; server authorization remains authoritative.
|
||||
4. Show project closed, member suspended, and removed/read-only states distinctly, with write controls disabled or absent while read access remains.
|
||||
5. Surface the fixed-template catalog and explicit selection state. Do not add document-duty assignment UI.
|
||||
6. Follow the existing UI design tokens, responsive behavior, accessibility, and async sequence ownership in `DESIGN.md`.
|
||||
|
||||
**Test scenarios:**
|
||||
|
||||
- Role users see only the project-access control; admins see only their authorized admin actions.
|
||||
- A role manager without `system.permissions.manage` sees an editable project-access checkbox after creating a role and can save/reload its value; a read-only role viewer gets a clear explanation and cannot submit a direct write.
|
||||
- The role editor does not grant the same actor access to system-grant or platform-limit changes.
|
||||
- A non-creator cannot discover active project-management controls, and a direct API request remains denied.
|
||||
- Active project/member affordances work; suspended/closed/removed states show read-only behavior with all work actions blocked.
|
||||
- A late response cannot restore active controls after account switch, state transition, or membership revocation.
|
||||
- Template browsing shows all catalog entries while no duty-assignment control is introduced.
|
||||
|
||||
**Verification:** Frontend typecheck/unit tests and focused Playwright flows pass at desktop and narrow viewport sizes; design guard remains green.
|
||||
|
||||
## System-Wide Impact
|
||||
|
||||
- **Users:** Role editing becomes smaller; project creation and management become predictable; removed/suspended members retain read-only history but cannot resume work.
|
||||
- **Administrators:** System/user duties remain in protected admin surfaces, separate from job-role permissions. System administrators do not gain project contents by virtue of their status.
|
||||
- **RDM:** Remains authoritative for canonical projects, membership/state, template catalog, and domain content. Member deletion, project closure, and creator management checks require coordinated domain/API updates.
|
||||
- **Backend:** Continues to own identity and role authority and must fence conversation writes and continuations using current authorization, not UI claims.
|
||||
- **Compatibility:** Existing role data, active membership, project archive/close fields, host-project projections, conversation privacy, and template identities require explicit migration/compatibility tests. Dev's RDM pin must be aligned only through the normal reviewed cross-repository process.
|
||||
|
||||
## Verification Contract
|
||||
|
||||
| Scope | Verification | Proves |
|
||||
|---|---|---|
|
||||
| Backend role authority | Focused `go test` for `internal/authz`, `internal/store`, and `internal/server` role/authz tests | Protected system actions remain admin-only; role project access gates creation; migrations do not elevate users |
|
||||
| RDM authorization and state | Focused tests for `internal/perms`, `internal/members`, `internal/projects`, and `internal/rdmapp` | Creator identity, member/project states, removal history, privacy and execution gates |
|
||||
| Database portability | Existing isolated PostgreSQL test harness for affected Backend/RDM migration and concurrency tests | SQLite/PostgreSQL parity, conflict handling, and transaction fencing |
|
||||
| Template inventory | Catalog/source/hash checks plus supported generation tests | Complete fixed-template coverage with exact source provenance and honest fidelity state |
|
||||
| Frontend | Focused Vitest/typecheck and browser tests for role, project, member, status, and template flows | Correct affordances and read-only behavior across real UI transitions |
|
||||
| Integrated candidate | `make test-worktree`, `scripts/check-pins.sh`, and the applicable Dev composition suite once on the final fixed candidate | Exact cross-repository composition is buildable and matches reviewed component pins |
|
||||
| Delivery | Independent review, exact-head required CI, PR base/target checks and task evidence under `policy/delivery.md` and `policy/governance.md` | Reviewed candidate is deliverable; local SELF results are not presented as CI or runtime acceptance |
|
||||
|
||||
No deployment, production database migration, live permission rewrite, or business-account acceptance is included. Runtime behavior remains `NOT_RUN` until separately authorized and validated against the exact deployed composition.
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- Role permission API and UI offer only project access; system settings, user/account, role, and platform-permission actions remain admin/super-admin controlled.
|
||||
- Project creation requires effective project access; project management additionally requires exact canonical creator identity and current enabled state.
|
||||
- Active members can see projects and create personal conversations. Suspended/removed members and closed projects reject all new and continued work while preserving the agreed read-only history.
|
||||
- Authorization is rechecked for synchronous, queued, resumed, retried, streamed, and confirmation-triggered side effects.
|
||||
- Every fixed company template is inventoried and available to projects with traceable identity, phase, version, and supported-generation status; specific template instances are created only on selection.
|
||||
- No document-duty assignment functionality is added or newly connected; existing private data and duty history remain unchanged.
|
||||
- Focused Backend, RDM SQLite/PostgreSQL, Frontend/browser, and exact Dev composition checks pass for the final candidate; independent review and required exact-head CI pass before merge.
|
||||
- No deployment, production data change, release/tag publication, or live-user permission adjustment is performed under this plan.
|
||||
@@ -0,0 +1,73 @@
|
||||
# 快慢通道 O2 后续:A01 检查点证据合规 实施与测试计划
|
||||
|
||||
- 状态:**计划稿,待人审**(与[方案](../specs/2026-10-20-fast-slow-o2-a01-evidence-design.md)同批评审)。人审通过前不派 worker、不改产品代码。
|
||||
- 日期:2026-10-20;作者:host;来源等级:SELF。
|
||||
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md);worker 不提交、不推送、不开 PR;host 负责范围审阅、权威验证、PR/CI/合入。
|
||||
- 付费与真实模型:I1/I2 零付费;P2 付费复验按资源提案单独批准。
|
||||
|
||||
## 1. 交付切分与顺序
|
||||
|
||||
| 增量 | 仓 | 内容 | 依赖 | 合入方式 |
|
||||
|---|---|---|---|---|
|
||||
| I1 | Backend | 说明/提示收紧(方案 §3.1)+ 评测允许修正一次与新标签(§3.2) | 无 | 组件仓 PR,当前 HEAD CI 通过后合入 |
|
||||
| I2 | Dev | `modules/backend` gitlink 前移到 I1 合入提交 | I1 | Dev PR,`make test` 组合验证 + 合入窗口 |
|
||||
| P2 | 证据目录 | 一次 A01 付费复验 | I2 + 用户批准资源提案 | 不入仓;结论写回后续任务卡 |
|
||||
|
||||
I1 是一个可独立实施的单元:文案与评测两处改动都在 Backend 且共享同一验证命令,拆开只会增加一次派遣与一次 CI。
|
||||
|
||||
## 2. I1 Backend(单元 `o2a01e-i1-backend-evidence-guidance`)
|
||||
|
||||
**允许路径**(前缀):
|
||||
|
||||
- `internal/runtime/work_checkpoint.go`、`internal/runtime/work_checkpoint_test.go`
|
||||
- `internal/runmgr/material.go`
|
||||
- `internal/runmgr/material_sources_test.go`、`internal/runmgr/material_first_checkpoint_test.go`(提示断言所在文件)
|
||||
- `internal/runmgr/material_evidence_test.go`(宿主归一断言,只加测试,不改 `material_evidence.go`)
|
||||
- `internal/server/work_checkpoint_eval_test.go`、`internal/server/work_checkpoint_eval_budget_test.go`
|
||||
- `eval/live/work_checkpoints_test.go`
|
||||
|
||||
**改动**:
|
||||
|
||||
1. `checkpointDecl()`:按方案 §3.1 第 1、2 条重写 `Description`,并只给现有 schema 字段补 `description`;类型、枚举、`required`、`additionalProperties`、`minItems/maxItems` 与嵌套结构逐字不变。
|
||||
2. `material.go`:按方案 §3.1 第 3 条调整材料任务提示的三句;其余提示不动。
|
||||
3. `evalRejectedCheckpointPersisted` 改为返回被拒检查点去重计数;评测流按方案 §3.2 计算 `max(库内计数, 本次输入计数)`,达 2 才停,原因 `repeated rejected work checkpoint`,停止状态保持且判断优先于终态异常;库读失败仍 `checkpoint stop state unavailable`。
|
||||
4. 新标签:`evalA01Budget` 增加 `approved-o2-a01-evidence`(30,000、护栏开);`checkpointEvalOutputName("A01", "approved-o2-a01-evidence")` → `candidate-o2-a01-evidence`。评测调用记录增加本样本被拒检查点计数字段。
|
||||
|
||||
**测试**(全部离线、零付费):
|
||||
|
||||
| ID | 测试 | 断言 |
|
||||
|---|---|---|
|
||||
| T1 | 说明示例可校验 | 从 `Description` 抽出的示例 JSON(`material_record` 无 evidence、`material_record`+`direct_relation`、`difference`、`unresolved_relation`)套入最小合法检查点后 `core.ValidateWorkCheckpoint` 通过 |
|
||||
| T2 | 反例仍被拒 | `direct_relation` 带 result、`unresolved_relation` 带 result 均被核心校验拒绝(锁定说明与校验一致) |
|
||||
| T3 | Schema 结构不变 | 现有 `work_checkpoint_test.go` schema 断言保持通过;新增断言:新旧 schema **都**去掉全部 `description` 键后一致(旧 schema 的 `quote` 已带 description,不能直接与改动前原文比) |
|
||||
| T4 | 一次被拒可继续 | 改写 `TestEvaluationStopsPaidRequestsAfterRejectedCheckpoint`:先修正该测试 SSE 夹具缺失的结尾 `}`(否则真实调用会触发终态异常);输入含 1 次被拒 → 允许 1 次 provider 调用且无错误;输入含 2 次被拒 → 不调用、原因 `repeated rejected work checkpoint`;其后再发一次无被拒输入的请求仍被拒(停止状态保持) |
|
||||
| T5 | 跨 run 累计 | 改写 `TestEvaluationStopsAcrossRunsAfterPersistedCheckpointRejection`(同样先修正其 SSE 夹具缺失的结尾 `}`;因 `terminalFault` 在同一评测流内保持,各子用例各用新的评测流与数据目录):库内 1 次被拒 → 允许;2 次(不同 callId)→ 停;同一 callId 重复事件只计 1;库内 1 次 + 输入中同一次 → 仍只计 1 |
|
||||
| T6 | fail closed 不变 | `TestEvaluationFailsClosedWhenCheckpointEventsUnavailable` 保持通过 |
|
||||
| T7 | 新标签 | `evalA01Budget` 与 `checkpointEvalOutputName` 表驱动用例覆盖新标签;未知标签仍拒绝 |
|
||||
| T8 | 宿主降级与说明一致 | `material_evidence_test.go`:`analysis`+`direct_relation` 经 `normalizeMaterialEvidence` 转 `needs_user`;`material_record` 无 evidence 保持 `material_record` |
|
||||
| T9 | 提示约定 | 提示断言文件中断言材料任务提示含“原样记载用 material_record,只附 sources,不附 evidence”的约定 |
|
||||
|
||||
**单元验证命令**(worker 自检与 host 权威验证相同):
|
||||
|
||||
1. `go test ./internal/runtime/ ./internal/runmgr/ ./internal/server/ -count=1`
|
||||
2. `go test -tags live -run '^TestFollowupA01KeepsItsOwnOneShotResult$' ./eval/live/ -count=1` 与 `go vet -tags live ./eval/live/`:`eval/live` 全部文件带 `//go:build live`,默认 `go test ./...` 与 Backend CI **不覆盖**,须单独运行;该用例为表驱动、不联网(2026-10-20 在 Backend 74e5396 本地实测通过,SELF)。不设置 `WORK_CHECKPOINT_SERVER_MODE`。
|
||||
3. host 另在 Backend 根目录运行 `go test ./... -count=1` 与 `go vet ./...` 各一次;PR CI 为准(不含第 2 条,由 host 经 `make evidence` 记录)。
|
||||
|
||||
**越界即停**:需要改 core、schema 结构、生产停止逻辑或其他文件时,worker 以 `scope-expansion` 终止,由 host 请求授权。
|
||||
|
||||
## 3. I2 Dev 组合
|
||||
|
||||
- 前移 `modules/backend` 到 I1 合入提交;`make test` + `make build` 由 PR CI 验证;按合入窗口流程合入。
|
||||
- 不改 `pins/` 与其他组件;`task-pr-check` 通过后合入。
|
||||
|
||||
## 4. P2 付费复验(不在本计划授权内)
|
||||
|
||||
- 依据方案 §5、§6 与源码外资源提案执行一次 A01;标签 `approved-o2-a01-evidence`,数据目录名含 `fast-slow-u2-u3-`(如 `fast-slow-u2-u3-o2-a01e-model-data`)。
|
||||
- 候选二进制从 I2 合入后的 Dev 精确提交构建,记录 Dev/Backend SHA。
|
||||
- 结束后删除路由文件,判定按方案 §5 表格,结论与花费写入后续任务卡。
|
||||
|
||||
## 5. 完成与停止
|
||||
|
||||
- 本任务(设计)完成标准:方案、计划与源码外资源提案经 PR 合入 Dev,用户收到审阅请求。
|
||||
- I1/I2 在用户批准方案与计划后启动;P2 在用户单独批准资源提案后启动。
|
||||
- 任一单元 fail-stop 终态(`failed-unknown`、`scope-expansion`、`blocked` 等)立即停止并报告,不自动重试。
|
||||
@@ -0,0 +1,311 @@
|
||||
---
|
||||
title: Agent Stack Audit and Remediation - Plan
|
||||
type: fix
|
||||
date: 2026-10-21
|
||||
artifact_contract: ce-unified-plan/v1
|
||||
product_contract_source: ce-plan-bootstrap
|
||||
execution: code
|
||||
---
|
||||
|
||||
# Agent Stack Audit and Remediation - Plan
|
||||
|
||||
## Goal Capsule
|
||||
|
||||
对 Agent-Native、Agent-Squad、Agent-Intent、Agent-Command 及其 Backend 接入做源码审查和外部协议核对,先修复现有能力的契约缺陷,再补充影响实际使用的功能。
|
||||
本次交付是待用户审阅的方案;业务实现、迁移、部署和发布尚未授权或执行。
|
||||
建议保留现有四库分层,分批兑现可验证结果,不重写 Agent 平台、不迁移到新的编排框架。
|
||||
|
||||
- 第一批:执行边界、失败结果、权限缓存、意图校验、命令回执、用户/项目配置。
|
||||
- 第二批:持久化适配、队列限额、过程事件。
|
||||
- 后续增量:多轮意图闭环、恢复诊断、目录召回、外部互通。
|
||||
- 停止条件:批准批次的回归和适用组合验证通过,完成对应 Review、PR/CI 与收口;未批准增量不顺带实施。
|
||||
|
||||
## Product Contract
|
||||
|
||||
### Scope and Evidence
|
||||
|
||||
审查日期为 2026-10-21。以下 SHA 是本次实际检出;Dev 启动检查确认与当次远端 main 一致、组件无漂移,不能据此声称各组件远端没有更新。
|
||||
|
||||
| 对象 | 路径 | 审查 SHA |
|
||||
|---|---|---|
|
||||
| Dev | 仓库根 | `d9e9c0560a2767539f4415b579adbeff86a22ba6` |
|
||||
| Native | `deps/agent-native` | `2fc66be65201232ea38ba64d457efad5565d869d` |
|
||||
| Squad | `deps/agent-squad` | `c7121bda2a681dc46078350f7929e56e142db57f` |
|
||||
| Intent | `deps/agent-intent` | `651a2e1e03c4326888ef9f742ce592140ae30ab8` |
|
||||
| Command | `deps/agent-command` | `4f583ca3eeeb07a56a714c5086de84dcd6ad2980` |
|
||||
| Backend | `modules/backend` | `f084d10aa5ea2e4ad2ab4e6eb1268d3e83c6e991` |
|
||||
|
||||
发现以当前源代码及现有测试的检查为依据,均给出触发条件;不是线上事故结论,也不是穷尽性安全认证。
|
||||
研究子任务报告了 Native、Squad 的本地测试和仓外探针;主线程已核对关键源代码,但未重放这些执行,也未将其导入正式 evidence,故本方案不以这些报告作为通过凭证。
|
||||
修复回归、组合测试、GATE、独立 REPLAY、生产验收均为 `NOT_RUN`。
|
||||
旧 N1/N2/N3、Q1/Q2/Q3 和 I1/I2 提案只用于理解历史意图,不把旧“已完成”或“待实现”表述当作当前事实。
|
||||
|
||||
### Existing Responsibilities
|
||||
|
||||
| 模块 | 当前已有能力 | 应保持的边界 |
|
||||
|---|---|---|
|
||||
| Agent-Intent | 规则/守卫/预期、槽位验证、语义裁决、生成兜底、目录与诊断 | 识别和提出动作;不授予执行权限 |
|
||||
| Agent-Command | 命令资产/注册表、危险分级、执行四态、审计信封 | 保持无 I/O;实际授权、执行、幂等持久化由宿主负责 |
|
||||
| Agent-Native | 模型流、Agent 循环、工具/MCP、预算/取消、会话、checkpoint、外部适配 | 执行一次受约束任务;路径授权不冒充 OS 沙箱 |
|
||||
| Agent-Squad | 规划、图调度、leader、HITL、黑板/邮箱/移交/任务板、存储与队列适配 | 协作与步骤推进;依赖注入执行器,不复制单 Agent 内核 |
|
||||
|
||||
这些库并非一条固定串行流水线:普通对话可以直接执行 Native,系统命令由宿主消费 Intent 并经 Command 受理,多步任务才进入 Squad。
|
||||
真正的问题集中在边界传递和失败路径,而不是缺少更多拓扑或工具数量。
|
||||
|
||||
### Findings
|
||||
|
||||
严重级别按触发后的影响排序,生产暴露面另列。P1 不表示已观察到线上损害。
|
||||
下列行号是本次基线定位,实施时还须按符号及新基线复核。
|
||||
|
||||
| ID | 优先级与触发条件 | 源码证据 | 后果与影响边界 |
|
||||
|---|---|---|---|
|
||||
| F01 | P1:reasoning 请求的 Targets 含 `../` 或指向根外的符号链接 | Native `adapters/native/backend.go:324-333` 先直接写文件,后续才做交付校验 | 可先写出工作区再返回失败;应在任何副作用前拒绝非法目标。上层自行校验可降低暴露,不能代替库契约 |
|
||||
| F02 | P1:同一 WorkDir 被不同 ContractRequest.SessionScope 复用;或设置 Workspace.Writable | Native `adapters/native/contract.go:42-51` 未映射这些约束;`backend.go:260-273` 使用固定 transcript,仅记录字符串 scope | 跨 scope 上下文串读及写集约束失效。当前 Backend 未发现 Contract() 消费,不据此断言产品跨租户泄露 |
|
||||
| F03 | P1:ExecConfig 已启用解释器但 Env 为 nil | Native `runtime/tools/exec.go:25-26,139` | nil 被交给 os/exec,继承宿主环境,与完整白名单合同不符。Backend 当前解释器装配显式给出 env,该具体入口已有缓解 |
|
||||
| F04 | P1:崩溃留下半截 JSONL 尾行,随后续写 | Native `runtime/session/tree.go:155-181,194-207` | Load 只在内存忽略尾行,Append 紧接损坏字节;下一次读取变为中段 JSON 错误。完整 JSON 无换行也需处理 |
|
||||
| F05 | P1:Executor 成功仅含 patches、失败带 patches,或 running 步骤崩溃后自动恢复 | Squad `squad/workflow.go:403-418,455-464`、`squad/engine.go:1055-1109` | 正常路径成功未物化而失败、失败可被提升为成功;恢复路径文件齐全即判 completed,缺文件则直接重跑,未核对可信终态和外部副作用。Native 自行落盘只能掩盖其中的成功物化问题 |
|
||||
| F06 | P1:同名 role/model/backend 的权限或并发配置更新 | Squad `squad/backend.go:38-48,86-123`;`adapters/native/register.go:104-106` | 缓存键忽略 BashPermission/其他配置,Register 也不清缓存;allow→deny 后可能继续复用旧执行器。产品每次新建 resolver 可缓解,长期服务/公共库使用仍受影响 |
|
||||
| F07 | P1:调用方修改注册输入或公开返回的嵌套对象 | Intent `intent/fold.go:37-57`、`registry.go:289,494,531,557`;Command `registry.go:60,98,107` | Intent.Action/Examples 与部分目录数据、Command.ParamsSchema/ExposeAsTool 共享引用;可在 revision/锁之外改变已验证定义。这是同进程 API 合同缺陷,不等于任意远端用户已能修改 |
|
||||
| F08 | P1:启用意图生成兜底,并返回缺槽/非法槽值或异名 Action | Intent `intent/recognizer.go:333-414`、`catalog.go` | 生成路径未共用完整槽位校验,Top 丢风险/确认/动作元数据,handler 槽位查表键不一致;显式 Action 的合法异名目标又会被拒。Backend 开关 `runtime.intentGenerator` 默认 false |
|
||||
| F09 | P1:规则识别到非法槽位,宿主收到 Start + Clarify | Backend `internal/intentgate/gate.go:167-178`、`internal/route/route.go:66-72`、`internal/server/server.go:1206-1230` | InvalidSlots 被丢弃,Router 只取 Start。命令入口还只传 text,未传已识别槽位;可能误路由或被后续 schema 拒绝。不能据此断言任何高危命令都能绕过 cmdbus 确认 |
|
||||
| F10 | P1:handler 已有副作用后报错,或成功后审计存储失败 | Command `result.go:41-43`;Backend `internal/cmdbus/cmdbus.go:433-438,539-559` | 任意 handler error 被归为“无副作用 rejected”;审计失败改 unknown 却丢已生成 op ID,且库禁止 unknown 携带 ID,影响对账及安全重试 |
|
||||
| F11 | P1/P2:使用 SQLiteStore 保存协作状态,或在 async/on_exit 模式退出时 checkpoint 失败 | Squad `adapters/sqlite/runstore.go:77-119`;`squad/workflow.go:41-53` | Mailbox/Handoffs/Board 未持久化;相同 JSON 文本作 map key 会覆盖解码目标;flush 错误被吞。Checkpoint 失败不等于已完成外部动作回滚,应单独报告持久化失败 |
|
||||
| F12 | P2:同一 runtime 下多个 agent 同时或连续领取任务 | Squad `adapters/sqlite/queue.go:226-239` | 只数 agent 的在途任务,取两种 cap 的最小值不能限制 runtime 总并发;当前产品是否启用该独立队列须在实施时核验 |
|
||||
| F13 | P1:当前用户/项目有独立模型路由配置,进入 Squad | Backend `internal/app/app.go:340-364`、`internal/runtime/squad_runner.go:83-95`、`internal/runtime/llm.go:604-608` | 小队从默认账户/空项目读取 base/key 等,入口只覆盖 model,可能把用户模型名与默认端点/凭据混用;ReasoningSplit、RouteContract 也与 direct 路径不一致 |
|
||||
| F14 | P2:小队同名工具多次调用,或工具返回错误 | Native `runtime/loop.go:43-45,777-836`、`adapters/native/backend.go:405-418`;Squad `squad/port.go:66`;Backend `internal/runtime/squad_runner.go:49-54` | CallID 在旧过程契约中丢失,宿主用工具名代替;结束事件实际文本在 Text,宿主读 Detail 并恒写 ok。结果归属、摘要与成功状态失真 |
|
||||
| F15 | P2:执行工具输出大量数据;或 Contract 调用要求纯文本 | Native `runtime/tools/exec.go:149,175`、`adapters/native/contract.go:25,67-77` | CombinedOutput 先全量缓冲再裁剪,64KiB 只是展示上限;声明 CapText 却不填 Text。分别并入 U1、U2 的合同修复 |
|
||||
| F16 | P2:使用 A2A adapter 接入符合 1.0 的外部服务 | Squad `adapters/a2a/card.go:24-46`、`executor.go:19-25,135,171`、`conformance_test.go` | 声称 1.0,却仍用旧 `message/send`、`tasks/get`、小写状态与旧 Card;测试假服务复制了同一套自定义结构。当前 Backend 无该依赖,不阻塞本地核心修复,但不得声称 1.0 互通已验证 |
|
||||
|
||||
### Requirements
|
||||
|
||||
**执行与权限**
|
||||
|
||||
- R1. 不合法路径/写集/会话身份必须在模型或工具副作用发生前拒绝;支持空约束的既有兼容语义。
|
||||
- R2. 后续运行采用新的权限配置;不能因旧缓存或错误配置作用域扩大能力或混用用户凭据。
|
||||
- R3. 失败、取消、不确定、已完成保持真实语义;文件存在不是成功证据,未知不能无条件重试。
|
||||
|
||||
**输入、恢复与可核对性**
|
||||
|
||||
- R4. 规则与生成路径共用权威动作定义和槽位验证;缺失/非法/歧义必须保留到宿主。
|
||||
- R5. 状态快照和注册表对外隔离可变数据,读者不能绕过更新协议改变定义。
|
||||
- R6. 恢复后保留协作状态,保存失败可见;runtime/agent 并发上限独立生效。
|
||||
- R7. 回执保留可信关联标识;事件保留工具调用身份、真实错误和实际输出;用户可分辨完成、等待与待核对。
|
||||
|
||||
**功能增量**
|
||||
|
||||
- R8. 在已批准修复收口后,单独选择下一批功能;不把提案中的全部建议自动视为实施范围。
|
||||
|
||||
---
|
||||
|
||||
## Planning Contract
|
||||
|
||||
### Technical Decisions
|
||||
|
||||
- KTD1. 保留四库边界和现有 Go 模块;通过当前接口增量修复。现有功能覆盖足够广,整体重写会扩大兼容性与验证成本。
|
||||
- KTD2. 识别输出只作为动作建议。执行时按当前 actor/project 授权、命令 schema、确认凭证再判断;目录可见不代表允许执行。MCP 对工具 annotations 的不可信要求也支持这一边界,见 S1。
|
||||
- KTD3. 以宿主当前运行的有效配置构造 model/base/key/协议及完成性规则,作为同一配置快照传入;不随意开通新的 provider 协议。工具权限可在执行前进一步收紧,不能由旧构造缓存恢复。
|
||||
- KTD4. 先验证执行结果,再物化候选文件,再做交付检查;失败候选可以作为诊断保留,但不得发布为成功。结果 Unknown 与可重试性分别判断,不把 HTTP/网络错误等同于“尚未执行”。
|
||||
- KTD5. Native 会话和 Squad 协作状态分别在原有存储上修复。检查点、工具副作用和产品聊天历史是不同记录;不新增统一分布式执行平台,不承诺跨外部系统 exactly-once。恢复设计参考 S2 的线程状态/跨线程存储区分。
|
||||
- KTD6. Command 保持无 I/O。扩展可表达副作用不确定性的 handler 结果,并复用宿主已有 operation/outbox 存储;unknown 可携带已可信生成的 ID。可查询操作记录与权限检查必须真实存在,不能只改枚举注释。
|
||||
- KTD7. 两个四态合同不强行合并:Command 描述动作受理/生效,Native 描述 Agent 执行终态;Squad 通过适配保留差异。命令 accepted 不是 Agent succeeded,创建确认卡也不是业务动作完成。
|
||||
- KTD8. A2A 先明确支持版本并以官方独立 fixture 验证;需要兼容旧协议时显式分版本,不把旧协议重新标成 1.0。是否投入完整互通是后续增量决策,见 S3。
|
||||
|
||||
### Component Flow
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
U[用户输入] --> H[Backend 身份与项目上下文]
|
||||
H --> I[Intent 识别与槽位验证]
|
||||
I --> C[Command 宿主授权与确认]
|
||||
I --> W[工作流或 Squad 编排]
|
||||
I --> N[Native 单 Agent 执行]
|
||||
W --> N
|
||||
N --> T[经授权工具与外部能力]
|
||||
C --> O[操作记录与回执]
|
||||
T --> E[真实结果与过程事件]
|
||||
W --> S[协作状态与 checkpoint]
|
||||
E --> H
|
||||
O --> H
|
||||
S --> H
|
||||
```
|
||||
|
||||
这张图表达边界关系,不要求所有输入都逐一经过四个库。
|
||||
|
||||
### Outcome Flow
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
R[请求] --> V{授权与参数有效}
|
||||
V -->|否| X[Rejected: 未开始动作]
|
||||
V -->|是| P{需要确认或异步处理}
|
||||
P -->|是| A[Accepted: 保留可查询 ID]
|
||||
P -->|否| E[执行动作]
|
||||
A -->|授权确认或继续| E
|
||||
E --> F{结果及记录是否确定}
|
||||
F -->|已完成并可核对| C[Committed 或执行 Succeeded]
|
||||
F -->|明确失败| D[Failed 或有依据的 Rejected]
|
||||
F -->|无法确认| Q[Unknown: 保留 ID 与待核对原因]
|
||||
Q --> Z[查询与对账: 禁止盲目重放]
|
||||
```
|
||||
|
||||
取消请求同样需要确认执行侧状态;不能只因为本地 context 结束就声称远端动作已停止。
|
||||
|
||||
### Delivery Order
|
||||
|
||||
第一批建议 U1-U7、U10:其中 U1-U4 先修 Native/Squad 执行与恢复基础,U5-U7 处理输入/命令合同;U10 的当前用户/项目配置问题与它们并行处理,不排在可选存储适配之后。
|
||||
第二批 U8、U9、U11:恢复适配、队列和事件一致性。
|
||||
每个批次在组件兼容提交落地后更新 Backend 嵌套 gitlink 和 Dev 组合指针,完成一次适用组合验证。
|
||||
同批只读研究可以并行;每个仓库写入单元按 `AGENT-EXECUTION.md` 新 session、受控 transport/admit 进入候选,由 host 验证与交付。
|
||||
|
||||
实施前重新确认当前代码、在途任务与远端分流;本文不会覆盖后续已修复问题,也不会复用过期 CI 结论。
|
||||
迁移脚本可以在隔离测试库验证,真实数据迁移、部署和发布另走授权边界。
|
||||
|
||||
---
|
||||
|
||||
## Implementation Units
|
||||
|
||||
### U1. Native Workspace and Process Boundaries
|
||||
|
||||
目标:修复 F01/F03 及 F15 的输出缓冲问题,落实 R1。
|
||||
文件:`deps/agent-native/adapters/native/backend.go`、`runtime/tools/exec.go`、现有 `runtime/tools/jail.go`/`atomic.go`;测试放 `adapters/native/backend_test.go`、`runtime/tools/exec_tools_test.go`。
|
||||
方案:复用已有工作区路径解析,目标在执行前预检,实际访问仍校验根内与 symlink;Env nil 归一为空白名单;子进程持续 drain 到有界头尾缓冲,不在结束后才限制内存。
|
||||
验收:合法目标正常产出;`../`、绝对路径和 symlink 逃逸在零外部写入前拒绝;nil/空/显式 env 分别验证;大输出与取消后子进程退出且缓冲有界。
|
||||
不把工作区路径检查描述为完整 OS 沙箱;并发敌对文件系统场景优先复用可提供根约束的文件 API。
|
||||
|
||||
### U2. Native Session and Contract Fidelity
|
||||
|
||||
目标:修复 F02/F04 及 F15 的 CapText,落实 R1/R3/R6;依赖 U1 的文件访问边界。
|
||||
文件:`deps/agent-native/adapters/native/contract.go`、`backend.go`、`runtime/session/tree.go`、`exec/contract_spec.go`;测试放 `runtime/session/tree_test.go`、`adapters/native/contract_regression_test.go`(新增)及 `conformance/`。
|
||||
方案:将非空 SessionScope 绑定存储身份并在恢复前检查,Writable 限制贯穿可约束的文件工具;对于进程/MCP/宿主工具等无法兑现该写集的配置,本批在 preflight 明确拒绝非空 Writable,不假定根路径检查能限制任意进程。独占追加时修复半截尾记录/补齐合法无换行记录;中段损坏仍报错。保留真实文本输出,纯文本任务不强制隐式文件交付。
|
||||
验收:同 root 不同 tenant/project/session 不串上下文;已有匹配 scope 可继续;身份不明的旧文件不得自动归给新身份;只允许写指定路径;半截尾行恢复后可再读再写;纯文本成功的 Text 非空。
|
||||
兼容:空 scope 保持既有一次性/旧门面语义,不进行全库存量会话迁移;历史缺身份记录只在明确兼容路径读取。
|
||||
|
||||
### U3. Squad Execution Results
|
||||
|
||||
目标:修复 F05,落实 R3;无先行依赖。
|
||||
文件:`deps/agent-squad/squad/workflow.go`、`leader.go`、`engine.go` 的 recoverInterrupted、`port.go`、步骤状态持久化及 `step_retry.go`、`adapters/native/register.go`;必要的 `deps/agent-native/exec/backend.go` 与适配器映射;测试 `squad/step_retry_test.go`、`squad/recovery_test.go`、`squad/attempt_test.go`、`squad/executor_result_contract_test.go`(新增)。
|
||||
方案:按 KTD4 统一普通步、IntentRunner 和 leader 委派的结果处理;显式处理 `(nil,nil)` 与物化错误。已有文件只能帮助诊断,不能覆盖执行器终态。
|
||||
当前旧 ExecResult/exec.Result 只有 Success/Retryable,不能承接 Unknown/SideEffects。本单元先以兼容新增的可选字段或等价的类型化错误保留这些信息,再让 retry 层消费;缺少副作用证明的失败默认禁止自动重放,不能继续只按错误文本推断可重试。已有明确未派发或纯读取失败仍可有界重试;无需在本批整体替换所有 Executor 接口。
|
||||
恢复入口同样消费持久化的终态与副作用证据。running 崩溃后,只有可信完成记录才能恢复为 completed,只有明确未执行或安全重放证据才能转 pending;其他情况保留待核对原因,禁止自动推进,沿现有中断恢复/HITL 入口处理。无交互模式明确返回需核对状态,不无限等待,也不伪称动作已失败。
|
||||
验收:成功 patches-only 正常完成;失败带完整 patches 仍失败;空结果稳定失败而不 panic;部分/非法 patches 不成功;取消/Unknown 不自动变成可重试失败。外部动作完成、终态未保存时崩溃,分别测试目标文件齐全/缺失,两种情况均不伪成功或自动重复动作。检查重复尝试仍复用 ExecutionID 和截止时间。
|
||||
|
||||
### U4. Squad Resolver Configuration
|
||||
|
||||
目标:修复 F06,落实 R2;无先行依赖。
|
||||
文件:`deps/agent-squad/squad/backend.go`、`adapters/native/register.go`;测试 `squad/backend_test.go`、`adapters/native/register_test.go`。
|
||||
方案:后端工厂和角色执行配置有明确版本/摘要,更新时使相关缓存失效;限流身份与执行器配置身份分开,不能换版本便绕过同一角色并发上限。
|
||||
验收:allow→deny 收紧、deny→allow 显式放开、工厂替换、工具配置变化和限额变化正确;并行首次 Resolve 不重复构造;在途旧执行不会因新版本开启而突破总限额。
|
||||
在途任务已开始动作不承诺回滚;需要即时撤权的宿主继续在每次工具执行前检查。
|
||||
|
||||
### U5. Intent Snapshot and Generated Actions
|
||||
|
||||
目标:修复 F07 的 Intent 部分与 F08,落实 R4/R5;无先行依赖。
|
||||
文件:`deps/agent-intent/intent/fold.go`、`registry.go`、`catalog.go`、`recognizer.go`、`slots.go`;测试 `intent/registry_test.go`、`intent/slots_catalog_test.go`、`intent/i2_test.go`。
|
||||
方案:补齐现有 clone 的输入/输出边界;目录保留权威 ActionRef;生成命令先解析回唯一 spec,再复用槽位校验和风险元数据;不得用 handler 字符串索引 spec-ID 槽位表。分类器输入同样隔离,拒绝非有限/越界分数并重检阈值。
|
||||
验收:修改原始 spec、SpecByID/Catalog/Options 不影响生效快照;规则与生成同动作同槽位得到等价结果;必填缺失、非法枚举/格式、未知槽位及 handler 重名均稳定处理;Action 指向不同命令 ID 仍可正确路由;低置信度不转 matched。
|
||||
拆成独立实施时沿“快照隔离”和“生成校验”两个有界 worker 包执行,不合成通用 schema 平台。
|
||||
|
||||
### U6. Backend Intent Admission
|
||||
|
||||
目标:修复 F09,落实 R4;依赖 U5 的权威结果结构。
|
||||
文件:`modules/backend/internal/intentgate/gate.go`、`internal/route/route.go`、`internal/server/server.go`、必要的 `internal/workflow/` 消费点;测试 `internal/intentgate/gate_test.go`、`internal/route/route_test.go`、`internal/server/intent_admission_test.go`(新增)。
|
||||
方案:传递 missing/invalid 及澄清信息;区分“可开始澄清的流程草稿”和“可执行副作用的完整动作”。命令槽位按已声明 schema 映射,不能无类型地复制所有字符串;已有 text handler 保持兼容。高危动作仍由 cmdbus 确认。
|
||||
验收:缺槽和非法槽都不会直接执行业务动作;允许澄清的流程可挂起;校验通过后的槽位正确进入 handler;显式取消/纠正尚未实现时诚实回退,不能声明已支持。
|
||||
|
||||
### U7. Command Isolation and Receipts
|
||||
|
||||
目标:修复 F07 的 Command 部分及 F10,落实 R3/R5/R7;与 U6 可独立。
|
||||
文件:`deps/agent-command/registry.go`、`result.go`、`conformance/`;`modules/backend/internal/cmdbus/cmdbus.go` 及已有 operation/outbox 的最小存储接口;测试 `deps/agent-command/registry_test.go`、`result_test.go`(不存在时新增)、`modules/backend/internal/cmdbus/cmdbus_test.go`、`account_write_fence_test.go`。
|
||||
方案:复制 ParamsSchema 与 ExposeAsTool;unknown 允许保留可信 OperationID;handler 区分明确未执行与执行后未知。优先按现有持久化记录提供经 owner/project 授权的查询;若所选命令没有操作记录,则把最小持久化与查询作为该命令适配的组成部分,不能宣布“有 ID 就可查询”。
|
||||
验收:外部修改返回 spec 不改注册表;成功后 audit 失败返回 unknown 且保留 ID;明确校验拒绝为 rejected;副作用后报错不伪称未执行;未知操作只核对、不自动重放;重复确认/跨用户查询不会重复执行或泄露结果。
|
||||
跨仓先落兼容库变更,再更新 Backend。完整通用幂等平台不在本批;新幂等键只在选定命令已有明确重复执行风险时设计。
|
||||
|
||||
### U8. Squad Durable State
|
||||
|
||||
目标:修复 F11,落实 R6/R7;结果解释依赖 U3。
|
||||
文件:`deps/agent-squad/adapters/sqlite/runstore.go`、迁移入口、`squad/workflow.go` 与现有 checkpoint writer;测试 `adapters/sqlite/runstore_test.go`、`squad/checkpoint_test.go`。
|
||||
方案:SQLite 完整保存 Mailbox/Handoffs/Board;用明确字段到目标的序列解码,不用内容作 map key。终态保存、manifest 和 checkpoint flush 均检查错误,并在 EvRunCompleted/成功返回之前收敛;不能只在 defer 改 error 而此前已经发出完成事件。保存错误与业务终态分别可见,不能因此重复执行已完成业务动作。
|
||||
验收:FileStore/SQLite 同一合同 round-trip;恢复后继续收信/移交/认领;相同 clarifications/steering 都保存;sync/async/on_exit 的失败注入均可见;终态保存/manifest 失败不发伪成功事件;旧 schema 升级保留原数据。真实库迁移另行授权。
|
||||
|
||||
### U9. Squad Queue Capacity
|
||||
|
||||
目标:修复 F12,落实 R6;可独立。
|
||||
文件:`deps/agent-squad/adapters/sqlite/queue.go`;测试 `adapters/sqlite/queue_test.go`。
|
||||
方案:一次原子 Claim 同时验证 agent 在途数和 runtime 总在途数,读取/计数错误拒绝认领;保留原租约和重领规则。
|
||||
验收:runtime cap=1 下两个不同 agent 只能领取一个;同 agent 跨 runtime 仍受 agent cap;并发 Claim 不超限;释放后可继续领取。未接产品的队列模块不因此新接入产品。
|
||||
|
||||
### U10. Backend Scoped Runtime Configuration
|
||||
|
||||
目标:修复 F13,落实 R2;无需等全部库修复。
|
||||
文件:`modules/backend/internal/app/app.go`、`internal/runtime/model.go`、`llm.go`、`internal/squadadapter/entry.go`;测试 `internal/app/squad_config_test.go`(新增)、`internal/runtime/model_test.go`、`route_contract_eof_test.go`。
|
||||
方案:从当前 rc.Settings、run owner、profile 解析完整路由,direct/squad 复用现有配置规则;必要时增加窄的工厂函数。保持项目来源观察/授权限制,不能为接线一致性重新开启被明确禁用的直连工具。
|
||||
验收:两个用户不同配置与密钥来源不串用;项目覆盖生效;profile model 覆盖使用对应路由规则;MiniMax reasoning 分离和受限 EOF contract 一致;默认配置仍兼容;测试仅用合成密钥和本地 fake server。
|
||||
|
||||
### U11. Tool Event Fidelity
|
||||
|
||||
目标:修复 F14,落实 R7;跨 Native→Squad→Backend。
|
||||
文件:`deps/agent-native/runtime/loop.go`、`exec/backend.go`、`adapters/native/backend.go`;`deps/agent-squad/squad/port.go`、`adapters/native/register.go`;`modules/backend/internal/runtime/squad_runner.go`;测试各层现有 host-tools/adapter/runner 测试。
|
||||
方案:向后兼容新增调用 ID 和真实错误/状态信息,按原语义传递 Text/Detail;宿主事件与最终 message parts 一致。并行回调使用受控串行聚合或局部锁,必要时修复共享 artifactList 的并发访问。
|
||||
验收:同名工具两次调用各自配对;失败/拒绝/取消不显示 ok;摘要来自实际结果;并行事件/工件无丢失、无 race;旧适配器字段缺省不得被推断为已确认成功。
|
||||
仅修事件合同,本单元不修改界面布局;若后续需要 UI 展示,先按 DESIGN.md 单列范围。
|
||||
|
||||
---
|
||||
|
||||
## Functional Follow-Ups
|
||||
|
||||
以下是功能建议,不计入前述修复的默认实施范围。推荐顺序为 A1、A2、A3,A4 按真实外部对接需求启动。
|
||||
|
||||
| ID | 建议 | 用户收益 | 最小交付与验收 |
|
||||
|---|---|---|---|
|
||||
| A1 | 多轮意图闭环 | “把日期改到周五”“取消刚才的任务”能作用于明确对象 | 消费已有 set_slot/change_slot/cancel/expectation;绑定 owner/project/conversation/action/revision;歧义先澄清,过期回答不影响新任务;先覆盖一类真实业务动作 |
|
||||
| A2 | 执行与恢复诊断 | 看清卡在哪里、能否恢复、是否还要等人 | 在现有 run/operation 查询上提供当前责任角色、等待原因、最后稳定 checkpoint、已知副作用、下一步;先做授权读 API,不先造大型控制台 |
|
||||
| A3 | 能力目录召回与离线评测 | 不因目录固定 top-8 而遗漏低优先级能力,减少误启动 | 先建立脱敏评测集:否定/引用指令、多意图、错误槽位、过期确认、生成规则一致性;比较准确匹配、误执行、澄清率与成本,再决定是否需要检索或模型调整 |
|
||||
| A4 | 明确版本的 A2A 互通 | 外部 Agent 任务可查询、可取消并诚实报告状态 | 修 F16;采用官方 fixture/独立服务验证 Card、方法、状态与身份;持久化远端 task ID,处理 auth-required、未知态、超时和取消回执;不自动重发 message/send |
|
||||
|
||||
现有 Makefile 已显式覆盖若干 Squad 嵌套 go.mod,但未列 A2A 子模块。
|
||||
A4 启动时须纳入自己的验证入口,不能假设根 `go test ./...` 自动覆盖独立模块。
|
||||
不建议本轮新增更多拓扑、动态角色市场、全自动自我改进或向量平台;先用评测证明新能力确有收益。
|
||||
|
||||
---
|
||||
|
||||
## Verification Contract
|
||||
|
||||
实施先用各 F-ID 的具体触发条件形成最小回归,禁止只补“函数能调用”的测试。
|
||||
不调用真实付费模型,不使用生产数据库,不将已设置的真实凭据输出到日志。
|
||||
|
||||
| 验证层 | 范围 | 通过判据 |
|
||||
|---|---|---|
|
||||
| 直接回归 | U1-U11 各自文件/包 | 原触发条件被阻断,正常使用不退化 |
|
||||
| 合同验收 | 现有 conformance 与 adapter 边界 | Native 文本/文件/隔离合同,Squad 成功/失败/未知处理,Intent 规则/生成一致,Command 四态可对账 |
|
||||
| 定向并发 | 注册表/缓存/队列/事件聚合 | 相关 `-race` 与原子认领用例通过,不机械扩大到所有包 |
|
||||
| 故障恢复 | 截断 JSONL、存储错误、执行后响应丢失 | 恢复不丢协作状态,不伪称完成,不重复外部副作用 |
|
||||
| 宿主集成 | 两个用户、项目覆盖、缺槽、确认、direct/squad | 权限与配置作用域一致,确认后只执行一次,结果与事件一致 |
|
||||
| 组合 | 固定组件及 Backend 嵌套 gitlink | 每批最终候选执行项目规定的 `make test` 和必要构建,按当前门禁保存 SELF;当前 HEAD 必需 CI 为 GATE |
|
||||
|
||||
库级测试成功不证明产品装配成功;fake 协议服务成功不证明外部协议兼容;启动 READY 不证明业务功能通过。
|
||||
对于缺少真实后端的可选路径,清楚标注 NOT_RUN,不阻断已批准且不依赖它的本地修复交付。
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- 已批准批次的 F-ID 均有对应修复、反例与正常路径验证;被新基线消除的项记录来源,不重复实现。
|
||||
- 变化的公共合同向后兼容或有明确迁移路径;错误语义和状态持久化可解释。
|
||||
- 组件 SHA、Backend 嵌套引用与 Dev 组合一致;当前候选通过适用验证、独立 Review 和必需 CI 后按项目规范合入。
|
||||
- 无未授权部署/真实数据迁移/发布;可选功能保持独立,用户业务验收不被工程自检替代。
|
||||
|
||||
## Appendix
|
||||
|
||||
### Primary Research
|
||||
|
||||
- S1. MCP Tools specification,核对版本 `2025-11-25`,研究日 2026-10-21:工具定义、结构化输入/输出与 annotations 信任边界。`https://modelcontextprotocol.io/specification/2025-11-25/server/tools`。影响 KTD2;未要求升级协议或引入 SDK。
|
||||
- S2. LangGraph Persistence 官方文档,研究日 2026-10-21:checkpointer 的线程状态与 store 的跨线程数据各有作用域。`https://docs.langchain.com/oss/python/langgraph/persistence`。影响 KTD5;仅借鉴状态边界,不引入 LangGraph。
|
||||
- S3. A2A `v1.0.1` 官方 specification 与 v1 变更说明,研究日 2026-10-21:协议方法、Agent Card 的 supportedInterfaces 和任务状态表示。`https://a2a-protocol.org/v1.0.1/specification/`、`https://a2a-protocol.org/v1.0.1/whats-new-v1/`。影响 F16/KTD8/A4;不混用开发中 PR 版本。
|
||||
|
||||
外部研究用于校验契约和改进方案,不能单凭行业实践宣判本项目缺陷。
|
||||
进一步获取 Go os/exec 与 LangGraph interrupts 网页时检索工具连接失败;本方案未把未获取内容列为外部证据。
|
||||
|
||||
### Remaining Decisions
|
||||
|
||||
建议批准第一批 U1-U7、U10 后连续实施到各自可验收的集成结果,再进入第二批 U8、U9、U11。
|
||||
也可一次批准两批,但不因此纳入 A1-A4。
|
||||
需要用户确定的是实施批次与后续功能优先级;具体函数、补充字段和回归组织由实施者在上述合同内确定,不再逐文件请求许可。
|
||||
当前没有证据支持宣称线上已被利用;部署状态、真实模型行为和外部 A2A 互通仍待相应阶段验证。
|
||||
@@ -0,0 +1,93 @@
|
||||
# 快慢通道 O2 后续:A01 检查点证据合规方案
|
||||
|
||||
- 状态:**方案稿,待人审。** 本文不授权产品代码改动、付费请求、A01 复验、实例配置写入或部署;人审通过后才按配套计划派 worker 实施零付费部分,付费复验按[资源提案](#6-付费复验资源提案摘要)单独批准。
|
||||
- 日期:2026-10-20;作者:host(只读调查与文档撰写);来源等级:SELF。
|
||||
- 决策来源:用户 2026-10-20 选定“立项:出设计和资源提案,方向是收紧说明并让评测允许修正一次(推荐,先零付费)”。
|
||||
- 前序:[O2 方案](2026-10-19-fast-slow-o2-route-contract-eof-design.md);P1 结论见归档卡 `.task/archive/feature-fast-slow-o2-p1.md`(限路由契约在真实路由上有效;A01 停在应用层证据合规)。
|
||||
- 配套计划:[实施与测试计划](../plans/2026-10-20-fast-slow-o2-a01-evidence-plan.md)。
|
||||
|
||||
## 1. 目标与非目标
|
||||
|
||||
**目标**:让 A01 在已生效的限路由契约下,第一批检查点按工具约定发布成功;偶发一次不合规时,评测允许模型按工具结果修正一次,而不是立即判失败。
|
||||
|
||||
**非目标**:
|
||||
|
||||
- 不改 `agent-system-core` 校验器(`validateWorkFindingEvidence`)与宿主证据归一(`normalizeMaterialEvidence`)语义;“不合规证据软降级为待确认”另行讨论(§5)。
|
||||
- 不改工具 JSON Schema 结构(不引入 `oneOf`/条件 schema);不改路由契约、传输层或 `[DONE]` 判定。
|
||||
- 不放宽评测质量判据(仍要求至少 2 个 running 检查点落库并经独立评估);不改预算上限的数值口径。
|
||||
- 不写实例系统层配置、不部署。
|
||||
|
||||
## 2. 现状与根因(零付费调查)
|
||||
|
||||
**A01 事实**(P1 第二段,收据 `a01-run-receipt.json` sha256 前缀 `7e3583a8`):第一批检查点被核心校验拒绝:`checkpoint.evidence: direct relation cannot have result`。离线重放(同一参数、`agent-system-core` 当前版本)得到:finding 0 为 `kind=material_record`、`mode=direct_relation`,却带 `total=2`、`phase=1` 两个前提与 `result=8`;去掉 evidence 后同一 finding 校验通过。评测随即按“检查点被拒即停”停止全部付费请求,样本终止。
|
||||
|
||||
| 位置 | 现行行为 | 问题 |
|
||||
|---|---|---|
|
||||
| Backend `internal/runtime/work_checkpoint.go` `checkpointDecl()` | 工具说明用一句话并列三种 mode;schema 为单一扁平对象:`mode` 枚举、`premises[].role` 四值枚举、`result` 可选 | schema 允许任意 mode 搭配任意 role/value/result;各 mode 的必填与禁填只在核心校验器里,模型看不到 |
|
||||
| Backend `internal/runmgr/material.go`(材料任务提示) | “分析判断必须写 assumptions,并说明单位、算式或前提”;“数字或分期归属用 evidence 给依据” | 未说明 `material_record` 不需要 evidence;“算式或前提”推动模型给原样记载也补数字前提 |
|
||||
| core `task_snapshot.go` `validateWorkFindingEvidence` | `direct_relation`:恰 1 个 `direct_relation` 前提、无 value、无 result;`difference`:total、phase 带值且有 result;`unresolved_relation`:total、phase、dependent 带值、无 result | 一条 finding 不合规即整次检查点被拒(工具说明称“宿主会改为待确认”,与此不一致) |
|
||||
| Backend `runmgr/material_evidence.go` `normalizeMaterialEvidence` | 无 evidence 的 `material_record` 由宿主扩展为整句并按原句展示(原句语境不唯一则转待确认);无 evidence 的非审慎分析转待确认;`direct_relation` **仅当** kind 为 `material_record`、单一来源、短引即完整原句且原句明确表达关系时保留,其余一律转待确认;`difference` 要求 `result == total − phase`,否则转待确认 | 正常路径本已支持“原样记载不附 evidence”;分析类使用 `direct_relation` 必然被降级 |
|
||||
| Backend `internal/server/work_checkpoint_eval_test.go` 评测流 | 输入中出现被拒的 `publish_work_checkpoint` 工具结果,或库内已落一次被拒结果 → 停止后续全部付费请求 | 比生产严格:生产(`runtime/sub_loop.go`)把错误结果回给模型并继续 |
|
||||
|
||||
**根因**:模型看不到“按 mode 的必填/禁填”,提示又鼓励给记载补前提;而评测对首个不合规零容忍。前者是主因,后者把一次可修正的偏差放大为整样本失败。
|
||||
|
||||
## 3. 方案
|
||||
|
||||
### 3.1 收紧工具说明与任务提示(生产可见,零付费)
|
||||
|
||||
1. **工具说明**(`checkpointDecl().Description`)改为分条写清:
|
||||
- `material_record` 默认只引用原句,**不附 evidence**(宿主扩展为整句并按原句展示)。
|
||||
- 唯一例外:原句本身明确表达关系(如“第二期包含3项依赖外部接口的需求”)时,`material_record` 可附 `direct_relation`:单一来源、短引即完整原句、恰 1 个前提 `{role:"direct_relation", sourceIndex:0}`,**不写 value、不写 result**;不满足这些条件宿主会转待确认。
|
||||
- `analysis`/`provisional`/`needs_user` 里的数字或分期归属用以下两种 mode 之一(每条 finding 只选一种;不要用 `direct_relation`,宿主会转待确认):
|
||||
- `difference`:前提 `total`、`phase` 各 1 个且都写 value;`result` 必须等于 total − phase。
|
||||
- `unresolved_relation`:前提 `total`、`phase`、`dependent` 各 1 个且都写 value;**不写 result**。
|
||||
- 各给一个最小 JSON 示例(字段名与 schema 一致)。
|
||||
- 删除“宿主会把不能证明的判断改为待确认”中与校验器不一致的承诺,改为如实描述两类结果:(a) evidence 结构不符(如 `direct_relation` 带 result)会**整次被拒**并在工具结果给出原因,须按原因修正后重发;(b) 结构合规但宿主核不上的——前提数字与短引不符、`direct_relation` 不满足上述条件、原句语境不唯一、无 evidence 的非审慎分析、无 evidence 的 `needs_user`(原文会被替换为通用待确认文案)——该条转为待确认并在工具结果说明。
|
||||
2. **Schema 字段说明**:仅在现有字段上补 `description`(`evidence`、`mode`、`premises`、`role`、`value`、`result`),写明上面的按 mode 约束;**不改类型、枚举、必填和嵌套结构**,不引入 `oneOf`(部分 OpenAI 兼容服务对复合 schema 支持不确定)。
|
||||
3. **材料任务提示**(`material.go`):把“分析判断必须写 assumptions,并说明单位、算式或前提”限定为分析类;补一句“原样记载用 material_record,只附 sources,不附 evidence”;保留现有 12/8/3 反例。
|
||||
|
||||
**测试锚点**:
|
||||
|
||||
- 说明中的每个 JSON 示例在测试里抽出并经 `core.ValidateWorkCheckpoint`(套入最小合法检查点)校验通过;反例(`direct_relation` 带 result)断言被拒。
|
||||
- 宿主归一层(`normalizeMaterialEvidence`)补断言:`analysis` + `direct_relation` 转 `needs_user`;`material_record` 无 evidence 保持原样记载——锁定说明所述的降级行为与宿主一致。
|
||||
- 材料任务提示补断言:含“原样记载不附 evidence”的约定。
|
||||
- 不以整段文案做快照断言,避免文案调整引起脆弱失败。
|
||||
|
||||
### 3.2 评测允许修正一次(仅评测代码)
|
||||
|
||||
- 现行“首个被拒即停”改为:**同一评测数据目录内,被拒的 `publish_work_checkpoint` 结果累计达 2 次即停**,原因串改为 `repeated rejected work checkpoint`;第 1 次被拒后允许继续请求,让模型按工具结果修正。
|
||||
- 计数口径:库内按 `(conversation, run, callId)` 去重的被拒检查点数,与当前请求输入中被拒检查点工具消息数取较大值(同一次被拒同时出现在库与输入中只计 1);库不可读仍 fail closed(原因不变)。停止状态一旦触发即保持,后续请求一律拒绝;被拒判断优先于终态异常等其他原因。
|
||||
- 已知边界:生产写 `tool.result` 事件时忽略写入错误(`runtime/sub_loop.go`),若事件未落库且两次被拒分属不同 run,可能少计一次、多放行一次请求;该请求仍受预算与单 run 调用上限约束,不阻塞本方案。
|
||||
- 其余停止条件(预算、单 run 8 次调用、6 个 run、终态异常、用量不可得)全部不变;修正请求同样计入这些上限。
|
||||
- 评测输出新增已批准标签(见 §6),不复用 `approved-r04-20261014`;该标签沿用 A01 30,000 token 画像上限与护栏。
|
||||
- 评测调用记录写入每次调用前的被拒计数;因最后一次被拒可能发生在最后一次调用之后,**判定以收尾时库内去重计数为准**,供独立评估区分“一次修正后通过”与“首发即通过”。
|
||||
|
||||
### 3.3 不做的替代方案
|
||||
|
||||
| 方案 | 不选原因 |
|
||||
|---|---|
|
||||
| Schema 改为按 mode 的 `oneOf` | 模型侧约束最强,但 provider 对复合 schema 的支持未核实,可能整体拒绝工具或静默忽略;收益不确定且扩大验证面 |
|
||||
| 核心校验器对不合规 evidence 软降级为待确认 | 改变核心契约与跨仓语义,影响生产展示;作为二次失败后的候选(§5) |
|
||||
| 只改评测不改说明 | 修正一次能掩盖说明缺陷,但每个样本多花一轮,且生产用户仍会看到被拒结果 |
|
||||
|
||||
## 4. 影响面与风险
|
||||
|
||||
- 生产:仅改模型可见文案与 schema 字段说明;校验与归一逻辑不变。风险是模型更少附 evidence,结果更多显示为“待确认”——这是当前设计允许的保守方向。
|
||||
- 评测:只放宽“被拒即停”的次数,预算与调用上限不变,最坏额外消耗受同一上限约束。
|
||||
- 跨仓:仅 Backend 一个组件仓 PR + Dev gitlink 组合 PR;不触 core、Agent-Native、Frontend/UI。
|
||||
|
||||
## 5. 付费复验判定
|
||||
|
||||
| 结果 | 判定 | 后续 |
|
||||
|---|---|---|
|
||||
| 首发检查点即合规,≥2 个 running 检查点落库,最终正文完整且独立评估通过 | PASS | O2 收口;是否写实例契约配置另行决策 |
|
||||
| 第 1 次被拒、修正后合规,其余同上 | PASS(附注:说明仍有缺口) | 记录被拒原因,评估是否需进一步收紧 |
|
||||
| 被拒 2 次 | FAIL(证据合规) | 提出“核心软降级”方案另行评审,不追加付费 |
|
||||
| 传输终态、预算、用量等其他原因停止 | 按原因归类,不计入本方案结论 | 按原因另行处理,不自动重跑 |
|
||||
|
||||
## 6. 付费复验资源提案(摘要)
|
||||
|
||||
- 范围:在本方案零付费部分合入 Dev 后,一次 A01(同一 MiniMax-M3 路由 + 已验证的限路由契约),不重跑、不扩样本。
|
||||
- 上限:报告 token ≤ 60,000(`WORK_CHECKPOINT_REMAINING_TOKENS=60000`);单 run ≤ 8 次调用;被拒检查点达 2 次即停。
|
||||
- 前置:用户重新放置路由文件(P1 结束后已删除);host 只核验权限、哈希、模型、主机、key 是否存在及三元组摘要,不读取或回显 key。
|
||||
- 详细提案(逐项命令、收据路径、停止规则)放在源码外证据目录,人审时一并提供;本提案不随本文获批,须用户单独批准。
|
||||
@@ -0,0 +1,50 @@
|
||||
# O2 A01 P2 复验 C8 零付费复核
|
||||
|
||||
日期:2026-10-21;性质:只读复核(零付费、不改代码);对象:Backend `dfb31da20ab0276ea8e167cf26675f7ac95a1b26`、agent-native `2fc66be65201232ea38ba64d457efad5565d869d`、P2 收据 `ec02e80753db2f85…`、provider-records `add19bd1500127bb…`。
|
||||
|
||||
## 结论
|
||||
|
||||
P2 第 3 次请求的 C8 是**工具名未声明**,不是参数 schema 不符。第 3 次请求被宿主切到 final-only,从工具声明里移除了 `publish_work_checkpoint`;模型按任务指令(reading → crosscheck)仍去调用它。final-only 由两个条件同时触发:已保存 2 个检查点;30000 预算在 4000 输出上限下只够再留一次最终答复。即使只存了 1 个检查点,第 3 次也会是 final-only。根因是:A01 的预算只够两轮检查点请求加一次最终答复,任务指令却期望至少两个阶段再加最终答复;同时模型无视了 final-only 系统提醒。
|
||||
|
||||
所以「schema 不符时不丢弃整轮、交给工具层返回可修正错误」这一改法**不适用于本次失败**,即使实施也不会让 P2 通过。
|
||||
|
||||
## 依据
|
||||
|
||||
| # | 事实 | 来源 |
|
||||
|---|---|---|
|
||||
| E1 | 物料子 Agent 在 `SavedCheckpoints >= 2` 或 `remaining <= 2*(LastPrompt+OutputCap+margin)` 时进入 final-only | Backend `internal/runtime/material_final_reserve.go:21-30` |
|
||||
| E2 | final-only 请求从 `requestDecls` 中删除 `publish_work_checkpoint`,并插入系统提醒「不再发布检查点」 | Backend `internal/runtime/sub_loop.go:125-148` |
|
||||
| E3 | P2 在第 3 次请求前已保存 2 个检查点(均 ok、0 拒绝),且 `phaseId` 只出现过 `reading` | P2 数据库 events |
|
||||
| E4 | 第 3 次请求的预算核算:已用 12317、上次 prompt 5926、outputCap 4000、limit 30000。剩余 17683 > 5926+1481,不触发 stop;17683 <= 2×(5926+4000+1481)=22814,且已存 2 个检查点,两个条件都触发 final-only | provider-records 第 1、2 次 usage 与 outputCap,按 E1 公式计算 |
|
||||
| E5 | 第 3 次 prompt_tokens 为 5160,比第 2 次的 5926 少。历史变长、prompt 反而变短,与「删去大段检查点工具声明」一致。cache_read 从 5503 跌到 128,由删声明与在首条 system 后插入提醒共同造成 | provider-records 第 2、3 次;`sub_loop.go:137-148` |
|
||||
| E6 | 第 3 次思考末句为「Now crosscheck phase.」,随后发出一个 1000 字节、可解析的工具调用;任务指令要求 crosscheck 阶段调用 `publish_work_checkpoint` | P2 数据库;Backend `internal/runmgr/material.go:306` |
|
||||
| E7 | C8 = `callsDeclared` 失败,即工具名不在本次请求声明中,或顶层 `argschema.Validate` 失败 | agent-native `runtime/llm/route_contract.go` `check`/`callsDeclared` |
|
||||
|
||||
E1、E2、E4 是确定性代码路径,可以确定第 3 次请求未声明 `publish_work_checkpoint`。E5、E6 佐证模型调用的就是它。原始参数按设计不落盘,所以「调用名是 `publish_work_checkpoint`」属于高置信推断,未经原文核实。
|
||||
|
||||
## 对「schema 不符交给工具层」的只读评估
|
||||
|
||||
- **不解决本次问题**:本次是名称未声明,不经过 schema 分支。
|
||||
- **schema 分支本身风险有限,但收益也有限**:`argschema.Validate` 只校验顶层,即必填项非空、顶层原始类型,以及在声明了 `properties` 时拒绝未声明的顶层键;不递归 `findings`。证据结构等深层错误本来就由工具层返回可修正结果。
|
||||
- **改契约语义需要设计评审**:C8 是 O2 已批准契约的一项。放宽后,一次顶层违规会从「终态失败」变成「多一轮请求」,影响付费上限的估算。
|
||||
- **即使没有契约也是终态**:final-only 阶段调用检查点时,`runTool` 返回可修正错误,但同时置位 `finalOnlyRejected`;下一次请求直接以 `checkpoint requested after final-only transition` 结束(`sub_loop.go:109-110`、`380-388`)。所以单改契约不能把它变成可恢复路径。
|
||||
|
||||
结论:不建议实施该改法。
|
||||
|
||||
## 可选修正(未实施,需单独授权)
|
||||
|
||||
| 选项 | 做法 | 影响 | 建议 |
|
||||
|---|---|---|---|
|
||||
| F1 | 让模型提前知道下一次是 final-only:宿主按 E1 的两个条件,判定下一次请求将是 final-only 时,在当次检查点工具结果里写明「下一步直接写最终结论,不再发布检查点」;任务指令把阶段要求改为与预算一致(例如第 2 次检查点即 crosscheck,或预算紧时允许跳过)。本次的 final-only 系统提醒已被无视,所以工具结果提示是否有效必须付费复验 | 只改 Backend 物料提示与工具结果文字,不动契约;零付费单测可以验证宿主侧行为 | **推荐** |
|
||||
| F2 | final-only 阶段保留检查点声明,由工具层拒绝,并去掉 `finalOnlyRejected` 终止 | 避免 C8,但每次违规多一轮付费请求,可能击穿最终答复预留;缓存还受插入提醒影响,不会因此稳定 | 备选 |
|
||||
| F3 | C8 对未声明名称改为可修正 | 削弱路由契约,与 O2 设计相悖 | 不建议 |
|
||||
|
||||
F1、F2 是否真能让模型遵守,只能靠付费复验判定;零付费单测只能证明宿主侧行为。
|
||||
|
||||
## P2 花费与结论(记录)
|
||||
|
||||
- 授权:用户批准一次 A01 付费复验(label `approved-o2-a01-evidence`,30000 tokens 守卫)。
|
||||
- 花费:3 次付费请求,共 17955 tokens(5873 + 6444 + 5638),未超守卫。
|
||||
- 结论:NOT_PASS。路由契约在第 3 次请求判 C8(`terminal_error`),子运行以 `tool call stream ended before [DONE]` 失败,outcome 为 partial。
|
||||
- 证据合规目标已达成:2 个检查点,0 拒绝,`material_record` 均未附 evidence。
|
||||
- 路由文件已在复验后删除;本次复核没有发起任何付费请求。
|
||||
@@ -189,6 +189,12 @@ scripts/svc.sh <name> status # 单服务详情
|
||||
- 显式给任何运行参数(或 `--config` 文件、`SVC_*` 变量)时**整体重建**参数;
|
||||
- agent/static 换端口:`restart --port N`(进程与登记同步更新);
|
||||
- postgres 换端口:`set --apply`(见 §7.4)或 `restart --port N` 同样有效。
|
||||
- **超级管理员凭据文件(agent,`superadmin_envfile=`)**:在 deploy/start/restart 时传一次
|
||||
`AGENT_SYSTEM_ENV_FILE=<文件>`,svc 先校验(普通文件、属当前用户、权限 0600 即无组/其他位,
|
||||
不合格则在停旧服务前拒绝),再只把**绝对路径**记入 svc.env(从不读取、复制或打印文件内容);
|
||||
此后裸 `restart`、`recover`/watchdog 拉起都复用该路径。本次环境显式设置的
|
||||
`SUPER_ADMIN_OWNER`/`SUPER_ADMIN_PASSWORD`/`AGENT_SYSTEM_ENV_FILE` 优先于登记;
|
||||
`AGENT_SYSTEM_ENV_FILE=`(空值)清除登记;登记文件已不存在时告警且不转发(serve 按缺凭据拒启)。
|
||||
|
||||
`status` 输出示例(postgres):
|
||||
|
||||
|
||||
+1
-1
Submodule modules/backend updated: d8923f18ec...f084d10aa5
+1
-1
Submodule modules/documents updated: 82b4619574...2e9c3bc1d8
+1
-1
Submodule modules/frontend updated: d7768d4ea0...113103f028
@@ -84,7 +84,7 @@ start_serve() {
|
||||
stop_serve() { [ -n "${API_PID:-}" ] && { kill "$API_PID" 2>/dev/null || true; wait "$API_PID" 2>/dev/null || true; API_PID=""; }; }
|
||||
cleanup() {
|
||||
stop_serve
|
||||
if [ -n "${UI_PID:-}" ] && ps -p "$UI_PID" -o args= 2>/dev/null | grep -Fq -- "vite preview --port $UI_PORT"; then
|
||||
if [ -n "${UI_PID:-}" ] && grep -Fq -- "vite preview --port $UI_PORT" < <(ps -p "$UI_PID" -o args= 2>/dev/null); then
|
||||
kill "$UI_PID" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
@@ -125,7 +125,7 @@ cmd() { # cmd <jar> <id> <params> → payload;rejected 即死
|
||||
|| die "command $2 rejected"
|
||||
}
|
||||
log "seed: host project + members + doc + duties + grant"
|
||||
log "me probe: $(curl -s -b "$AJ" "$API_BASE/api/me" | head -c 160) | jar=$(ls -la "$AJ" 2>/dev/null | awk '{print $5}')"
|
||||
log "me probe: $(head -c 160 < <(curl -s -b "$AJ" "$API_BASE/api/me")) | jar=$(ls -la "$AJ" 2>/dev/null | awk '{print $5}')"
|
||||
PIDRESP=$(curl -s -w '\n%{http_code}' -b "$AJ" -X POST "$API_BASE/api/projects" -H 'Content-Type: application/json' -d '{"name":"职责验收夹具"}')
|
||||
CODE=${PIDRESP##*$'\n'}; BODY=${PIDRESP%$'\n'*}
|
||||
[ "$CODE" = 200 ] || die "host project create: HTTP $CODE body=${BODY:0:200}"
|
||||
|
||||
+1
-1
@@ -208,7 +208,7 @@ log "seed: load cockpit doc-tree into $PID_MAIN"
|
||||
"$OUT/agent-system-rdm" -data "$DATA" \
|
||||
-structure "$DEV_ROOT/docs/structure/cockpit-e2e.yaml" -project "$PID_MAIN" \
|
||||
>"$OUT/structure-cockpit.log" 2>&1 || { tail -5 "$OUT/structure-cockpit.log"; die "cockpit structure load failed"; }
|
||||
grep -o '装载(.*)' "$OUT/structure.log" | head -1 || true
|
||||
grep -m1 -o '装载(.*)' "$OUT/structure.log" || true
|
||||
|
||||
# 阶段③:重启服务,切片入库挂载到已装载骨架(API)。
|
||||
start_serve
|
||||
|
||||
@@ -6,7 +6,7 @@ case "$branch" in
|
||||
echo "pre-commit: 拒绝——集成分支 ${branch} 不得直接提交,走 feature 分支 + PR(collaboration.md)" >&2
|
||||
exit 1 ;;
|
||||
esac
|
||||
echo "$branch" | grep -qE '^(feature|fix|hotfix|release)/[A-Za-z0-9._-]+$' || {
|
||||
grep -qE '^(feature|fix|hotfix|release)/[A-Za-z0-9._-]+$' <<<"$branch" || {
|
||||
echo "pre-commit: 拒绝——分支名须为 feature|fix|hotfix|release/<slug>,当前:${branch}" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ for dir in deps/agent-intent deps/agent-native deps/agent-squad; do
|
||||
deps_out=$(cd "$root/$dir" && GOWORK=off go list -deps ./...) || fail=1
|
||||
for sib in "${siblings[@]}"; do
|
||||
[ "$sib" = "$mod" ] && continue
|
||||
if printf '%s\n' "$deps_out" | grep -E -q "^(${sib}|${sib}/)"; then
|
||||
if grep -E -q "^(${sib}|${sib}/)" <<<"$deps_out"; then
|
||||
echo "boundary: $mod core depends on $sib" >&2
|
||||
fail=1
|
||||
fi
|
||||
|
||||
+124
-34
@@ -897,13 +897,13 @@ derive_port() {
|
||||
# svc_get KEY:读当前服务 svc.env 的键值(无则空)。
|
||||
svc_get() {
|
||||
[ -f "$ENV_DIR/svc.env" ] || return 0
|
||||
sed -n "s/^$1=//p" "$ENV_DIR/svc.env" | head -1
|
||||
sed -n "/^$1=/{s///p;q;}" "$ENV_DIR/svc.env"
|
||||
}
|
||||
|
||||
# svc_kind NAME:服务类型(svc.env kind=,无登记或无 kind 键 → agent,兼容旧登记)。
|
||||
svc_kind() {
|
||||
local k=""
|
||||
[ -f "$RUNTIME/$1/svc.env" ] && k="$(sed -n 's/^kind=//p' "$RUNTIME/$1/svc.env" | head -1)"
|
||||
[ -f "$RUNTIME/$1/svc.env" ] && k="$(sed -n '/^kind=/{s///p;q;}' "$RUNTIME/$1/svc.env")"
|
||||
[ -n "$k" ] && printf '%s' "$k" || printf agent
|
||||
}
|
||||
|
||||
@@ -942,6 +942,7 @@ svc_merge_tags() {
|
||||
# (托管绑定不重复存 DSN,外部直连存受保护 dsn=);未走解析的调用保持旧值。
|
||||
record_state() {
|
||||
local pid prev_src prev_dbref prev_conffile prev_db prev_dsn prev_envfile prev_plugins plugins_out merged_tags
|
||||
local prev_saenv
|
||||
pid="$(read_pid)"
|
||||
prev_src=""
|
||||
[ -f "$ENV_DIR/svc.env" ] && prev_src="$(svc_get source)"
|
||||
@@ -950,6 +951,7 @@ record_state() {
|
||||
prev_db="$(svc_get db)"
|
||||
prev_dsn="$(svc_get dsn)"
|
||||
prev_envfile="$(svc_get envfile)"
|
||||
prev_saenv="$(svc_get superadmin_envfile)"
|
||||
prev_plugins="$(svc_get plugins)"
|
||||
plugins_out="$prev_plugins"
|
||||
# 只有激活校验和 UI 探活都通过之后才提交新的 plugins=。中途 record_state
|
||||
@@ -999,6 +1001,11 @@ record_state() {
|
||||
elif [ -n "$prev_envfile" ]; then
|
||||
printf 'envfile=%s\n' "$prev_envfile"
|
||||
fi
|
||||
if [ -n "${SUPERADMIN_ENVFILE_NEW:-}" ]; then
|
||||
printf 'superadmin_envfile=%s\n' "$SUPERADMIN_ENVFILE_NEW"
|
||||
elif [ "${SUPERADMIN_ENVFILE_CLEAR:-0}" != 1 ] && [ -n "$prev_saenv" ]; then
|
||||
printf 'superadmin_envfile=%s\n' "$prev_saenv"
|
||||
fi
|
||||
[ -n "$plugins_out" ] && printf 'plugins=%s\n' "$plugins_out"
|
||||
[ $# -gt 0 ] && printf '%s\n' "$@"
|
||||
} > "$tmp"
|
||||
@@ -1055,7 +1062,7 @@ proc_stop() {
|
||||
port_owner() {
|
||||
local port="${ENV_ADDR##*:}"
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
ss -ltnp 2>/dev/null | awk -v p=":$port" '$4 ~ p"$" {print $0; exit}'
|
||||
awk -v p=":$port" '$4 ~ p"$" {print $0; exit}' < <(ss -ltnp 2>/dev/null)
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1064,7 +1071,7 @@ port_owner_pid() {
|
||||
local line pid
|
||||
line="$(port_owner)"
|
||||
[ -n "$line" ] || return 1
|
||||
pid="$(printf '%s' "$line" | sed -n 's/.*pid=\([0-9][0-9]*\).*/\1/p' | head -1)"
|
||||
pid="$(sed -n '/.*pid=\([0-9][0-9]*\).*/{s//\1/p;q;}' <<<"$line")"
|
||||
[ -n "$pid" ] || return 1
|
||||
printf '%s' "$pid"
|
||||
}
|
||||
@@ -1355,6 +1362,8 @@ svc_identity_run() {
|
||||
svc_db_identity_ensure() {
|
||||
# 仅 fresh/pending 或显式 adopt 调用。established 缺行不得走这里自动补。
|
||||
# 非 agent 或无能力:跳过。可选参数 BIN:部署候选。
|
||||
# SVC_IDENTITY_LAST_OUT:最近一次 identity CLI 输出,供候选门禁区分失败原因。
|
||||
SVC_IDENTITY_LAST_OUT=""
|
||||
local bin="${1:-${ENV_BIN:-}}"
|
||||
manifest_ok || return 0
|
||||
local kind; kind="$(svc_get kind)"
|
||||
@@ -1365,6 +1374,7 @@ svc_db_identity_ensure() {
|
||||
IFS=$'\t' read -r iid dbref data <<<"$line"
|
||||
local out rc=0
|
||||
out="$(svc_identity_run "$bin" ensure "$iid" "$dbref" "$data" 2>&1)" || rc=$?
|
||||
SVC_IDENTITY_LAST_OUT="$out"
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
c_err "[$ENV_NAME] DB 身份 ensure 失败:$out"
|
||||
return 1
|
||||
@@ -1374,6 +1384,7 @@ svc_db_identity_ensure() {
|
||||
}
|
||||
|
||||
svc_db_identity_verify() {
|
||||
SVC_IDENTITY_LAST_OUT=""
|
||||
manifest_ok || return 0
|
||||
local kind; kind="$(svc_get kind)"
|
||||
[ "$kind" = agent ] || return 0
|
||||
@@ -1384,6 +1395,7 @@ svc_db_identity_verify() {
|
||||
IFS=$'\t' read -r iid dbref data <<<"$line"
|
||||
local out rc=0
|
||||
out="$(svc_identity_run "$bin" verify "$iid" "$dbref" "$data" 2>&1)" || rc=$?
|
||||
SVC_IDENTITY_LAST_OUT="$out"
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
c_err "[$ENV_NAME] DB 身份校验失败,拒绝启动:$out"
|
||||
c_err " 先调查库文件与身份行;确认归属后 scripts/svc.sh $ENV_NAME adopt --confirm $iid"
|
||||
@@ -1395,6 +1407,22 @@ svc_db_identity_verify() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# svc_identity_schema_missing SRCBIN:最近一次 identity CLI 报身份表缺失(库 schema
|
||||
# 早于 v27)时打印迁移指引并返回 0;否则返回 1,调用方沿用原失败提示。不自动迁移。
|
||||
svc_identity_schema_missing() {
|
||||
case "${SVC_IDENTITY_LAST_OUT:-}" in
|
||||
*"identity table missing"*) ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
local data_root
|
||||
data_root="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["identity"]["data_root"])' "$ENV_DIR/manifest.json" 2>/dev/null)" \
|
||||
|| data_root="<data_root>"
|
||||
c_err "[$ENV_NAME] 数据库 schema 早于身份表(pre-v27,identity table missing):候选无法补齐/验证 DB 身份;旧服务/旧制品/登记保持不变"
|
||||
c_err " 处理:先备份数据库 → scripts/svc.sh $ENV_NAME stop → 用候选制品执行 $1 migrate --data $data_root(PostgreSQL:DSN 只经环境变量 AGENT_SYSTEM_DSN 传入,不进 argv)→ 再次部署"
|
||||
c_err " svc 不会自动迁移;本次暂存的候选在拒绝后清理,迁移请使用同一版本的制品"
|
||||
return 0
|
||||
}
|
||||
|
||||
# svc_deploy_identity_gate SRCBIN:部署候选的身份门禁——在停旧服务、覆盖制品之前
|
||||
# 执行(2026-10-12 rdm1/rdmcand 事故:停服→覆盖→启动即被身份门禁拒绝,只剩
|
||||
# 「登记版本≠磁盘制品」的混合状态且无恢复路径)。规则:
|
||||
@@ -1425,7 +1453,8 @@ svc_deploy_identity_gate() {
|
||||
data_root="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["identity"]["data_root"])' "$ENV_DIR/manifest.json")"
|
||||
if [ -f "$data_root/agent-system.db" ] || { [ "${DB_MODE:-}" = pg ] && [ -n "${DB_DSN:-}" ]; }; then
|
||||
svc_db_identity_ensure "$srcbin" || {
|
||||
c_err "[$ENV_NAME] 候选补齐 DB 身份失败(外宾行/不一致 fail-closed);旧服务/旧制品/登记保持不变"
|
||||
svc_identity_schema_missing "$srcbin" \
|
||||
|| c_err "[$ENV_NAME] 候选补齐 DB 身份失败(外宾行/不一致 fail-closed);旧服务/旧制品/登记保持不变"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
@@ -1433,7 +1462,8 @@ svc_deploy_identity_gate() {
|
||||
*)
|
||||
# established:只验证,不补行。缺行或不匹配在停旧服务前拒绝。
|
||||
svc_db_identity_verify "$srcbin" || {
|
||||
c_err "[$ENV_NAME] 候选无法验证已建立的 DB 身份;旧服务/旧制品/登记保持不变"
|
||||
svc_identity_schema_missing "$srcbin" \
|
||||
|| c_err "[$ENV_NAME] 候选无法验证已建立的 DB 身份;旧服务/旧制品/登记保持不变"
|
||||
return 1
|
||||
}
|
||||
;;
|
||||
@@ -1473,7 +1503,7 @@ PY
|
||||
iid="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["instance_id"])' "$ENV_DIR/manifest.json")"
|
||||
[ "$CLI_CONFIRM" = "$iid" ] || { c_err "[$ENV_NAME] --migrate-schema 需要 --confirm $iid"; return 1; }
|
||||
backup="$(agent_backup_root)"
|
||||
if ! find "$backup" -mindepth 1 -maxdepth 1 -type d -print -quit | grep -q .; then
|
||||
if ! grep -q . < <(find "$backup" -mindepth 1 -maxdepth 1 -type d -print -quit); then
|
||||
c_err "[$ENV_NAME] forward-only 迁移必须先 backup"
|
||||
return 1
|
||||
fi
|
||||
@@ -1506,6 +1536,38 @@ svc_write_serve_env() {
|
||||
chmod 0600 "$f" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# svc_superadmin_envfile_check:agent 的 start/restart/deploy 在停旧服务前校验本次
|
||||
# AGENT_SYSTEM_ENV_FILE(只看路径与元数据,绝不读内容)。非空须为当前用户所有、
|
||||
# 无组/其他权限位的普通文件,通过后由 record_state 记为 superadmin_envfile=;
|
||||
# 已设置但为空则清除登记。
|
||||
SUPERADMIN_ENVFILE_NEW=""
|
||||
SUPERADMIN_ENVFILE_CLEAR=0
|
||||
svc_superadmin_envfile_check() {
|
||||
[ -n "${AGENT_SYSTEM_ENV_FILE+x}" ] || return 0
|
||||
if [ -z "$AGENT_SYSTEM_ENV_FILE" ]; then
|
||||
SUPERADMIN_ENVFILE_CLEAR=1
|
||||
return 0
|
||||
fi
|
||||
local f="$AGENT_SYSTEM_ENV_FILE" dir meta owner mode
|
||||
dir="$(cd "$(dirname "$f")" 2>/dev/null && pwd)" || dir=""
|
||||
[ -n "$dir" ] && f="${dir%/}/$(basename "$f")"
|
||||
if [ -z "$dir" ] || [ ! -f "$f" ]; then
|
||||
c_err "[$ENV_NAME] 超级管理员凭据文件不存在或不是普通文件:$f(运行中的服务未改动)"
|
||||
return 1
|
||||
fi
|
||||
meta="$(stat -L -c '%u %a' "$f" 2>/dev/null)" || meta=""
|
||||
owner="${meta%% *}"; mode="${meta##* }"
|
||||
if [ -z "$meta" ] || [ "$owner" != "$(id -u)" ]; then
|
||||
c_err "[$ENV_NAME] 超级管理员凭据文件不属于当前用户:$f(运行中的服务未改动)"
|
||||
return 1
|
||||
fi
|
||||
if [ $(( 8#$mode & 8#077 )) -ne 0 ]; then
|
||||
c_err "[$ENV_NAME] 超级管理员凭据文件权限过宽($mode):$f,须 chmod 600(运行中的服务未改动)"
|
||||
return 1
|
||||
fi
|
||||
SUPERADMIN_ENVFILE_NEW="$f"
|
||||
}
|
||||
|
||||
# svc_launch_serve ARGS:env -i 只带 serve.env 与本次构造的 guard_env。
|
||||
svc_launch_serve() {
|
||||
local exec_args="$1"
|
||||
@@ -1535,11 +1597,25 @@ svc_launch_serve() {
|
||||
args+=("RDM_DISABLE_EXEC_TOOLS=$RDM_DISABLE_EXEC_TOOLS")
|
||||
fi
|
||||
# 超级管理员凭据只从本次环境转发,不写入 serve.env;缺失时由 serve 自行 fail closed。
|
||||
# 本次环境三者皆未设置时回落到 svc.env 登记的 superadmin_envfile=(只转发路径),
|
||||
# recover/watchdog 的清空环境子进程因此也能拉起认证实例。
|
||||
local sa_explicit=0 sa_file
|
||||
for k in SUPER_ADMIN_OWNER SUPER_ADMIN_PASSWORD AGENT_SYSTEM_ENV_FILE; do
|
||||
if [ -n "${!k+x}" ]; then
|
||||
args+=("$k=${!k}")
|
||||
sa_explicit=1
|
||||
fi
|
||||
done
|
||||
if [ "$sa_explicit" = 0 ]; then
|
||||
sa_file="$(svc_get superadmin_envfile)"
|
||||
if [ -n "$sa_file" ]; then
|
||||
if [ -f "$sa_file" ]; then
|
||||
args+=("AGENT_SYSTEM_ENV_FILE=$sa_file")
|
||||
else
|
||||
c_warn "[$ENV_NAME] 登记的超级管理员凭据文件已不存在:$sa_file(不转发,serve 将按缺凭据拒启)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
# shellcheck disable=SC2086
|
||||
nohup "${args[@]}" "$ENV_BIN" serve $exec_args >> "$ENV_DIR/svc.log" 2>&1 9>&- &
|
||||
}
|
||||
@@ -1700,7 +1776,7 @@ adjust_args_for_bin() {
|
||||
case "$w" in
|
||||
--auth|--config)
|
||||
flag="${w#--}"
|
||||
if printf '%s\n' "$out" | grep -Eq -- "(^|[[:space:]])-{1,2}${flag}([[:space:]]|=|$)"; then
|
||||
if grep -Eq -- "(^|[[:space:]])-{1,2}${flag}([[:space:]]|=|$)" <<<"$out"; then
|
||||
filtered="$filtered $w"
|
||||
else
|
||||
drop_next=1
|
||||
@@ -2429,7 +2505,7 @@ materialize_release() {
|
||||
fi
|
||||
if [ -f "$mf" ]; then
|
||||
local want have
|
||||
want="$(sed -n 's/.*"agent-system": *"\([0-9a-f]\{64\}\)".*/\1/p' "$mf" | head -1)"
|
||||
want="$(sed -n '/.*"agent-system": *"\([0-9a-f]\{64\}\)".*/{s//\1/p;q;}' "$mf")"
|
||||
have="$(sha256sum "$bin" | cut -d' ' -f1)"
|
||||
if [ -n "$want" ] && [ "$want" != "$have" ]; then
|
||||
c_err "[$ENV_NAME] sha256 校验失败:制品与 manifest 不符($bin)——拒绝部署"
|
||||
@@ -3317,7 +3393,7 @@ agent_do_deploy() {
|
||||
fi
|
||||
local mf="$REL_ROOT/versions/$REL_VERSION/manifest.json" want have
|
||||
if [ -f "$mf" ]; then
|
||||
want="$(sed -n 's/.*"agent-system": *"\([0-9a-f]\{64\}\)".*/\1/p' "$mf" | head -1)"
|
||||
want="$(sed -n '/.*"agent-system": *"\([0-9a-f]\{64\}\)".*/{s//\1/p;q;}' "$mf")"
|
||||
have="$(sha256sum "$relbin" | cut -d' ' -f1)"
|
||||
if [ -n "$want" ] && [ "$want" != "$have" ]; then
|
||||
c_err "[$ENV_NAME] sha256 校验失败:制品与 manifest 不符($relbin)——拒绝部署"
|
||||
@@ -3574,7 +3650,7 @@ for u in users:
|
||||
"$ENV_BIN" apikey revoke ${seed_dbf[@]+"${seed_dbf[@]}"} --owner "$seed_owner" --name svc-seed >/dev/null 2>&1 \
|
||||
|| c_warn "[$ENV_NAME] 临时播种密钥回收失败:请手动吊销 owner=$seed_owner name=svc-seed"
|
||||
fi
|
||||
if echo "$resp" | grep -q '"ok": *true'; then
|
||||
if grep -q '"ok": *true' <<<"$resp"; then
|
||||
local did=""
|
||||
[ -n "${CLI_MBASE:-${SVC_MODEL_BASE:-}}${CLI_MKEY:-${SVC_MODEL_KEY:-}}${CLI_MNAME:-${SVC_MODEL_NAME:-}}" ] && did="主模型"
|
||||
[ -n "${CLI_LBASE:-${SVC_LIGHT_BASE:-}}${CLI_LKEY:-${SVC_LIGHT_KEY:-}}${CLI_LNAME:-${SVC_LIGHT_NAME:-}}" ] && did="${did:+$did、}轻量模型"
|
||||
@@ -3801,7 +3877,7 @@ resolve_db_config() {
|
||||
# conffile 的 dsn 键(二进制自行读文件,连接串不经环境重复传递)。
|
||||
local cf_dsn=""
|
||||
if [ -n "$p_conf" ] && [ -f "$p_conf" ]; then
|
||||
cf_dsn="$(grep -oE '"dsn"[[:space:]]*:[[:space:]]*"[^"]*"' "$p_conf" 2>/dev/null | head -1 | sed -E 's/.*"([^"]*)"$/\1/')"
|
||||
cf_dsn="$(sed -n '/"dsn"[[:space:]]*:[[:space:]]*"[^"]*"/{s//\n&/;s/.*\n"dsn"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p;q;}' "$p_conf" 2>/dev/null)"
|
||||
fi
|
||||
|
||||
# mock 档与数据库配置共存 = 冲突,拒绝(行为要明确,不隐式降级)。
|
||||
@@ -3940,7 +4016,7 @@ bin_env_dsn_supported() {
|
||||
# 「支持」(回归教训:test_svc 的 legacy 桩即脚本形态)。
|
||||
[ "$(head -c 4 "$1" 2>/dev/null)" = $'\x7fELF' ] || return 1
|
||||
command -v strings >/dev/null 2>&1 || return 1
|
||||
strings "$1" 2>/dev/null | grep -q 'AGENT_SYSTEM_DSN' && return 0
|
||||
grep -q 'AGENT_SYSTEM_DSN' < <(strings "$1" 2>/dev/null) && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -4357,7 +4433,7 @@ db_create() {
|
||||
SVC_KIND=postgres
|
||||
SRC_LABEL="postgres@local"
|
||||
local pgver merged_tags
|
||||
pgver="$("$pgbin/postgres" --version 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i ~ /^[0-9]+(\.[0-9]+)+$/) {print $i; exit}}' || true)"
|
||||
pgver="$(awk '{for(i=1;i<=NF;i++) if ($i ~ /^[0-9]+(\.[0-9]+)+$/) {print $i; exit}}' < <("$pgbin/postgres" --version 2>/dev/null) || true)"
|
||||
merged_tags="$(svc_merge_tags)"
|
||||
{
|
||||
printf 'kind=postgres\n'
|
||||
@@ -4412,7 +4488,7 @@ pg_do_start() {
|
||||
# 首次启动:建默认库(已存在则跳过)。
|
||||
local dbname; dbname="$(svc_get dbname)"
|
||||
if [ -n "$dbname" ]; then
|
||||
if ! pg_psql "SELECT 1 FROM pg_database WHERE datname='$dbname'" tA | grep -q 1; then
|
||||
if ! grep -q 1 < <(pg_psql "SELECT 1 FROM pg_database WHERE datname='$dbname'" tA); then
|
||||
if pg_psql "CREATE DATABASE \"$dbname\""; then
|
||||
c_ok "[$ENV_NAME] 已建库:$dbname"
|
||||
else
|
||||
@@ -4920,7 +4996,7 @@ EOF
|
||||
[ -n "$target" ] && { c_err "--clear 不接受文件参数"; return 2; }
|
||||
local exec_old new_exec
|
||||
exec_old="$(svc_get exec)"
|
||||
if printf '%s' "$exec_old" | grep -q -- '--config '; then
|
||||
if grep -q -- '--config ' <<<"$exec_old"; then
|
||||
new_exec="$(printf '%s' "$exec_old" | sed -E 's/(^| )--config [^ ]+//; s/ +/ /g; s/^ //; s/ $//')"
|
||||
svc_set_key exec "$new_exec"
|
||||
fi
|
||||
@@ -4946,13 +5022,13 @@ EOF
|
||||
c_err "(键与取值以 config explain 为准;先修正文件再切换)"
|
||||
return 2
|
||||
fi
|
||||
if ! "$ENV_BIN" serve -h 2>&1 | grep -Eq -- "(^|[[:space:]])-{1,2}config([[:space:]]|=|$)"; then
|
||||
if ! grep -Eq -- "(^|[[:space:]])-{1,2}config([[:space:]]|=|$)" < <("$ENV_BIN" serve -h 2>&1); then
|
||||
c_warn "[$ENV_NAME] 当前产物不识别 --config(旧版本二进制)——绑定已登记,但重启将忽略该参数"
|
||||
fi
|
||||
fi
|
||||
local exec_old new_exec
|
||||
exec_old="$(svc_get exec)"
|
||||
if printf '%s' "$exec_old" | grep -q -- '--config '; then
|
||||
if grep -q -- '--config ' <<<"$exec_old"; then
|
||||
new_exec="$(printf '%s' "$exec_old" | sed -E "s|--config [^ ]+|--config $abs|")"
|
||||
else
|
||||
new_exec="${exec_old:+$exec_old }--config $abs"
|
||||
@@ -5037,7 +5113,7 @@ EOF
|
||||
[ -n "$target" ] && { c_err "--clear 不接受服务名参数"; return 2; }
|
||||
local exec_old new_exec
|
||||
exec_old="$(svc_get exec)"
|
||||
if printf '%s' "$exec_old" | grep -q -- '--dsn '; then
|
||||
if grep -q -- '--dsn ' <<<"$exec_old"; then
|
||||
new_exec="$(printf '%s' "$exec_old" | sed -E 's/(^| )--dsn [^ ]+//; s/ +/ /g; s/^ //; s/ $//')"
|
||||
svc_set_key exec "$new_exec"
|
||||
fi
|
||||
@@ -5084,7 +5160,7 @@ EOF
|
||||
# 实例运行中:确保目标库存在(缺则建,幂等)。
|
||||
st="$(svc_pid_state "$target")"
|
||||
if [ "${st%% *}" = alive ]; then
|
||||
if ! pg_run "$thost" "$tport" "$tuser" "$tpass" postgres "SELECT 1 FROM pg_database WHERE datname='$tdb'" tA | grep -q 1; then
|
||||
if ! grep -q 1 < <(pg_run "$thost" "$tport" "$tuser" "$tpass" postgres "SELECT 1 FROM pg_database WHERE datname='$tdb'" tA); then
|
||||
if pg_run "$thost" "$tport" "$tuser" "$tpass" postgres "CREATE DATABASE \"$tdb\"" >/dev/null; then
|
||||
c_ok "[$target] 已建库:$tdb"
|
||||
else
|
||||
@@ -5097,7 +5173,7 @@ EOF
|
||||
dsn="postgresql://$tuser:$tpass@$thost:$tport/$tdb"
|
||||
# 写持久化配置(托管绑定形态):dbref= + db=pg;清除直接 dsn=;剥离 exec 残留 --dsn。
|
||||
# DSN 不落 exec=——启动时由实例登记重建(实例端口/密码变更自动跟随,密码不重复保存)。
|
||||
if printf '%s' "$exec_now" | grep -q -- '--dsn '; then
|
||||
if grep -q -- '--dsn ' <<<"$exec_now"; then
|
||||
local new_exec
|
||||
new_exec="$(printf '%s' "$exec_now" | sed -E 's/(^| )--dsn [^ ]+//; s/ +/ /g; s/^ //; s/ $//')"
|
||||
svc_set_key exec "$new_exec"
|
||||
@@ -5153,7 +5229,7 @@ agent_backup_snapshot() { # $1=dest:svc.env + history.jsonl + data/(*.db 一
|
||||
[ -f "$ENV_DATA/$rel" ] || continue
|
||||
case "$rel" in
|
||||
*"'"*|*'"'*)
|
||||
if head -c 15 "$ENV_DATA/$rel" | grep -qa '^SQLite format 3$'; then
|
||||
if grep -qa '^SQLite format 3$' < <(head -c 15 "$ENV_DATA/$rel"); then
|
||||
c_err "[$ENV_NAME] 路径含引号,无法走 sqlite 一致性导出:$rel"
|
||||
return 1
|
||||
fi
|
||||
@@ -5166,7 +5242,7 @@ agent_backup_snapshot() { # $1=dest:svc.env + history.jsonl + data/(*.db 一
|
||||
rm -f "$dest/data/$rel-wal" "$dest/data/$rel-shm" # .backup 已并入 WAL 内容
|
||||
else
|
||||
rm -f "$dest/data/$rel.bk" # 失败尝试的半成品不留在快照里
|
||||
if head -c 15 "$ENV_DATA/$rel" | grep -qa '^SQLite format 3$'; then
|
||||
if grep -qa '^SQLite format 3$' < <(head -c 15 "$ENV_DATA/$rel"); then
|
||||
c_err "[$ENV_NAME] SQLite 一致性备份/自检失败:$rel"
|
||||
return 1
|
||||
fi
|
||||
@@ -5446,7 +5522,7 @@ svc_pid_state() { # NAME → "alive <pid>" | "dead"
|
||||
[ -f "$d/svc.pid" ] || { printf dead; return 0; }
|
||||
pid="$(cat "$d/svc.pid" 2>/dev/null || true)"
|
||||
[ -n "$pid" ] || { printf dead; return 0; }
|
||||
mark="$(sed -n 's/^pidcheck=//p' "$d/svc.env" 2>/dev/null | head -1)"
|
||||
mark="$(sed -n '/^pidcheck=/{s///p;q;}' "$d/svc.env" 2>/dev/null)"
|
||||
[ -z "$mark" ] && mark="agent-system"
|
||||
if pid_alive_mark "$pid" "$mark"; then printf 'alive %s' "$pid"; else printf dead; fi
|
||||
return 0
|
||||
@@ -5479,7 +5555,7 @@ db_ref_of() {
|
||||
cf="$(env_kv_of "$name" conffile)"
|
||||
[ -z "$cf" ] && cf="$(printf '%s' "$(env_kv_of "$name" exec)" | sed -n 's/.*--config \([^ ]*\).*/\1/p')"
|
||||
if [ -n "$cf" ] && [ -f "$cf" ]; then
|
||||
dsn="$(grep -oE '"dsn"[[:space:]]*:[[:space:]]*"[^"]*"' "$cf" 2>/dev/null | head -1 | sed -E 's/.*"([^"]*)"$/\1/')"
|
||||
dsn="$(sed -n '/"dsn"[[:space:]]*:[[:space:]]*"[^"]*"/{s//\n&/;s/.*\n"dsn"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p;q;}' "$cf" 2>/dev/null)"
|
||||
fi
|
||||
fi
|
||||
[ -n "$dsn" ] || return 0
|
||||
@@ -5503,10 +5579,10 @@ global_list() {
|
||||
while IFS= read -r name; do names+=("$name"); done < <(registered_names)
|
||||
for name in ${names[@]+"${names[@]}"}; do
|
||||
d="$RUNTIME/$name"
|
||||
kind="$(sed -n 's/^kind=//p' "$d/svc.env" | head -1)"; [ -n "$kind" ] || kind=agent
|
||||
addr="$(sed -n 's/^addr=//p' "$d/svc.env" | head -1)"
|
||||
src="$(sed -n 's/^source=//p' "$d/svc.env" | head -1)"
|
||||
tags="$(sed -n 's/^tags=//p' "$d/svc.env" | head -1)"
|
||||
kind="$(sed -n '/^kind=/{s///p;q;}' "$d/svc.env")"; [ -n "$kind" ] || kind=agent
|
||||
addr="$(sed -n '/^addr=/{s///p;q;}' "$d/svc.env")"
|
||||
src="$(sed -n '/^source=/{s///p;q;}' "$d/svc.env")"
|
||||
tags="$(sed -n '/^tags=/{s///p;q;}' "$d/svc.env")"
|
||||
if [ -n "${LIST_TAG:-}" ]; then
|
||||
case ",$tags," in *",${LIST_TAG},"*) ;; *) continue ;; esac
|
||||
fi
|
||||
@@ -5553,6 +5629,8 @@ env_status_detail() {
|
||||
cfile="$(svc_get conffile)"
|
||||
[ -z "$cfile" ] && cfile="$(printf '%s' "$(svc_get exec)" | sed -n 's/.*--config \([^ ]*\).*/\1/p')"
|
||||
printf ' 配置文件 %s\n' "${cfile:-无(命令行参数形态;<name> config <file> 绑定)}"
|
||||
local saf; saf="$(svc_get superadmin_envfile)"
|
||||
[ -n "$saf" ] && printf ' 超管凭据文件 %s(仅登记路径)\n' "$saf"
|
||||
printf ' 监听守卫 insecureListen=%s allowedHosts=%s\n' \
|
||||
"$(env_var "$ENV_NAME" INSECURE_LISTEN)" \
|
||||
"$(env_var "$ENV_NAME" ALLOWED_HOSTS)"
|
||||
@@ -5673,7 +5751,7 @@ info_file_mode() {
|
||||
# info 遍历多服务时使用;无则空)。
|
||||
env_kv_of() {
|
||||
[ -f "$RUNTIME/$1/svc.env" ] || return 0
|
||||
sed -n "s/^$2=//p" "$RUNTIME/$1/svc.env" | head -1
|
||||
sed -n "/^$2=/{s///p;q;}" "$RUNTIME/$1/svc.env"
|
||||
}
|
||||
|
||||
# info_snapshot_lines FILE:逐行打印 KEY=VALUE 内容快照(按 CLI_REVEAL 决定掩码)。
|
||||
@@ -5936,7 +6014,7 @@ cmd_update() {
|
||||
# rdm@ 走独立 RDM 源仓。
|
||||
local latest_sha="" latest_rel="" latest_rdm="" need_rel=0 need_dev=0 need_rdm=0 e_src
|
||||
for e in ${targets[@]+"${targets[@]}"}; do
|
||||
e_src="$(sed -n 's/^source=//p' "$RUNTIME/$e/svc.env" | head -1)"
|
||||
e_src="$(sed -n '/^source=/{s///p;q;}' "$RUNTIME/$e/svc.env")"
|
||||
case "$e_src" in release@*) need_rel=1 ;; local@*|dev@*) need_dev=1 ;; rdm@*) need_rdm=1 ;; esac
|
||||
done
|
||||
local fetch_note="" src_args=""
|
||||
@@ -5974,7 +6052,7 @@ cmd_update() {
|
||||
for e in ${targets[@]+"${targets[@]}"}; do
|
||||
local kind src cur st_running
|
||||
kind="$(svc_kind "$e")"
|
||||
src="$(sed -n 's/^source=//p' "$RUNTIME/$e/svc.env" | head -1)"
|
||||
src="$(sed -n '/^source=/{s///p;q;}' "$RUNTIME/$e/svc.env")"
|
||||
to=""
|
||||
latest_disp=""
|
||||
case "$kind" in
|
||||
@@ -6083,7 +6161,7 @@ cmd_update() {
|
||||
# 每个服务独立子进程、清空环境,避免批内 AGENT_SYSTEM_* 串实例。
|
||||
# 原样带上 plugins=,避免在终端上再次询问,也避免 ${src##*@} 把插件提交当成基座。
|
||||
local plug plug_args=()
|
||||
plug="$(sed -n 's/^plugins=//p' "$RUNTIME/$name/svc.env" | head -1)"
|
||||
plug="$(sed -n '/^plugins=/{s///p;q;}' "$RUNTIME/$name/svc.env")"
|
||||
if [ -n "$plug" ]; then
|
||||
plug_args=(--with "$plug")
|
||||
else
|
||||
@@ -6429,6 +6507,12 @@ main() {
|
||||
exit 2
|
||||
fi
|
||||
env_config "$ENV_NAME"
|
||||
case "$action" in
|
||||
start|restart|deploy)
|
||||
if [ "$SVC_KIND" = agent ]; then
|
||||
svc_superadmin_envfile_check || exit 1
|
||||
fi ;;
|
||||
esac
|
||||
# 生命周期动作持 .start.lock(wait 模式;进程退出自动释放)——deploy 全程含
|
||||
# 构建窗口,防 recover/并发 start 在停机-换产物间隙抢跑。只读动作不加锁。
|
||||
case "$action" in
|
||||
@@ -6507,7 +6591,13 @@ finally:
|
||||
PY
|
||||
svc_db_marker_set pending || exit 1
|
||||
svc_db_identity_ensure || { c_err "[$ENV_NAME] adopt ensure 失败"; exit 1; }
|
||||
c_ok "[$ENV_NAME] adopt 完成,db_marker=established" ;;
|
||||
local adopt_marker; adopt_marker="$(svc_db_marker)"
|
||||
if [ "$adopt_marker" = established ]; then
|
||||
c_ok "[$ENV_NAME] adopt 完成,db_marker=established"
|
||||
else
|
||||
c_warn "[$ENV_NAME] adopt 未建立 DB 身份:db_marker=$adopt_marker(当前已装制品不具备 identity 能力,无法建立身份)"
|
||||
c_warn " 部署具备 identity ensure/verify 的制品后,由候选身份门禁补齐"
|
||||
fi ;;
|
||||
status) env_status_detail ;;
|
||||
logs) [ -f "$ENV_LOG" ] && tail -n "${CLI_LOGS_N:-50}" -f "$ENV_LOG" || c_warn "[$ENV_NAME] 暂无日志" ;;
|
||||
esac
|
||||
|
||||
+15
-14
@@ -20,13 +20,13 @@ fetch_target() {
|
||||
}
|
||||
origin_transport_unavailable() { # $1=失败的远端命令 stderr → 仅明确的传输失败(DNS/连接超时/拒绝/重置/无路由,或本地缺失仓库路径)返回 0;认证/授权/权限与未知错误返回 1
|
||||
local err=$1 url path
|
||||
if printf '%s\n' "$err" | grep -qiE 'authentication failed|permission denied|unauthori[sz]ed|forbidden|HTTP[^0-9]*40[13]|\b40[13]\b|could not read (username|password)|terminal prompts disabled|invalid credentials|access denied|host key verification failed'; then
|
||||
if grep -qiE 'authentication failed|permission denied|unauthori[sz]ed|forbidden|HTTP[^0-9]*40[13]|\b40[13]\b|could not read (username|password)|terminal prompts disabled|invalid credentials|access denied|host key verification failed' <<<"$err"; then
|
||||
return 1
|
||||
fi
|
||||
if printf '%s\n' "$err" | grep -qiE 'could not resolve (host|hostname|proxy)|name or service not known|temporary failure in name resolution|nodename nor servname|connection timed out|operation timed out|timed out after|connection refused|connection reset|no route to host|network is unreachable|couldn.t connect to server'; then
|
||||
if grep -qiE 'could not resolve (host|hostname|proxy)|name or service not known|temporary failure in name resolution|nodename nor servname|connection timed out|operation timed out|timed out after|connection refused|connection reset|no route to host|network is unreachable|couldn.t connect to server' <<<"$err"; then
|
||||
return 0
|
||||
fi
|
||||
printf '%s\n' "$err" | grep -qF 'does not appear to be a git repository' || return 1
|
||||
grep -qF 'does not appear to be a git repository' <<<"$err" || return 1
|
||||
url=$(git remote get-url origin 2>/dev/null) || return 1
|
||||
case "$url" in
|
||||
file://*) path=${url#file://} ;;
|
||||
@@ -211,12 +211,12 @@ cmd_precheck() {
|
||||
cmd_start() {
|
||||
need_repo
|
||||
local name="${1:-}"; [ -n "$name" ] || die "usage: task.sh start <feature|fix|hotfix|release>/<slug>"
|
||||
echo "$name" | grep -qE '^(feature|fix|hotfix|release)/[A-Za-z0-9._-]+$' || die "branch name must match (feature|fix|hotfix|release)/<slug>"
|
||||
grep -qE '^(feature|fix|hotfix|release)/[A-Za-z0-9._-]+$' <<<"$name" || die "branch name must match (feature|fix|hotfix|release)/<slug>"
|
||||
local kind="${TASK_BRANCH_KIND:-standalone}" initiative="${TASK_INITIATIVE:-}"
|
||||
case "$kind" in standalone|initiative|child) ;; *) die "TASK_BRANCH_KIND must be standalone, initiative or child" ;; esac
|
||||
if [ "$kind" = child ]; then
|
||||
[ -n "$initiative" ] || die "child task requires INITIATIVE=<parent initiative branch>"
|
||||
echo "$initiative" | grep -qE '^(feature|fix|hotfix|release)/[A-Za-z0-9._-]+$' || die "invalid parent initiative branch"
|
||||
grep -qE '^(feature|fix|hotfix|release)/[A-Za-z0-9._-]+$' <<<"$initiative" || die "invalid parent initiative branch"
|
||||
case "${name#*/}" in "${initiative#*/}"-?*) ;; *) die "child slug must start with parent initiative slug plus '-'" ;; esac
|
||||
fi
|
||||
local slug planned_child=0
|
||||
@@ -312,8 +312,8 @@ cmd_start() {
|
||||
esac
|
||||
if [ "$kind" != child ] && [ -n "${TASK_PARENT_BRANCH:-}${TASK_PARENT_PR:-}" ]; then
|
||||
[ -n "${TASK_PARENT_BRANCH:-}" ] && [ -n "${TASK_PARENT_PR:-}" ] || die "stacked task requires TASK_PARENT_BRANCH and TASK_PARENT_PR"
|
||||
echo "$TASK_PARENT_BRANCH" | grep -qE '^(feature|fix|hotfix|release)/[A-Za-z0-9._-]+$' || die "invalid parent task branch"
|
||||
echo "$TASK_PARENT_PR" | grep -qE '^https?://[^[:space:]]+/pulls/[0-9]+$' || die "TASK_PARENT_PR must be a parent PR URL"
|
||||
grep -qE '^(feature|fix|hotfix|release)/[A-Za-z0-9._-]+$' <<<"$TASK_PARENT_BRANCH" || die "invalid parent task branch"
|
||||
grep -qE '^https?://[^[:space:]]+/pulls/[0-9]+$' <<<"$TASK_PARENT_PR" || die "TASK_PARENT_PR must be a parent PR URL"
|
||||
case "${name#*/}" in "${TASK_PARENT_BRANCH#*/}"-?*) ;; *) die "stacked child slug must start with parent slug plus '-'" ;; esac
|
||||
parent_head=$(git rev-parse --verify "refs/heads/${TASK_PARENT_BRANCH}^{commit}") || die "parent branch missing locally"
|
||||
git merge-base --is-ancestor "$rmain" "$parent_head" || die "parent branch does not include current origin/${TARGET_BRANCH}; sync it first"
|
||||
@@ -640,13 +640,13 @@ cmd_sync() {
|
||||
# 默认闭合方式是 close --self:任务分支在合入前把本分支的卡改为"已完成(合入前闭合)"并归档,
|
||||
# 随本 PR 合入;上述合入后回填只用于遗留卡。
|
||||
|
||||
card_of_branch() { sed -n 's/^- 工作区:.*分支:\([A-Za-z0-9._/-]\+\).*/\1/p' "$1" | head -1; }
|
||||
card_status() { sed -n 's/^.*;状态:\([^;]*\).*/\1/p' "$1" | head -1; }
|
||||
card_of_branch() { sed -n '/^- 工作区:.*分支:\([A-Za-z0-9._/-]\+\).*/{s//\1/p;q;}' "$1"; }
|
||||
card_status() { sed -n '/^.*;状态:\([^;]*\).*/{s//\1/p;q;}' "$1"; }
|
||||
# 两级分支元数据行:'- 分支类型:<kind>;initiative_id:…;root_target:…;immediate_target:…;accepting_requirements:…'。
|
||||
# 旧卡没有该行:按 standalone、root/immediate 均为目标分支、不接收新需求处理。
|
||||
card_field() { sed -n "s/^- 分支类型:.*;$2:\([^;]*\).*/\1/p" "$1" 2>/dev/null | head -1; }
|
||||
card_r0() { sed -n 's/^- 权威仓:.*;R0(initial_base_sha)= \([0-9a-f]\+\)[[:space:]]*$/\1/p' "$1" 2>/dev/null | head -1; }
|
||||
card_branch_kind() { local v; v=$(sed -n 's/^- 分支类型:\([^;]*\).*/\1/p' "$1" 2>/dev/null | head -1); printf '%s\n' "${v:-standalone}"; }
|
||||
card_field() { sed -n "/^- 分支类型:.*;$2:\([^;]*\).*/{s//\1/p;q;}" "$1" 2>/dev/null; }
|
||||
card_r0() { sed -n '/^- 权威仓:.*;R0(initial_base_sha)= \([0-9a-f]\+\)[[:space:]]*$/{s//\1/p;q;}' "$1" 2>/dev/null; }
|
||||
card_branch_kind() { local v; v=$(sed -n '/^- 分支类型:\([^;]*\).*/{s//\1/p;q;}' "$1" 2>/dev/null); printf '%s\n' "${v:-standalone}"; }
|
||||
card_initiative_id() { card_field "$1" initiative_id; }
|
||||
card_root_target() { local v; v=$(card_field "$1" root_target); printf '%s\n' "${v:-${TARGET_BRANCH}}"; }
|
||||
card_immediate_target() { local v; v=$(card_field "$1" immediate_target); printf '%s\n' "${v:-${TARGET_BRANCH}}"; }
|
||||
@@ -1067,7 +1067,7 @@ EOF
|
||||
-d "{\"head\":\"${headbr}\",\"base\":\"${pr_base}\",\"title\":\"Close merged task cards (${headbr})\",\"body\":\"Auto-generated by task.sh close; see commit message for evidence.\"}" 2>&1) || resp=""
|
||||
pr_url=$(printf '%s' "$resp" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("html_url","") if isinstance(d,dict) else "")' 2>/dev/null || true)
|
||||
[ -n "$pr_url" ] && break
|
||||
echo "[close] 建 PR 第 ${attempt} 次未成功${resp:+($(printf '%s' "$resp" | head -c 120))},退避重试" >&2
|
||||
echo "[close] 建 PR 第 ${attempt} 次未成功${resp:+($(head -c 120 <<<"$resp"))},退避重试" >&2
|
||||
[ "$attempt" = 4 ] || sleep $((attempt * 3))
|
||||
done
|
||||
if [ -z "$pr_url" ]; then
|
||||
@@ -1131,7 +1131,8 @@ cmd_finish() {
|
||||
# registered-root, owner, lock, recursive dirt and ignored-file checks have passed.
|
||||
check_worktree finish
|
||||
local -a removal=(worktree remove)
|
||||
if git ls-files --stage | grep -q '^160000 '; then
|
||||
# Read the whole listing: an early-exit reader SIGPIPEs git on large indexes and pipefail drops --force.
|
||||
if git ls-files --stage | awk '$1 == "160000" { found = 1 } END { exit !found }'; then
|
||||
removal+=(--force)
|
||||
fi
|
||||
if ! git -C "$mainrepo" "${removal[@]}" "$root"; then
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
"""Shared free-port helper for svc tests (not a test module)."""
|
||||
import socket
|
||||
|
||||
|
||||
def free_test_port():
|
||||
"""Return a port the kernel reports free on 127.0.0.1 right now.
|
||||
|
||||
Each call opens a fresh reservation and closes it before returning, so the
|
||||
caller (e.g. `svc deploy`) can bind the port itself. Ports are not pooled.
|
||||
"""
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as reservation:
|
||||
reservation.bind(("127.0.0.1", 0))
|
||||
return reservation.getsockname()[1]
|
||||
@@ -1,6 +1,11 @@
|
||||
# RDM 制品钉扎。三行都是 40 位 hex。升级走 PR,不在 CI 里执行候选文件。
|
||||
# legacy 没有 version --json。prev 是身份能力合入(#291)。sha 与 prev 钉同一
|
||||
# backend(0f192a4),schema 兼容,但产品树不同,切换能在 release 名上看到。
|
||||
sha=c6304224f995747ea3ffcb8d0e81f3ad46a96546
|
||||
prev_sha=3484ec225b0087a9065466ed436ea97a768691ef
|
||||
# sha:生命周期套件的目标版本(6-pin 布局)。
|
||||
# prev_sha:来回部署身份稳定性测试的起点。选取受两条硬约束——与 sha 的 pins 布局一致,
|
||||
# 且两者之间无跨 schema 迁移(scripts/svc.sh 的 schema 兼容闸会在停旧服务前拒绝不兼容切换)——
|
||||
# 而非追求版本跨度。取 sha 在 RDM 主线上的第一父提交(交付合入前的 main);代价是 prev 与 sha
|
||||
# 仅隔一次合并,版本跨度小,往返部署覆盖的版本距离有限。
|
||||
# legacy_sha:承载「无身份」语义(没有 version --json),必然早于 6-pin 布局。
|
||||
# 三者的 pins 布局可能不同;CI 按每个 pin 自身的 gitlink 清单物化并逐个断言,不要求布局统一。
|
||||
sha=bc8db886606e88201e7f7210a1357d22dc84124b
|
||||
prev_sha=59b3624325fe132ef92ccee83dd9ddc6c3fcc521
|
||||
legacy_sha=72bb1fec79d64c2af5e49416640712320816c3b4
|
||||
|
||||
@@ -153,7 +153,7 @@ func TestTwoInstancesOverSharedPG(t *testing.T) {
|
||||
t.Fatalf("stale event replayed: seq %d <= since %d", ev.seq, lastSeq)
|
||||
}
|
||||
case <-time.After(8 * time.Second):
|
||||
t.Fatal("event written via A not visible on B SSE within 8s")
|
||||
t.Fatalf("event written via A not visible on B SSE within 8s: no event with seq > %d observed on %s", lastSeq, e.instanceOf(uc.streamB))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -212,17 +212,24 @@ func TestTwoInstancesOverSharedPG(t *testing.T) {
|
||||
// (interrupted),随后原实例重新占用会话并推进。
|
||||
e.a = e.startInstance(t, e.a.name, strings.TrimPrefix(e.a.base, "http://"))
|
||||
var detail map[string]any
|
||||
deadline := time.Now().Add(httpWait)
|
||||
var statuses []string
|
||||
reconciled := false
|
||||
start := time.Now()
|
||||
deadline := start.Add(httpWait)
|
||||
for time.Now().Before(deadline) {
|
||||
e.do(t, uc.api, "GET", "/api/conversations/"+convID, nil, &detail)
|
||||
if runs, ok := detail["runs"].([]any); ok && len(runs) > 0 {
|
||||
last, _ := runs[len(runs)-1].(map[string]any)
|
||||
if last["status"] == "interrupted" {
|
||||
break
|
||||
}
|
||||
statuses = runStatuses(detail)
|
||||
if n := len(statuses); n > 0 && statuses[n-1] == "interrupted" {
|
||||
reconciled = true
|
||||
break
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
if !reconciled {
|
||||
// 不能落到下面的 POST:未收口时它会得到看似无关的 409 in-flight。
|
||||
t.Fatalf("restarted %s never reported the crashed run as interrupted: waited %v (budget %v), last run status never became \"interrupted\"; run statuses %v; log:\n%s",
|
||||
e.instanceOf(uc.api), time.Since(start).Round(time.Millisecond), httpWait, statuses, e.a.log.String())
|
||||
}
|
||||
e.do(t, uc.api, "POST", "/api/conversations/"+convID+"/messages", map[string]any{"text": "A resumes after restart"}, nil)
|
||||
})
|
||||
|
||||
@@ -230,6 +237,36 @@ func TestTwoInstancesOverSharedPG(t *testing.T) {
|
||||
e.killNine(t, e.b)
|
||||
e.b = e.startInstance(t, e.b.name, strings.TrimPrefix(e.b.base, "http://"))
|
||||
|
||||
// 断言前先等重启实例给出完整的恢复视图:对账收口的 interrupted run 可见,
|
||||
// 且会话内无进行中 run。断言本身不依赖这次轮询的结果。
|
||||
var statuses []string
|
||||
recovered := false
|
||||
start := time.Now()
|
||||
readyBy := start.Add(httpWait)
|
||||
for time.Now().Before(readyBy) {
|
||||
var view map[string]any
|
||||
e.do(t, uc.apiB, "GET", "/api/conversations/"+convID, nil, &view)
|
||||
statuses = runStatuses(view)
|
||||
interrupted, busy := false, false
|
||||
for _, s := range statuses {
|
||||
switch s {
|
||||
case "interrupted":
|
||||
interrupted = true
|
||||
case "pending", "running", "waiting_hitl":
|
||||
busy = true
|
||||
}
|
||||
}
|
||||
if interrupted && !busy {
|
||||
recovered = true
|
||||
break
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
if !recovered {
|
||||
t.Fatalf("restarted %s never showed the recovered view (an interrupted run and no pending/running/waiting_hitl run): waited %v (budget %v); run statuses %v; logA:\n%s\nlogB:\n%s",
|
||||
e.instanceOf(uc.apiB), time.Since(start).Round(time.Millisecond), httpWait, statuses, e.a.log.String(), e.b.log.String())
|
||||
}
|
||||
|
||||
var detail map[string]any
|
||||
e.do(t, uc.apiB, "GET", "/api/conversations/"+convID, nil, &detail)
|
||||
msgs, _ := detail["messages"].([]any)
|
||||
@@ -255,7 +292,7 @@ func TestTwoInstancesOverSharedPG(t *testing.T) {
|
||||
}
|
||||
seen = ev.seq
|
||||
case <-deadline:
|
||||
t.Fatalf("replay stalled at seq %d / %d", seen, lastSeq)
|
||||
t.Fatalf("replay stalled at seq %d / %d: event seq %d never observed on %s SSE within 15s", seen, lastSeq, seen+1, e.instanceOf(uc.streamB))
|
||||
}
|
||||
}
|
||||
// 重启后写入仍可推进。
|
||||
@@ -410,6 +447,7 @@ func (e *env) uploadArtifact(t *testing.T, c *http.Client, projectID, name strin
|
||||
|
||||
func (e *env) waitHealthy(t *testing.T, inst *instance) {
|
||||
t.Helper()
|
||||
last := "no probe completed"
|
||||
deadline := time.Now().Add(60 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
resp, err := http.Get(inst.base + "/api/health")
|
||||
@@ -419,10 +457,13 @@ func (e *env) waitHealthy(t *testing.T, inst *instance) {
|
||||
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusUnauthorized {
|
||||
return
|
||||
}
|
||||
last = "status " + strconv.Itoa(resp.StatusCode)
|
||||
} else {
|
||||
last = err.Error()
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
t.Fatalf("instance %s not healthy in 60s; log:\n%s", inst.name, inst.log.String())
|
||||
t.Fatalf("instance %s (%s) not healthy in 60s: GET /api/health never returned 200/401 (last probe: %s); log:\n%s", inst.name, inst.base, last, inst.log.String())
|
||||
}
|
||||
|
||||
func (e *env) stopInstance(inst *instance) {
|
||||
@@ -485,7 +526,7 @@ func (e *env) loginAlice(t *testing.T) *userClient {
|
||||
}, nil)
|
||||
|
||||
apiKey := e.cliOut(t, "apikey", "create", "--dsn", e.dbDSN, "--owner", aliceOwner, "--name", "mi-acceptance")
|
||||
// IAM U4 把建项目改为系统动作 project.create:由引导管理员经真实 API 授予 alice。
|
||||
// 建项目需角色上的 project.create:由引导管理员经真实 API 建角色并绑定 alice。
|
||||
e.grantAliceProjectCreate(t)
|
||||
uc := &userClient{
|
||||
api: &http.Client{Timeout: httpWait, Transport: bearerRT{key: apiKey}},
|
||||
@@ -498,44 +539,53 @@ func (e *env) loginAlice(t *testing.T) *userClient {
|
||||
return uc
|
||||
}
|
||||
|
||||
// grantAliceProjectCreate 以 mi-root 身份经 admin API 为 alice 追加系统授权
|
||||
// project.create(保留其已有授权;PUT 为整体替换,按 authorityEpoch 乐观并发)。
|
||||
// mi-root 由 serve 启动时的 SUPER_ADMIN_* 建立,可直接为其发 API key。
|
||||
// grantAliceProjectCreate 以 mi-root 身份经 admin API 为 alice 授予项目访问:
|
||||
// project.create 只由已启用账号所绑定的已启用角色上的单一布尔决定,管理员
|
||||
// 身份或系统授权都不再隐含它。故新建角色、写入 project.create
|
||||
// (allowed=capAllowed=true,按 roleVersion 乐观并发),再按 alice 的
|
||||
// stateVersion 绑定该角色。mi-root 由 serve 启动时的 SUPER_ADMIN_* 建立,
|
||||
// 可直接为其发 API key。
|
||||
func (e *env) grantAliceProjectCreate(t *testing.T) {
|
||||
t.Helper()
|
||||
rootKey := e.cliOut(t, "apikey", "create", "--dsn", e.dbDSN, "--owner", "mi-root", "--name", "mi-acceptance-root")
|
||||
root := &http.Client{Timeout: httpWait, Transport: bearerRT{key: rootKey}}
|
||||
var users []struct {
|
||||
UserID string `json:"userId"`
|
||||
Owner string `json:"owner"`
|
||||
UserID string `json:"userId"`
|
||||
Owner string `json:"owner"`
|
||||
StateVersion int64 `json:"stateVersion"`
|
||||
}
|
||||
e.do(t, root, "GET", "/api/admin/users", nil, &users)
|
||||
userID := ""
|
||||
userID, stateVersion := "", int64(0)
|
||||
for _, u := range users {
|
||||
if u.Owner == aliceOwner {
|
||||
userID = u.UserID
|
||||
userID, stateVersion = u.UserID, u.StateVersion
|
||||
}
|
||||
}
|
||||
if userID == "" {
|
||||
t.Fatalf("user %s not found in /api/admin/users", aliceOwner)
|
||||
}
|
||||
var grants struct {
|
||||
Actions []string `json:"actions"`
|
||||
AuthorityEpoch int64 `json:"authorityEpoch"`
|
||||
var role struct {
|
||||
RoleID string `json:"roleId"`
|
||||
RoleVersion int64 `json:"roleVersion"`
|
||||
}
|
||||
e.do(t, root, "GET", "/api/admin/users/"+userID+"/system-grants", nil, &grants)
|
||||
actions := grants.Actions
|
||||
has := false
|
||||
for _, a := range actions {
|
||||
has = has || a == "project.create"
|
||||
e.do(t, root, "POST", "/api/admin/roles", map[string]any{
|
||||
"name": "mi-project-access",
|
||||
"description": "multi-instance acceptance: project.create",
|
||||
}, &role)
|
||||
if role.RoleID == "" {
|
||||
t.Fatal("POST /api/admin/roles returned no roleId")
|
||||
}
|
||||
if !has {
|
||||
actions = append(actions, "project.create")
|
||||
}
|
||||
e.do(t, root, "PUT", "/api/admin/users/"+userID+"/system-grants", map[string]any{
|
||||
"expectedAuthorityEpoch": grants.AuthorityEpoch,
|
||||
"idempotencyKey": "mi-alice-project-create",
|
||||
"actions": actions,
|
||||
e.do(t, root, "PUT", "/api/admin/roles/"+role.RoleID+"/permissions", map[string]any{
|
||||
"expectedRoleVersion": role.RoleVersion,
|
||||
"idempotencyKey": "mi-role-project-create",
|
||||
"changes": []map[string]any{
|
||||
{"actionId": "project.create", "allowed": true, "capAllowed": true},
|
||||
},
|
||||
}, nil)
|
||||
e.do(t, root, "PUT", "/api/admin/users/"+userID+"/role", map[string]any{
|
||||
"roleId": role.RoleID,
|
||||
"expectedStateVersion": stateVersion,
|
||||
"idempotencyKey": "mi-alice-project-access-role",
|
||||
}, nil)
|
||||
}
|
||||
|
||||
@@ -633,6 +683,27 @@ func (e *env) baseFor(c *http.Client) string {
|
||||
return e.a.base
|
||||
}
|
||||
|
||||
// instanceOf 给出 client 实际轮询的实例(名 + 地址),供有界等待的失败信息定位。
|
||||
func (e *env) instanceOf(c *http.Client) string {
|
||||
base := e.baseFor(c)
|
||||
if e.b != nil && base == e.b.base {
|
||||
return e.b.name + " (" + base + ")"
|
||||
}
|
||||
return e.a.name + " (" + base + ")"
|
||||
}
|
||||
|
||||
// runStatuses 按顺序取会话详情里各 run 的状态,供等待条件与失败信息复用。
|
||||
func runStatuses(detail map[string]any) []string {
|
||||
runs, _ := detail["runs"].([]any)
|
||||
out := make([]string, 0, len(runs))
|
||||
for _, r := range runs {
|
||||
m, _ := r.(map[string]any)
|
||||
s, _ := m["status"].(string)
|
||||
out = append(out, s)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ---- 场景辅助 ----
|
||||
|
||||
type sseEvent struct {
|
||||
@@ -662,10 +733,13 @@ func (e *env) postMessage(t *testing.T, c *http.Client, convID, text string) {
|
||||
|
||||
func (e *env) waitConversationIdle(t *testing.T, c *http.Client, convID string, within time.Duration) {
|
||||
t.Helper()
|
||||
deadline := time.Now().Add(within)
|
||||
var statuses []string
|
||||
start := time.Now()
|
||||
deadline := start.Add(within)
|
||||
for time.Now().Before(deadline) {
|
||||
var detail map[string]any
|
||||
e.do(t, c, "GET", "/api/conversations/"+convID, nil, &detail)
|
||||
statuses = runStatuses(detail)
|
||||
busy := false
|
||||
if runs, ok := detail["runs"].([]any); ok {
|
||||
for _, r := range runs {
|
||||
@@ -681,7 +755,8 @@ func (e *env) waitConversationIdle(t *testing.T, c *http.Client, convID string,
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
t.Fatalf("conversation %s still busy after %v", convID, within)
|
||||
t.Fatalf("conversation %s still busy on %s: waited %v (budget %v) and never observed all runs out of pending/running/waiting_hitl; run statuses %v",
|
||||
convID, e.instanceOf(c), time.Since(start).Round(time.Millisecond), within, statuses)
|
||||
}
|
||||
|
||||
func (e *env) lastSeq(t *testing.T, c *http.Client, convID string) int64 {
|
||||
@@ -746,11 +821,17 @@ func (e *env) subscribe(t *testing.T, c *http.Client, convID string, since int64
|
||||
|
||||
func (e *env) waitActiveHITL(t *testing.T, c *http.Client, convID string) hitlCard {
|
||||
t.Helper()
|
||||
deadline := time.Now().Add(15 * time.Second)
|
||||
var statuses []string
|
||||
cardCount := 0
|
||||
start := time.Now()
|
||||
deadline := start.Add(15 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
var detail map[string]any
|
||||
e.do(t, c, "GET", "/api/conversations/"+convID, nil, &detail)
|
||||
if cards, ok := detail["activeHitl"].([]any); ok && len(cards) > 0 {
|
||||
statuses = runStatuses(detail)
|
||||
cards, ok := detail["activeHitl"].([]any)
|
||||
cardCount = len(cards)
|
||||
if ok && len(cards) > 0 {
|
||||
m, _ := cards[0].(map[string]any)
|
||||
id, _ := m["id"].(string)
|
||||
runID, _ := m["runId"].(string)
|
||||
@@ -760,21 +841,27 @@ func (e *env) waitActiveHITL(t *testing.T, c *http.Client, convID string) hitlCa
|
||||
}
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
t.Fatalf("no active HITL card in 15s; logA:\n%s", e.a.log.String())
|
||||
t.Fatalf("no active HITL card with id and runId on %s for conversation %s: waited %v (budget 15s); last activeHitl count %d, run statuses %v; logA:\n%s",
|
||||
e.instanceOf(c), convID, time.Since(start).Round(time.Millisecond), cardCount, statuses, e.a.log.String())
|
||||
return hitlCard{}
|
||||
}
|
||||
|
||||
func (e *env) waitRunStatus(t *testing.T, c *http.Client, runID, status string) {
|
||||
t.Helper()
|
||||
deadline := time.Now().Add(httpWait)
|
||||
observed := "<none>"
|
||||
start := time.Now()
|
||||
deadline := start.Add(httpWait)
|
||||
for time.Now().Before(deadline) {
|
||||
var out map[string]any
|
||||
e.do(t, c, "GET", "/api/runs/"+runID, nil, &out)
|
||||
run, _ := out["run"].(map[string]any)
|
||||
if s, _ := run["status"].(string); s == status {
|
||||
s, _ := run["status"].(string)
|
||||
if s == status {
|
||||
return
|
||||
}
|
||||
observed = s
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
t.Fatalf("run %s not %s within %v", runID, status, httpWait)
|
||||
t.Fatalf("run %s not %s on %s: waited %v (budget %v), last observed status %q; logA:\n%s",
|
||||
runID, status, e.instanceOf(c), time.Since(start).Round(time.Millisecond), httpWait, observed, e.a.log.String())
|
||||
}
|
||||
|
||||
@@ -177,13 +177,13 @@ echo "[pgbench] results $RESULTS"
|
||||
df -h "$ROOT"
|
||||
pgbench --version
|
||||
echo "--- heavy processes (comm only) ---"
|
||||
ps -eo pcpu,pmem,comm --sort=-pcpu | head -12
|
||||
ps -eo pcpu,pmem,comm --sort=-pcpu | sed -n '1,12p'
|
||||
} >"$RESULTS/host.txt" 2>&1
|
||||
|
||||
echo "[pgbench] creating isolated instance $NAME on 127.0.0.1:$PORT"
|
||||
"$ROOT/scripts/svc.sh" db create "$NAME" --port "$PORT" >"$RESULTS/svc-create.txt" 2>&1
|
||||
OWNED=1
|
||||
addr="$(sed -n 's/^addr=//p' "$ROOT/runtime/$NAME/svc.env" | head -1)"
|
||||
addr="$(sed -n '/^addr=/{s///p;q;}' "$ROOT/runtime/$NAME/svc.env")"
|
||||
[[ "$addr" == "127.0.0.1:$PORT" ]] || {
|
||||
echo "[pgbench] instance addr is $addr, expected 127.0.0.1:$PORT; aborting" >&2
|
||||
exit 1
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
"""Regression for tests/_port_alloc.free_test_port."""
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from _port_alloc import free_test_port # noqa: E402
|
||||
|
||||
|
||||
class FreeTestPortTests(unittest.TestCase):
|
||||
def test_two_calls_return_distinct_bindable_ports(self):
|
||||
first = free_test_port()
|
||||
second = free_test_port()
|
||||
self.assertNotEqual(first, second)
|
||||
for port in (first, second):
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listener:
|
||||
listener.bind(("127.0.0.1", port))
|
||||
listener.listen(1)
|
||||
self.assertEqual(listener.getsockname()[1], port)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,806 @@
|
||||
"""托管 shell 脚本:管道任一读取级不得是提前退出的读取器(READER_FAMILIES 声明的各族:grep -q/-m、head、sed q、
|
||||
awk exit/nextfile、read、mapfile/readarray -n、以 break/exit 结束或 until 条件含读取器的 while/until/select 循环;
|
||||
含被子壳或花括组包裹、带绝对路径或 env/exec/nohup/timeout 等前缀的写法)。守护的承诺是“已声明的各族在发现到的
|
||||
脚本中处处被拦截”,而非“理解任意 bash 命令”。
|
||||
|
||||
pipefail 下读取器先退出,上游生产者写满 64 KiB 管道缓冲后收到 SIGPIPE,管道状态报 141,
|
||||
`if producer | grep -q PAT` 与 `if ! ...` 的判定被静默翻转;读取器位于管道中段
|
||||
(`producer | head -1 | sed ...`)时同理。改用 here-string、进程替换或 sed 打印即退出,
|
||||
读取器的结论才不会丢。
|
||||
"""
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCRIPT_DIRS = ("scripts", "docker")
|
||||
EXTRA_SCRIPTS = ("docker/verify-env.sh", "tests/load/collaboration/run_pgbench.sh")
|
||||
SHELL_SHEBANG = re.compile(r"#!.*\b(?:ba|da|k|z)?sh\b")
|
||||
# 发现到但检测器无法正确解析的脚本:路径 -> 理由。条目须仍被发现,否则视为过期。
|
||||
EXEMPT = {}
|
||||
|
||||
READER_FAMILIES = (
|
||||
("grep", "-q/--quiet/--silent、-m/--max-count(含 egrep/fgrep)", "得出匹配结论或读满 N 条匹配即退出"),
|
||||
("head", "任意用法", "输出前 N 行/字节后即退出"),
|
||||
("sed", "q/Q 命令", "执行 q/Q 即停止读取"),
|
||||
("awk", "END 块之外的 exit/nextfile(含 gawk/mawk/nawk)", "exit 立即结束,nextfile 放弃当前输入剩余部分"),
|
||||
("read", "任意用法(含 -n N/--chars N)", "只读一行,-n N 读满 N 个字符即返回"),
|
||||
("mapfile", "-n/-n<N>/-n <N>/--n/--numlines(含 readarray)", "读满 N 行即停止;长选项被 bash 拒绝时一字节不读即退出"),
|
||||
(
|
||||
"while",
|
||||
"while/until/select 循环:循环内命令位的 break 出本循环、命令位的 exit(子壳内除外)、until 条件含上列读取器"
|
||||
"(! 取反除外)",
|
||||
"break/exit 结束循环后不再读取剩余输入;until 条件读取成功即结束循环",
|
||||
),
|
||||
)
|
||||
"""守护覆盖的提前退出读取器族:(命令, 触发选项, 提前退出的原因)。
|
||||
|
||||
检测器保证这些族(含子壳/花括组包裹、VAR= 前缀、!/time/command/builtin/env/exec 前缀、nohup/timeout/nice/stdbuf
|
||||
包装命令及其选项与 timeout 时长、绝对路径写法)位于管道读取级时处处被拦截;不带触发选项的 mapfile/readarray、
|
||||
无 break/exit 的 while read、条件含读取器的 while(条件失败即 EOF 才结束)读完全部输入,不在拦截之列。
|
||||
"""
|
||||
|
||||
HEREDOC = re.compile(r"<<(-?)\s*(['\"]?)([A-Za-z_][A-Za-z0-9_]*)\2")
|
||||
SED_QUIT = re.compile(r"(?:^|[\s;{}'\"/$0-9])[qQ][0-9]*\s*(?:$|[\s;}'\"])")
|
||||
AWK_END = re.compile(r"\bEND\s*\{")
|
||||
AWK_EXIT = re.compile(r"\b(?:exit|nextfile)\b")
|
||||
AWK_STRING = re.compile(r'"(?:[^"\\\n]|\\.)*"')
|
||||
SHELL_SQ = re.compile(r"'[^']*'")
|
||||
ASSIGNMENT = re.compile(r"[A-Za-z_][A-Za-z0-9_]*=")
|
||||
WORD = re.compile(r"(\S+)\s*")
|
||||
BREAK_ARG = re.compile(r"[ \t]+([0-9]+)")
|
||||
PREFIXES = {"!", "time", "command", "builtin", "env", "exec"}
|
||||
# 包装命令:(带独立参数的选项, 被包装命令前的操作数个数);均在首个操作数处停止选项解析。
|
||||
WRAPPERS = {
|
||||
"nohup": (set(), 0),
|
||||
"timeout": ({"-s", "-k", "--signal", "--kill-after"}, 1),
|
||||
"nice": ({"-n", "--adjustment"}, 0),
|
||||
"stdbuf": ({"-i", "-o", "-e", "--input", "--output", "--error"}, 0),
|
||||
}
|
||||
LOOPS = ("while", "until", "select")
|
||||
GREP_ARG_OPTS = set("efABCdDX")
|
||||
MAPFILE_ARG_OPTS = set("dOsuCc")
|
||||
BOUNDARY = set(" \t\n;&|()<>")
|
||||
|
||||
|
||||
def _is_shell_script(rel):
|
||||
path = ROOT / rel
|
||||
if rel.endswith(".sh"):
|
||||
return True
|
||||
if not path.is_file():
|
||||
return False
|
||||
with path.open(encoding="utf-8", errors="replace") as f:
|
||||
return bool(SHELL_SHEBANG.match(f.readline()))
|
||||
|
||||
|
||||
def managed_scripts():
|
||||
"""git 跟踪的托管 shell 脚本(.sh 或首行 shell shebang);未跟踪副本(如 runtime/ 下的陈旧克隆)不在其中。"""
|
||||
out = subprocess.run(
|
||||
["git", "-C", str(ROOT), "ls-files", "-z", "--", *SCRIPT_DIRS, *EXTRA_SCRIPTS],
|
||||
check=True, capture_output=True, text=True,
|
||||
).stdout
|
||||
return sorted(p for p in set(out.split("\0")) if p and _is_shell_script(p))
|
||||
|
||||
|
||||
class _Frame:
|
||||
"""一层命令上下文(顶层、$(...)、(...)、<(...)、管道级的 { ...; }、while/until/select 循环)内正在累积的管道。"""
|
||||
|
||||
def __init__(self, line, depth, closer=None, kind=None, cond_start=0):
|
||||
self.stages, self.stage, self.start, self.cases = [], [], line, []
|
||||
self.depth, self.closer, self.dos, self.broken = depth, closer, 0, False
|
||||
self.kind, self.cond_start, self.cond = kind, cond_start, []
|
||||
|
||||
|
||||
def _lex(src):
|
||||
"""词法扫描 bash 源码;跳过注释与 heredoc 正文。
|
||||
|
||||
返回 (lists, loops):lists 为每个命令列表的 (帧深度, [(起始行号, 级文本), ...]),
|
||||
loops 为每个 while/until/select 循环的 (帧深度, 循环关键字, 是否被 break/exit 结束, 条件内的 lists)。
|
||||
"""
|
||||
found, loops, frames, modes, heredocs = [], [], [_Frame(1, 0)], ["cmd"], []
|
||||
line, word, i, n = 1, "", 0, len(src)
|
||||
|
||||
def emit(s):
|
||||
for f in frames:
|
||||
f.stage.append(s)
|
||||
|
||||
def finish(f):
|
||||
text = "".join(f.stage).strip()
|
||||
if f.stages or text:
|
||||
found.append((f.depth, f.stages + [(f.start, text)]))
|
||||
f.stages, f.stage, f.start = [], [], line
|
||||
|
||||
def push(closer, kind=None):
|
||||
modes.append("cmd")
|
||||
frames.append(_Frame(line, len(frames), closer, kind, len(found)))
|
||||
|
||||
def pop():
|
||||
finish(frames[-1])
|
||||
f = frames.pop()
|
||||
modes.pop()
|
||||
if f.closer == "done":
|
||||
loops.append((f.depth, f.kind, f.broken, f.cond))
|
||||
|
||||
def case_state():
|
||||
cases = frames[-1].cases
|
||||
return cases[-1] if cases else None
|
||||
|
||||
def command_position(w):
|
||||
j = i - len(w)
|
||||
while j > 0 and src[j - 1] in " \t":
|
||||
j -= 1
|
||||
if j == 0 or src[j - 1] in ";\n&|(" or (src[j - 1] == ")" and case_state() == "body"):
|
||||
return True
|
||||
k = j
|
||||
while k > 0 and src[k - 1] not in BOUNDARY:
|
||||
k -= 1
|
||||
return src[k:j] in ("do", "then", "else", "{", "!")
|
||||
|
||||
def break_out(levels):
|
||||
for f in reversed(frames):
|
||||
nested = f.dos - (f.closer == "done" and f.dos > 0)
|
||||
if levels <= nested:
|
||||
return
|
||||
levels -= nested
|
||||
if f.closer == "done":
|
||||
f.broken = True
|
||||
levels -= 1
|
||||
if levels <= 0:
|
||||
return
|
||||
elif f.closer != "}":
|
||||
return
|
||||
|
||||
def exit_out():
|
||||
for f in reversed(frames):
|
||||
if f.closer == "done":
|
||||
f.broken = True
|
||||
elif f.closer != "}":
|
||||
return
|
||||
|
||||
def keyword(w):
|
||||
top = frames[-1]
|
||||
cases = top.cases
|
||||
if w == "case":
|
||||
cases.append("in")
|
||||
elif w == "in" and case_state() == "in":
|
||||
cases[-1] = "pattern"
|
||||
elif w == "esac" and cases:
|
||||
cases.pop()
|
||||
elif case_state() == "pattern":
|
||||
pass
|
||||
elif w in LOOPS and "".join(top.stage).strip() == w:
|
||||
push("done", w)
|
||||
elif w == "do" and command_position(w):
|
||||
if top.closer == "done" and top.dos == 0:
|
||||
top.cond = found[top.cond_start :]
|
||||
top.dos += 1
|
||||
elif w == "done" and command_position(w):
|
||||
top.dos -= 1
|
||||
if top.closer == "done" and top.dos <= 0:
|
||||
pop()
|
||||
elif w == "break" and command_position(w):
|
||||
m = BREAK_ARG.match(src, i)
|
||||
break_out(int(m.group(1)) if m else 1)
|
||||
elif w == "exit" and command_position(w):
|
||||
exit_out()
|
||||
|
||||
def push_cmd(opener, closer=")"):
|
||||
emit(opener)
|
||||
push(closer)
|
||||
|
||||
while i < n:
|
||||
c, nxt = src[i], src[i + 1 : i + 2]
|
||||
mode = modes[-1]
|
||||
if mode in ("sq", "ansi"):
|
||||
if c == "\\" and mode == "ansi":
|
||||
emit(src[i : i + 2])
|
||||
i += 2
|
||||
continue
|
||||
emit(c)
|
||||
line += c == "\n"
|
||||
if c == "'":
|
||||
modes.pop()
|
||||
i += 1
|
||||
continue
|
||||
if mode in ("dq", "brace"):
|
||||
if c == "\\":
|
||||
emit(src[i : i + 2])
|
||||
line += nxt == "\n"
|
||||
i += 2
|
||||
elif c == "$" and nxt == "(":
|
||||
push_cmd("$(")
|
||||
i += 2
|
||||
elif c == "$" and nxt == "{":
|
||||
emit("${")
|
||||
modes.append("brace")
|
||||
i += 2
|
||||
elif c == '"':
|
||||
emit(c)
|
||||
if mode == "dq":
|
||||
modes.pop()
|
||||
else:
|
||||
modes.append("dq")
|
||||
i += 1
|
||||
elif c == "}" and mode == "brace":
|
||||
emit(c)
|
||||
modes.pop()
|
||||
i += 1
|
||||
else:
|
||||
emit(c)
|
||||
line += c == "\n"
|
||||
i += 1
|
||||
continue
|
||||
|
||||
if c in BOUNDARY and word:
|
||||
keyword(word)
|
||||
word = ""
|
||||
top = frames[-1]
|
||||
if c == "\\":
|
||||
if nxt == "\n":
|
||||
emit(" ")
|
||||
line += 1
|
||||
else:
|
||||
emit(src[i : i + 2])
|
||||
word += "\0"
|
||||
i += 2
|
||||
elif c == "#" and (i == 0 or src[i - 1] in " \t\n;&|("):
|
||||
j = src.find("\n", i)
|
||||
i = n if j < 0 else j
|
||||
elif c == "'" or (c == "$" and nxt == "'"):
|
||||
emit(src[i : i + 2] if c == "$" else c)
|
||||
modes.append("ansi" if c == "$" else "sq")
|
||||
word += "\0"
|
||||
i += 2 if c == "$" else 1
|
||||
elif c == '"':
|
||||
emit(c)
|
||||
modes.append("dq")
|
||||
word += "\0"
|
||||
i += 1
|
||||
elif c == "$" and nxt == "{":
|
||||
emit("${")
|
||||
modes.append("brace")
|
||||
word += "\0"
|
||||
i += 2
|
||||
elif c == "$" and nxt == "(":
|
||||
push_cmd("$(")
|
||||
word += "\0"
|
||||
i += 2
|
||||
elif c == "(":
|
||||
if case_state() == "pattern":
|
||||
emit(c)
|
||||
else:
|
||||
push_cmd(c)
|
||||
i += 1
|
||||
elif c == ")":
|
||||
if case_state() == "pattern":
|
||||
top.cases[-1] = "body"
|
||||
elif top.closer == ")":
|
||||
pop()
|
||||
emit(c)
|
||||
i += 1
|
||||
elif c == "{" and not word and nxt in (" ", "\t", "\n") and top.stages and not "".join(top.stage).strip():
|
||||
push_cmd(c, "}")
|
||||
i += 1
|
||||
elif c == "}" and not word and top.closer == "}" and (nxt == "" or nxt in BOUNDARY) and command_position(""):
|
||||
pop()
|
||||
emit(c)
|
||||
i += 1
|
||||
elif c == "|":
|
||||
if nxt == "|":
|
||||
finish(top)
|
||||
emit("||")
|
||||
i += 2
|
||||
elif case_state() == "pattern":
|
||||
emit(c)
|
||||
i += 1
|
||||
else:
|
||||
top.stages.append((top.start, "".join(top.stage).strip()))
|
||||
emit(c)
|
||||
top.stage, top.start = [], line
|
||||
i += 2 if nxt == "&" else 1
|
||||
elif c == ";":
|
||||
if src.startswith(";;", i) or src.startswith(";&", i):
|
||||
if case_state() == "body":
|
||||
top.cases[-1] = "pattern"
|
||||
k = 3 if src.startswith(";;&", i) else 2
|
||||
else:
|
||||
k = 1
|
||||
finish(top)
|
||||
emit(src[i : i + k])
|
||||
i += k
|
||||
elif c == "&":
|
||||
if nxt == "&":
|
||||
finish(top)
|
||||
emit("&&")
|
||||
i += 2
|
||||
elif nxt == ">" or (i > 0 and src[i - 1] in "<>"):
|
||||
emit(c)
|
||||
i += 1
|
||||
else:
|
||||
finish(top)
|
||||
emit(c)
|
||||
i += 1
|
||||
elif c == "<" and src.startswith("<<", i):
|
||||
m = None if src.startswith("<<<", i) else HEREDOC.match(src, i)
|
||||
if m:
|
||||
heredocs.append((m.group(3), m.group(1) == "-"))
|
||||
emit(m.group(0))
|
||||
i = m.end()
|
||||
else:
|
||||
k = 3 if src.startswith("<<<", i) else 2
|
||||
emit(src[i : i + k])
|
||||
i += k
|
||||
elif c == "\n":
|
||||
line += 1
|
||||
i += 1
|
||||
for term, dash in heredocs:
|
||||
while i < n:
|
||||
j = src.find("\n", i)
|
||||
body = src[i : n if j < 0 else j]
|
||||
i = n if j < 0 else j + 1
|
||||
line += 1
|
||||
if (body.lstrip("\t") if dash else body) == term:
|
||||
break
|
||||
heredocs = []
|
||||
if not (top.stages and not "".join(top.stage).strip()):
|
||||
finish(top)
|
||||
emit("\n")
|
||||
else:
|
||||
emit(c)
|
||||
if c not in BOUNDARY:
|
||||
word += c
|
||||
i += 1
|
||||
|
||||
if word:
|
||||
keyword(word)
|
||||
if len(frames) != 1 or modes != ["cmd"]:
|
||||
raise ValueError(f"unbalanced shell lexing at EOF: modes={modes}")
|
||||
finish(frames[0])
|
||||
return found, loops
|
||||
|
||||
|
||||
def pipelines(src):
|
||||
"""返回 bash 源码中每条多级管道各级的 [(起始行号, 文本), ...]。"""
|
||||
return [stages for _, stages in _lex(src)[0] if len(stages) > 1]
|
||||
|
||||
|
||||
def _grep_exits_early(words):
|
||||
for w in words[1:]:
|
||||
w = w.strip("'\"")
|
||||
if w == "--":
|
||||
break
|
||||
if w.startswith("--"):
|
||||
if w.split("=")[0] in ("--quiet", "--silent", "--max-count"):
|
||||
return True
|
||||
elif w.startswith("-") and len(w) > 1:
|
||||
for ch in w[1:]:
|
||||
if ch in "qm":
|
||||
return True
|
||||
if ch in GREP_ARG_OPTS:
|
||||
break
|
||||
return False
|
||||
|
||||
|
||||
def _mapfile_exits_early(words):
|
||||
"""mapfile/readarray 带 -n(-n<N>、-n <N>、选项簇内的 n)或 --n/--numlines 时只读 N 行即停止。"""
|
||||
skip = False
|
||||
for w in words[1:]:
|
||||
w = w.strip("'\"")
|
||||
if skip:
|
||||
skip = False
|
||||
continue
|
||||
# bash 内建在首个操作数处停止选项解析:`mapfile arr -n 1` 的 -n 不生效、读满输入,不是提前退出。
|
||||
if w == "--" or not w.startswith("-") or len(w) == 1:
|
||||
break
|
||||
if w.startswith("--"):
|
||||
if w.split("=")[0] in ("--n", "--numlines"):
|
||||
return True
|
||||
continue
|
||||
for k, ch in enumerate(w[1:], 1):
|
||||
if ch == "n":
|
||||
return True
|
||||
if ch in MAPFILE_ARG_OPTS:
|
||||
skip = k == len(w) - 1
|
||||
break
|
||||
return False
|
||||
|
||||
|
||||
def _awk_exits_early(prog):
|
||||
"""awk 程序在 END 块之外含 exit 或 nextfile(含 BEGIN 内),即可能在读完输入前停止读取。
|
||||
|
||||
单引号程序内的 awk 字符串字面量先剥去(`print "exit"` 不是语句)。
|
||||
"""
|
||||
prog = SHELL_SQ.sub(lambda q: AWK_STRING.sub('""', q.group(0)), prog)
|
||||
while m := AWK_END.search(prog):
|
||||
depth, j = 0, m.end() - 1
|
||||
while j < len(prog):
|
||||
depth += {"{": 1, "}": -1}.get(prog[j], 0)
|
||||
j += 1
|
||||
if depth == 0:
|
||||
break
|
||||
prog = prog[: m.start()] + prog[j:]
|
||||
return bool(AWK_EXIT.search(prog))
|
||||
|
||||
|
||||
def _group_exits_early(group):
|
||||
"""( ... ) 或 { ...; } 内任一命令列表的首级(读组 stdin 者)是提前退出的读取器。"""
|
||||
end = group.rfind(")" if group[0] == "(" else "}")
|
||||
inner = group[1:end] if end > 0 else group[1:]
|
||||
return any(
|
||||
early_exit_reader(stages[0][1].lstrip(";&| \t\n")) for depth, stages in _lex(inner)[0] if depth == 0
|
||||
)
|
||||
|
||||
|
||||
def _skip_wrapper_args(cmd, s):
|
||||
"""跳过包装命令的选项(含带独立参数者)与被包装命令前的操作数,返回被包装命令起的文本。"""
|
||||
arg_opts, operands = WRAPPERS[cmd]
|
||||
opts = True
|
||||
while m := WORD.match(s):
|
||||
w = m.group(1)
|
||||
if opts and w == "--":
|
||||
opts = False
|
||||
elif opts and w.startswith("-") and len(w) > 1:
|
||||
if w in arg_opts:
|
||||
s = s[m.end() :]
|
||||
m = WORD.match(s)
|
||||
if not m:
|
||||
return ""
|
||||
elif operands:
|
||||
opts, operands = False, operands - 1
|
||||
else:
|
||||
return s
|
||||
s = s[m.end() :]
|
||||
return s
|
||||
|
||||
|
||||
def _until_condition_reads(cond):
|
||||
"""until 条件含读取器:首次读取成功即结束循环。! 取反的条件只在读取失败(EOF)时成立,与 while 同样读满输入。"""
|
||||
for _, stages in cond:
|
||||
texts = [text.lstrip(";&| \t\n") for _, text in stages]
|
||||
if texts[0].split(None, 1)[:1] == ["!"]:
|
||||
continue
|
||||
if any(early_exit_reader(text) for text in texts):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def early_exit_reader(stage):
|
||||
"""读取级读到结论后是否会提前关闭 stdin(READER_FAMILIES:grep -q/-m、head、sed q、awk exit/nextfile、read、
|
||||
mapfile/readarray -n、以 break/exit 结束或 until 条件含读取器的 while/until/select)。
|
||||
|
||||
先剥去 VAR=value、!/time/command/builtin/env/exec 前缀与 nohup/timeout/nice/stdbuf 包装(连同其选项与 timeout
|
||||
时长)、取命令基名;子壳或花括组展开后逐条判定。
|
||||
"""
|
||||
s = stage.strip()
|
||||
while s:
|
||||
if s[0] in "({":
|
||||
return _group_exits_early(s)
|
||||
m = WORD.match(s)
|
||||
cmd = m.group(1).rsplit("/", 1)[-1]
|
||||
if not (ASSIGNMENT.match(m.group(1)) or cmd in PREFIXES or cmd in WRAPPERS):
|
||||
break
|
||||
s = s[m.end() :]
|
||||
if cmd in WRAPPERS:
|
||||
s = _skip_wrapper_args(cmd, s)
|
||||
if not s:
|
||||
return False
|
||||
if cmd in LOOPS:
|
||||
return any(
|
||||
broken or (kind == "until" and _until_condition_reads(cond))
|
||||
for depth, kind, broken, cond in _lex(s)[1]
|
||||
if depth == 1
|
||||
)
|
||||
words = [cmd, *s[m.end() :].split()]
|
||||
rest = " ".join(words[1:])
|
||||
if cmd in ("grep", "egrep", "fgrep"):
|
||||
return _grep_exits_early(words)
|
||||
if cmd in ("head", "read"):
|
||||
return True
|
||||
if cmd in ("mapfile", "readarray"):
|
||||
return _mapfile_exits_early(words)
|
||||
if cmd == "sed":
|
||||
return bool(SED_QUIT.search(rest))
|
||||
if cmd in ("awk", "gawk", "mawk", "nawk"):
|
||||
return _awk_exits_early(rest)
|
||||
return False
|
||||
|
||||
|
||||
def hazards(src):
|
||||
"""各管道第二级起(读 pipe 的各级)中提前退出的读取器,返回 (行号, 该级文本)。"""
|
||||
return [
|
||||
(line, text)
|
||||
for stages in pipelines(src)
|
||||
for line, text in stages[1:]
|
||||
if early_exit_reader(text)
|
||||
]
|
||||
|
||||
|
||||
class ShellPipeHazardTests(unittest.TestCase):
|
||||
def test_detector_flags_synthetic_hazard(self):
|
||||
found = hazards("set -euo pipefail\nseq 1 200000 | grep -q 1\n")
|
||||
self.assertEqual([line for line, _ in found], [2], found)
|
||||
|
||||
def test_detector_flags_hazard_shapes(self):
|
||||
for snippet in (
|
||||
'if ! seq 1 200000 | grep -q 1; then echo miss; fi',
|
||||
'x="$(sed -n \'s/^k=//p\' "$f" | head -1)"',
|
||||
'if ! "$bin" serve -h 2>&1 | grep -Eq -- "(^|[[:space:]])-{1,2}config"; then :; fi',
|
||||
'echo "${r:+($(printf \'%s\' "$r" | head -c 120))}"',
|
||||
'find . -type f \\\n | grep -m1 x',
|
||||
"seq 1 200000 | sed -n '/1/{p;q;}'",
|
||||
'seq 1 200000 | grep -qiE "a|b" || exit 1',
|
||||
):
|
||||
with self.subTest(snippet=snippet):
|
||||
self.assertTrue(hazards(snippet + "\n"), snippet)
|
||||
|
||||
def test_detector_flags_newly_covered_shapes(self):
|
||||
for snippet, stage in (
|
||||
("seq 1 200000 | head -1 | sed 's/^/x/'", "head -1"),
|
||||
("x=\"$(seq 1 200000 | head -n 3 | tr -d ' ')\"", "head -n 3"),
|
||||
("seq 1 200000 | awk '{print $1; exit}'", "awk '{print $1; exit}'"),
|
||||
("seq 1 200000 | awk 'BEGIN { exit 1 }'", "awk 'BEGIN { exit 1 }'"),
|
||||
("seq 1 200000 | awk '/7/{f=1; exit} END{print f}' | cat", "awk '/7/{f=1; exit} END{print f}'"),
|
||||
("seq 1 200000 | read -r first", "read -r first"),
|
||||
("seq 1 200000 | IFS= read -r first", "IFS= read -r first"),
|
||||
("if seq 1 200000 | LC_ALL=C grep -q 1; then :; fi", "LC_ALL=C grep -q 1"),
|
||||
("seq 1 200000 | env LC_ALL=C grep -m1 1 | wc -l", "env LC_ALL=C grep -m1 1"),
|
||||
("seq 1 200000 | grep -q 1 | cat", "grep -q 1"),
|
||||
):
|
||||
with self.subTest(snippet=snippet):
|
||||
self.assertEqual(hazards(snippet + "\n"), [(1, stage)], snippet)
|
||||
|
||||
def test_wrapped_and_prefixed_readers_flagged_and_harmful(self):
|
||||
"""子壳/花括组、awk nextfile、带 break 的 while read、绝对路径与 exec 前缀:检测器命中,且 pipefail 下确实报 141。"""
|
||||
grep = shutil.which("grep") or "/usr/bin/grep"
|
||||
shapes = (
|
||||
("seq 1 200000 | (head -1)", "(head -1)"),
|
||||
("seq 1 200000 | ( head -1 )", "( head -1 )"),
|
||||
("seq 1 200000 | { head -1; }", "{ head -1; }"),
|
||||
("seq 1 200000 | awk '$1 == 7 { print; nextfile }'", "awk '$1 == 7 { print; nextfile }'"),
|
||||
("seq 1 200000 | while read -r l; do break; done", "while read -r l; do break; done"),
|
||||
(f"seq 1 200000 | {grep} -q 1", f"{grep} -q 1"),
|
||||
("seq 1 200000 | exec grep -q 1", "exec grep -q 1"),
|
||||
)
|
||||
with tempfile.TemporaryDirectory(dir="/tmp") as tmp:
|
||||
for snippet, stage in shapes:
|
||||
with self.subTest(snippet=snippet):
|
||||
self.assertEqual(hazards(snippet + "\n"), [(1, stage)], snippet)
|
||||
script = Path(tmp) / "shape.sh"
|
||||
script.write_text(f"set -euo pipefail\n{snippet}\necho survived\n", encoding="utf-8")
|
||||
run = subprocess.run(["bash", str(script)], capture_output=True, text=True)
|
||||
self.assertEqual(run.returncode, 141, run)
|
||||
self.assertNotIn("survived", run.stdout)
|
||||
|
||||
def test_detector_flags_group_loop_and_prefix_variants(self):
|
||||
for snippet, stage in (
|
||||
("seq 1 200000 | {\n head -1\n} | cat", "{\n head -1\n}"),
|
||||
("if seq 1 200000 | (grep -q 1); then :; fi", "(grep -q 1)"),
|
||||
("seq 1 200000 | (cd /tmp && head -1)", "(cd /tmp && head -1)"),
|
||||
("seq 1 200000 | while read -r l\ndo\n [ \"$l\" = 7 ] && break\ndone", "while read -r l\ndo\n [ \"$l\" = 7 ] && break\ndone"),
|
||||
("seq 1 200000 | until ! read -r l; do break; done", "until ! read -r l; do break; done"),
|
||||
("seq 1 200000 | while read -r l; do for x in a; do break 2; done; done", "while read -r l; do for x in a; do break 2; done; done"),
|
||||
("seq 1 200000 | /usr/bin/env LC_ALL=C grep -q 1", "/usr/bin/env LC_ALL=C grep -q 1"),
|
||||
("seq 1 200000 | exec /usr/bin/head -1", "exec /usr/bin/head -1"),
|
||||
("seq 1 200000 | until IFS= read -r l\ndo\n :\ndone", "until IFS= read -r l\ndo\n :\ndone"),
|
||||
("seq 1 200000 | until read -r a && read -r b; do :; done", "until read -r a && read -r b; do :; done"),
|
||||
("seq 1 200000 | until [ \"$(head -1)\" = 7 ]; do :; done", "until [ \"$(head -1)\" = 7 ]; do :; done"),
|
||||
("seq 1 200000 | while read -r l; do { exit 1; }; done", "while read -r l; do { exit 1; }; done"),
|
||||
("seq 1 200000 | while read -r l; do if [ -z \"$l\" ]; then exit 1; fi; done",
|
||||
"while read -r l; do if [ -z \"$l\" ]; then exit 1; fi; done"),
|
||||
("seq 1 200000 | while read -r l; do case $l in 7) break ;; esac; done",
|
||||
"while read -r l; do case $l in 7) break ;; esac; done"),
|
||||
("seq 1 200000 | select x in a; do [ -n \"$x\" ] && exit; done", "select x in a; do [ -n \"$x\" ] && exit; done"),
|
||||
("seq 1 200000 | timeout -s KILL 5 grep -q 1", "timeout -s KILL 5 grep -q 1"),
|
||||
("seq 1 200000 | timeout --foreground 5 head -1", "timeout --foreground 5 head -1"),
|
||||
("seq 1 200000 | env LC_ALL=C /usr/bin/timeout 5 head -1", "env LC_ALL=C /usr/bin/timeout 5 head -1"),
|
||||
):
|
||||
with self.subTest(snippet=snippet):
|
||||
self.assertEqual(hazards(snippet + "\n"), [(1, stage)], snippet)
|
||||
|
||||
def test_loop_family_and_wrappers_flagged_and_harmful(self):
|
||||
"""until 条件读取、循环内 exit、select break、nohup/timeout/nice/stdbuf 包装:检测器命中且 pipefail 下报 141;
|
||||
对应的读满输入写法不命中且照常完成。"""
|
||||
shapes = (
|
||||
"until read -r l; do :; done",
|
||||
"until grep -q 1; do :; done",
|
||||
"while read -r l; do exit; done",
|
||||
"while read -r l; do [ \"$l\" = 7 ] && exit; done",
|
||||
"while read -r l; do case $l in 7) exit ;; esac; done",
|
||||
"until false; do read -r l; exit; done",
|
||||
"select x in a; do break; done",
|
||||
"timeout 5 head -1",
|
||||
"nohup head -1",
|
||||
"nice -n 5 head -1",
|
||||
"stdbuf -oL grep -q 1",
|
||||
)
|
||||
safe = (
|
||||
"while read -r l; do :; done",
|
||||
"while grep -q 1; do :; done",
|
||||
"until ! read -r l; do :; done",
|
||||
# select 读到 EOF 时状态为 1;断言生产者未收 SIGPIPE。
|
||||
"select x in a; do :; done >/dev/null || [ \"${PIPESTATUS[0]}\" = 0 ]",
|
||||
"awk '{print \"exit\"}' >/dev/null",
|
||||
"mapfile arr -n 1",
|
||||
"timeout 5 cat >/dev/null",
|
||||
)
|
||||
with tempfile.TemporaryDirectory(dir="/tmp") as tmp:
|
||||
for shape in shapes:
|
||||
snippet = f"seq 1 200000 | {shape}"
|
||||
with self.subTest(snippet=snippet):
|
||||
self.assertEqual(hazards(snippet + "\n"), [(1, shape)], snippet)
|
||||
script = Path(tmp) / "shape.sh"
|
||||
script.write_text(f"set -euo pipefail\n{snippet}\necho survived\n", encoding="utf-8")
|
||||
run = subprocess.run(["bash", str(script)], capture_output=True, text=True)
|
||||
self.assertEqual(run.returncode, 141, run)
|
||||
self.assertNotIn("survived", run.stdout)
|
||||
script = Path(tmp) / "safe.sh"
|
||||
script.write_text(
|
||||
"set -euo pipefail\n" + "".join(f"seq 1 20000 | {s}\n" for s in safe) + "echo survived\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
run = subprocess.run(["bash", str(script)], capture_output=True, text=True)
|
||||
self.assertEqual((run.returncode, run.stdout), (0, "survived\n"), run)
|
||||
self.assertEqual(hazards(script.read_text(encoding="utf-8")), [])
|
||||
|
||||
def test_counted_readers_flagged_and_harmful(self):
|
||||
"""mapfile/readarray -n 与 read -n:检测器命中,且 pipefail 下脚本中止(读满 N 后退出报 141,长选项被拒报非零)。"""
|
||||
shapes = (
|
||||
("seq 1 200000 | mapfile -n 1 arr", "mapfile -n 1 arr", 141),
|
||||
("seq 1 200000 | readarray --numlines 1 arr", "readarray --numlines 1 arr", None),
|
||||
("seq 1 200000 | read -n 5 -r line", "read -n 5 -r line", 141),
|
||||
)
|
||||
with tempfile.TemporaryDirectory(dir="/tmp") as tmp:
|
||||
for snippet, stage, rc in shapes:
|
||||
with self.subTest(snippet=snippet):
|
||||
self.assertEqual(hazards(snippet + "\n"), [(1, stage)], snippet)
|
||||
script = Path(tmp) / "shape.sh"
|
||||
script.write_text(f"set -euo pipefail\n{snippet}\necho survived\n", encoding="utf-8")
|
||||
run = subprocess.run(["bash", str(script)], capture_output=True, text=True)
|
||||
self.assertNotEqual(run.returncode, 0, run)
|
||||
if rc is not None:
|
||||
self.assertEqual(run.returncode, rc, run)
|
||||
self.assertNotIn("survived", run.stdout)
|
||||
script = Path(tmp) / "safe.sh"
|
||||
script.write_text(
|
||||
"set -euo pipefail\nseq 1 200000 > in.txt\nmapfile arr < in.txt\nread -r line < in.txt\n"
|
||||
"seq 1 200000 | mapfile -t arr\necho \"survived ${#arr[@]} $line\"\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
run = subprocess.run(["bash", str(script)], capture_output=True, text=True, cwd=tmp)
|
||||
self.assertEqual((run.returncode, run.stdout), (0, "survived 200000 1\n"), run)
|
||||
self.assertEqual(hazards(script.read_text(encoding="utf-8")), [])
|
||||
|
||||
def test_detector_flags_counted_reader_variants(self):
|
||||
for snippet, stage in (
|
||||
("seq 1 200000 | mapfile -n1 arr", "mapfile -n1 arr"),
|
||||
("seq 1 200000 | mapfile -t -n 3 arr", "mapfile -t -n 3 arr"),
|
||||
("seq 1 200000 | readarray -tn2 arr", "readarray -tn2 arr"),
|
||||
("seq 1 200000 | mapfile -u 0 -n 1 arr", "mapfile -u 0 -n 1 arr"),
|
||||
("seq 1 200000 | readarray --n 1 arr", "readarray --n 1 arr"),
|
||||
("seq 1 200000 | builtin mapfile -n 1 arr", "builtin mapfile -n 1 arr"),
|
||||
("seq 1 200000 | { mapfile -n 1 arr; }", "{ mapfile -n 1 arr; }"),
|
||||
("seq 1 200000 | read --chars 5 line", "read --chars 5 line"),
|
||||
):
|
||||
with self.subTest(snippet=snippet):
|
||||
self.assertEqual(hazards(snippet + "\n"), [(1, stage)], snippet)
|
||||
|
||||
def test_declared_reader_families_each_flagged(self):
|
||||
"""READER_FAMILIES 是守护的契约:非空,且每个声明族至少有一个被检测器命中的合成用例。"""
|
||||
cases = {
|
||||
"grep": "seq 1 200000 | grep -q 1",
|
||||
"head": "seq 1 200000 | head -1",
|
||||
"sed": "seq 1 200000 | sed -n '/1/{p;q;}'",
|
||||
"awk": "seq 1 200000 | awk '{print; exit}'",
|
||||
"read": "seq 1 200000 | read -n 5 -r line",
|
||||
"mapfile": "seq 1 200000 | mapfile -n 1 arr",
|
||||
"while": (
|
||||
"seq 1 200000 | while read -r l; do break; done",
|
||||
"seq 1 200000 | while read -r l; do exit; done",
|
||||
"seq 1 200000 | until read -r l; do :; done",
|
||||
"seq 1 200000 | select x in a; do break; done",
|
||||
),
|
||||
}
|
||||
self.assertTrue(READER_FAMILIES)
|
||||
for family in READER_FAMILIES:
|
||||
command = family[0]
|
||||
with self.subTest(family=command):
|
||||
self.assertEqual(len(family), 3)
|
||||
self.assertTrue(all(family), family)
|
||||
self.assertIn(command, cases, "declared family lacks a synthetic case")
|
||||
snippets = cases[command] if isinstance(cases[command], tuple) else (cases[command],)
|
||||
for snippet in snippets:
|
||||
self.assertTrue(hazards(snippet + "\n"), snippet)
|
||||
|
||||
def test_detector_accepts_safe_forms(self):
|
||||
src = "\n".join((
|
||||
"set -euo pipefail",
|
||||
"# seq 1 200000 | grep -q 1",
|
||||
"grep -q 1 < <(seq 1 200000)",
|
||||
'grep -q x <<<"$v"',
|
||||
"x=\"$(sed -n '/^k=/{s///p;q;}' \"$f\")\"",
|
||||
"x=\"$(sed -n '/^k=/{s///p;q;}' < <(seq 1 200000))\"",
|
||||
"awk '{print $1; exit}' < <(seq 1 200000)",
|
||||
"head -c 160 < <(seq 1 200000)",
|
||||
"ps -eo comm | sed -n '1,12p'",
|
||||
"seq 1 200000 | sed -n '1,12p' | tr -d ' '",
|
||||
"seq 1 200000 | awk '{n++} END {exit !n}'",
|
||||
"seq 1 200000 | awk '$1 == 7 { f = 1 } END { exit !f }' && echo hit",
|
||||
"seq 1 200000 | while read -r l; do :; done",
|
||||
"seq 1 200000 | while read -r l; do [ \"$l\" = 7 ] && continue; done",
|
||||
"seq 1 200000 | while read -r l; do for x in a b; do break; done; done",
|
||||
"while read -r l; do break; done < <(seq 1 200000)",
|
||||
"seq 1 200000 | while grep -q 1; do :; done",
|
||||
"seq 1 200000 | until ! read -r l; do :; done",
|
||||
"seq 1 200000 | while read -r l; do (exit 1); done",
|
||||
"seq 1 200000 | while read -r l; do x=$(exit 1); done",
|
||||
"seq 1 200000 | while read -r l; do echo exit; done",
|
||||
"seq 1 200000 | while read -r l; do case $l in 7) echo exit ;; esac; done",
|
||||
"seq 1 200000 | select x in a; do :; done",
|
||||
"seq 1 200000 | until [ -n \"$x\" ]; do x=1; done",
|
||||
"seq 1 200000 | awk '{print \"exit\"}'",
|
||||
"seq 1 200000 | mapfile arr -n 1",
|
||||
"seq 1 200000 | timeout 5 cat",
|
||||
"seq 1 200000 | nohup cat",
|
||||
"seq 1 200000 | stdbuf -oL sed -n p",
|
||||
"until read -r l; do :; done < file",
|
||||
"seq 1 200000 | awk '{n++} END { nextfile }'",
|
||||
"seq 1 200000 | (cat)",
|
||||
"seq 1 200000 | { cat; }",
|
||||
"seq 1 200000 | env FOO=1 command cat",
|
||||
"seq 1 200000 | time builtin cat >/dev/null",
|
||||
"seq 1 200000 | LC_ALL=C grep -c 1",
|
||||
"mapfile arr < file",
|
||||
"read -r line",
|
||||
"read -n 5 -r line < file",
|
||||
"seq 1 200000 | mapfile arr",
|
||||
"seq 1 200000 | mapfile -t arr",
|
||||
"seq 1 200000 | readarray -u 0 -t arr",
|
||||
"seq 1 200000 | mapfile -d '' arr",
|
||||
"seq 1 200000 | mapfile -t -- arr",
|
||||
"case \"$w\" in --auth|--config) grep -q x <<<\"$w\" ;; esac",
|
||||
"cat <<'EOF'",
|
||||
"seq 1 200000 | grep -q 1",
|
||||
"EOF",
|
||||
"",
|
||||
))
|
||||
self.assertEqual(hazards(src), [])
|
||||
|
||||
def test_discovery_covers_managed_scripts(self):
|
||||
found = managed_scripts()
|
||||
for rel in (
|
||||
"scripts/svc.sh",
|
||||
"scripts/task.sh",
|
||||
"scripts/e2e.sh",
|
||||
"scripts/e2e-duties.sh",
|
||||
"scripts/lint-boundaries.sh",
|
||||
"scripts/hooks/pre-commit",
|
||||
"scripts/task",
|
||||
"scripts/dm",
|
||||
"scripts/agent-exec",
|
||||
"docker/entrypoint.sh",
|
||||
*EXTRA_SCRIPTS,
|
||||
):
|
||||
self.assertIn(rel, found)
|
||||
for rel in ("scripts/evidence.py", "scripts/lint-imports.go", "docker/Dockerfile", "docker/README.md"):
|
||||
self.assertNotIn(rel, found)
|
||||
self.assertFalse([p for p in found if p.startswith("runtime/")], found)
|
||||
self.assertFalse(set(EXEMPT) - set(found), "stale EXEMPT entries")
|
||||
|
||||
def test_managed_scripts_have_no_early_exit_pipe_reader(self):
|
||||
for rel in managed_scripts():
|
||||
if rel in EXEMPT:
|
||||
continue
|
||||
with self.subTest(script=rel):
|
||||
path = ROOT / rel
|
||||
self.assertTrue(path.is_file(), f"{rel} missing")
|
||||
found = hazards(path.read_text(encoding="utf-8"))
|
||||
self.assertEqual(
|
||||
found, [], "\n".join(f"{rel}:{line}: | {text}" for line, text in found)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+71
-107
@@ -10,12 +10,16 @@ import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import socket
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
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"
|
||||
|
||||
@@ -72,7 +76,7 @@ case "${1:-}" in
|
||||
# GET/HEAD 一律 200(健康探测语义与 http.server 等价);PUT /api/* 捕获头与
|
||||
# 请求体到 trace/put.txt 供播种断言(settings 播种验证,2026-10-11)。
|
||||
# HTTP 子进程轮询 ppid:父进程被 SIGKILL 后自行退出,避免与 recover 套件
|
||||
# 并行运行时泄漏端口占用(与 test_svc_recover 桩同规)。
|
||||
# 共用 1880x 端口时泄漏占用(与 test_svc_recover 桩同规)。
|
||||
python3 - "$port" "$host" "$trace_dir" "$data" <<'PYSRV' >/dev/null 2>&1 &
|
||||
import json, os, sqlite3, sys, threading, time
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
@@ -166,7 +170,6 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
tmp = tempfile.mkdtemp(prefix="svc-db-regress-")
|
||||
self.addCleanup(shutil.rmtree, tmp, True)
|
||||
self.root = Path(tmp)
|
||||
self._allocated_ports = set()
|
||||
(self.root / "scripts").mkdir(parents=True)
|
||||
shutil.copy2(SVC_SCRIPT, self.root / "scripts" / "svc.sh")
|
||||
self.trace = self.root / "trace"
|
||||
@@ -224,21 +227,6 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertEqual(proc.returncode, 0, proc.stdout)
|
||||
return proc
|
||||
|
||||
def available_port(self):
|
||||
"""Allocate a distinct service port for this test, including address changes.
|
||||
|
||||
The socket closes before svc.sh binds it, leaving a small allocation-to-start
|
||||
race with unrelated processes. Keep allocated numbers distinct within the
|
||||
test so stopped/restarted services never share another instance's port.
|
||||
"""
|
||||
while True:
|
||||
with socket.socket() as sock:
|
||||
sock.bind(("0.0.0.0", 0))
|
||||
port = sock.getsockname()[1]
|
||||
if port not in self._allocated_ports:
|
||||
self._allocated_ports.add(port)
|
||||
return port
|
||||
|
||||
def runtime(self, name):
|
||||
return self.root / "runtime" / name
|
||||
|
||||
@@ -308,12 +296,11 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 1. 存量 exec= DSN:连续启动两次均恢复,进程参数无秘密 ----
|
||||
|
||||
def test_legacy_exec_dsn_survives_consecutive_restarts(self):
|
||||
port = self.available_port()
|
||||
self.register_pg("pg1", 18890, password="legacy-pw-1")
|
||||
dsn = "postgresql://agent:legacy-pw-1@127.0.0.1:18890/agent"
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", port,
|
||||
f"--addr 127.0.0.1:{port} --data %s --plugins %s --auth auto --dsn %s"
|
||||
self.seed_agent("dev", 18791,
|
||||
"--addr 127.0.0.1:18791 --data %s --plugins %s --auth auto --dsn %s"
|
||||
% (d / "data", d / "plugins", dsn))
|
||||
for _ in range(2):
|
||||
self.clear_serve_record()
|
||||
@@ -335,12 +322,11 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 2. 仅 dbref:裸 restart 与重新部署均保留绑定 ----
|
||||
|
||||
def test_dbref_binding_survives_bare_restart_and_redeploy(self):
|
||||
port = self.available_port()
|
||||
self.register_pg("pg1", 18891, password="bind-pw-2")
|
||||
dsn = "postgresql://agent:bind-pw-2@127.0.0.1:18891/agent"
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", port,
|
||||
f"--addr 127.0.0.1:{port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("dev", 18792,
|
||||
"--addr 127.0.0.1:18792 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg")])
|
||||
self.clear_serve_record()
|
||||
@@ -360,30 +346,30 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 3. 改地址不改数据库与认证 ----
|
||||
|
||||
def test_addr_change_keeps_database_and_auth(self):
|
||||
old_port = self.available_port()
|
||||
new_port = self.available_port()
|
||||
self.register_pg("pg1", 18892, password="addr-pw-3")
|
||||
dsn = "postgresql://agent:addr-pw-3@127.0.0.1:18892/agent"
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", old_port,
|
||||
f"--addr 127.0.0.1:{old_port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("dev", 18793,
|
||||
"--addr 127.0.0.1:18793 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg")])
|
||||
self.clear_serve_record()
|
||||
self.svc("dev", "restart", "--addr", f"127.0.0.1:{new_port}")
|
||||
self.svc("dev", "restart", "--addr", "127.0.0.1:18794")
|
||||
record = self.serve_record()
|
||||
self.assertIn("AGENT_SYSTEM_DSN=%s" % dsn, record)
|
||||
self.assertIn("--auth auto", record)
|
||||
self.assertEqual(self.kv("dev", "dbref"), "pg1/agent")
|
||||
self.assertEqual(self.kv("dev", "addr"), f"127.0.0.1:{new_port}")
|
||||
self.assertIn(f"--addr 127.0.0.1:{new_port}", self.kv("dev", "exec"))
|
||||
self.assertEqual(self.kv("dev", "addr"), "127.0.0.1:18794")
|
||||
self.assertIn("--addr 127.0.0.1:18794", self.kv("dev", "exec"))
|
||||
self.assertEqual(self.kv("dev", "db"), "pg")
|
||||
|
||||
# ---- 4. 外部直连与配置文件来源:后续无参数启动仍恢复 ----
|
||||
|
||||
def test_external_dsn_and_conffile_recover_on_bare_start(self):
|
||||
port = self.available_port()
|
||||
d = self.runtime("dev")
|
||||
with socket.socket() as reservation:
|
||||
reservation.bind(("127.0.0.1", 0))
|
||||
port = reservation.getsockname()[1]
|
||||
self.seed_agent("dev", port,
|
||||
"--addr 127.0.0.1:%d --data %s --plugins %s --auth auto"
|
||||
% (port, d / "data", d / "plugins"))
|
||||
@@ -423,13 +409,9 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 5. 拒启场景:不降级 SQLite、不启动进程、登记可恢复、停机前发现 ----
|
||||
|
||||
def test_refusals_keep_registration_without_sqlite_fallback(self):
|
||||
a1_port = self.available_port()
|
||||
a2_port = self.available_port()
|
||||
a3_port = self.available_port()
|
||||
a4_port = self.available_port()
|
||||
d = self.runtime("a1")
|
||||
self.seed_agent("a1", a1_port,
|
||||
f"--addr 127.0.0.1:{a1_port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("a1", 18796,
|
||||
"--addr 127.0.0.1:18796 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pgmissing/agent"), ("db", "pg")])
|
||||
self.clear_serve_record()
|
||||
@@ -442,8 +424,8 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# 实例登记不全(缺 password)→ 拒启。
|
||||
self.register_pg("pginc", 18895, complete=False)
|
||||
d = self.runtime("a2")
|
||||
self.seed_agent("a2", a2_port,
|
||||
f"--addr 127.0.0.1:{a2_port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("a2", 18797,
|
||||
"--addr 127.0.0.1:18797 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pginc/agent"), ("db", "pg")])
|
||||
result = self.svc("a2", "restart", check=False)
|
||||
@@ -453,8 +435,8 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# dbref 与 dsn= 指向不同库 → 配置冲突拒启。
|
||||
self.register_pg("pg1", 18896, password="clash-pw")
|
||||
d = self.runtime("a3")
|
||||
self.seed_agent("a3", a3_port,
|
||||
f"--addr 127.0.0.1:{a3_port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("a3", 18798,
|
||||
"--addr 127.0.0.1:18798 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg"),
|
||||
("dsn", "postgresql://u:p@127.0.0.1:18999/otherdb")])
|
||||
@@ -464,8 +446,8 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
|
||||
# db=pg 但无可恢复 DSN → 拒启。
|
||||
d = self.runtime("a4")
|
||||
self.seed_agent("a4", a4_port,
|
||||
f"--addr 127.0.0.1:{a4_port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("a4", 18799,
|
||||
"--addr 127.0.0.1:18799 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
extra_rows=[("db", "pg")])
|
||||
result = self.svc("a4", "restart", check=False)
|
||||
@@ -474,11 +456,10 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertEqual(self.kv("a4", "db"), "pg")
|
||||
|
||||
def test_restart_refusal_happens_before_stopping_running_service(self):
|
||||
port = self.available_port()
|
||||
self.register_pg("pg1", 18897, password="run-pw")
|
||||
d = self.runtime("a5")
|
||||
self.seed_agent("a5", port,
|
||||
f"--addr 127.0.0.1:{port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("a5", 18800,
|
||||
"--addr 127.0.0.1:18800 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg")])
|
||||
self.start_ok("a5")
|
||||
@@ -496,13 +477,10 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 6. 认证预检与后端口径一致(环境变量 DSN 覆盖 auto)----
|
||||
|
||||
def test_auth_preflight_aligns_with_effective_env_dsn(self):
|
||||
nl1_port = self.available_port()
|
||||
nl2_port = self.available_port()
|
||||
nl3_port = self.available_port()
|
||||
# 非回环 + auto + 无任何 DSN → 预检快速拒绝,不启动进程。
|
||||
d = self.runtime("nl1")
|
||||
self.seed_agent("nl1", nl1_port,
|
||||
f"--addr 10.255.255.1:{nl1_port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("nl1", 18801,
|
||||
"--addr 10.255.255.1:18801 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"))
|
||||
self.clear_serve_record()
|
||||
result = self.svc("nl1", "start", check=False)
|
||||
@@ -513,9 +491,9 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# 环境变量提供 DSN → auto 判为已开认证:预检放行(桩绑定非回环地址失败即退,
|
||||
# 区别于预检拒绝——证明进入启动路径且 DSN 经环境交付)。
|
||||
d = self.runtime("nl2")
|
||||
self.seed_agent("nl2", nl2_port,
|
||||
f"--addr 10.255.255.1:{nl2_port} --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"), addr=f"10.255.255.1:{nl2_port}")
|
||||
self.seed_agent("nl2", 18802,
|
||||
"--addr 10.255.255.1:18802 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"), addr="10.255.255.1:18802")
|
||||
env_dsn = "postgresql://envu:env-pw@127.0.0.1:18898/agent"
|
||||
env2 = dict(self.env, SVC_NL2_DSN=env_dsn)
|
||||
self.clear_serve_record()
|
||||
@@ -526,9 +504,9 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
|
||||
# 显式 auth=off 覆盖环境 DSN → 仍拒绝。
|
||||
d = self.runtime("nl3")
|
||||
self.seed_agent("nl3", nl3_port,
|
||||
f"--addr 10.255.255.1:{nl3_port} --data %s --plugins %s --auth off"
|
||||
% (d / "data", d / "plugins"), addr=f"10.255.255.1:{nl3_port}")
|
||||
self.seed_agent("nl3", 18803,
|
||||
"--addr 10.255.255.1:18803 --data %s --plugins %s --auth off"
|
||||
% (d / "data", d / "plugins"), addr="10.255.255.1:18803")
|
||||
self.clear_serve_record()
|
||||
result = self.svc("nl3", "start", check=False,
|
||||
env=dict(self.env, SVC_NL3_DSN=env_dsn))
|
||||
@@ -539,11 +517,10 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 7. 显式解绑:环境残留不再悄悄重新选库 ----
|
||||
|
||||
def test_explicit_unbind_blocks_residual_env_dsn(self):
|
||||
port = self.available_port()
|
||||
self.register_pg("pg1", 18899, password="unbind-pw")
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", port,
|
||||
f"--addr 127.0.0.1:{port} --data %s --plugins %s"
|
||||
self.seed_agent("dev", 18804,
|
||||
"--addr 127.0.0.1:18804 --data %s --plugins %s"
|
||||
% (d / "data", d / "plugins"))
|
||||
self.svc("dev", "db", "pg1", "--no-restart")
|
||||
self.assertEqual(self.kv("dev", "dbref"), "pg1/agent")
|
||||
@@ -571,12 +548,10 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 8. mock 冲突与旧产物:行为明确,不隐式降级 ----
|
||||
|
||||
def test_mock_conflict_and_legacy_binary_refuse(self):
|
||||
mock_port = self.available_port()
|
||||
legacy_port = self.available_port()
|
||||
self.register_pg("pg1", 18902, password="mock-pw")
|
||||
d = self.runtime("mk1")
|
||||
self.seed_agent("mk1", mock_port,
|
||||
f"--addr 127.0.0.1:{mock_port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("mk1", 18805,
|
||||
"--addr 127.0.0.1:18805 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg")])
|
||||
result = self.svc("mk1", "deploy", "--from", "local", "--mock", check=False)
|
||||
@@ -587,8 +562,8 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
|
||||
# 旧产物(不消费环境变量 DSN)绑定服务 restart → 拒绝,提示可操作出路。
|
||||
d = self.runtime("lb1")
|
||||
self.seed_agent("lb1", legacy_port,
|
||||
f"--addr 127.0.0.1:{legacy_port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("lb1", 18806,
|
||||
"--addr 127.0.0.1:18806 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg")])
|
||||
env_legacy = dict(self.env, SVC_STUB_NO_ENV_DSN="1")
|
||||
@@ -600,17 +575,15 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
# ---- 9. 多实例顺序更新不串用 DSN;凭据不进日志与历史 ----
|
||||
|
||||
def test_sequential_restarts_no_cross_dsn_and_no_secret_leak(self):
|
||||
dev_port = self.available_port()
|
||||
prod_port = self.available_port()
|
||||
self.register_pg("pg1", 18903, password="iso-pw-one")
|
||||
self.register_pg("pg2", 18904, password="iso-pw-two")
|
||||
d1, d2 = self.runtime("dev"), self.runtime("prod")
|
||||
self.seed_agent("dev", dev_port,
|
||||
f"--addr 127.0.0.1:{dev_port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("dev", 18807,
|
||||
"--addr 127.0.0.1:18807 --data %s --plugins %s --auth auto"
|
||||
% (d1 / "data", d1 / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg")])
|
||||
self.seed_agent("prod", prod_port,
|
||||
f"--addr 127.0.0.1:{prod_port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("prod", 18808,
|
||||
"--addr 127.0.0.1:18808 --data %s --plugins %s --auth auto"
|
||||
% (d2 / "data", d2 / "plugins"),
|
||||
extra_rows=[("dbref", "pg2/agent"), ("db", "pg")])
|
||||
dsn1 = "postgresql://agent:iso-pw-one@127.0.0.1:18903/agent"
|
||||
@@ -669,13 +642,12 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
return shas[1], shas[0] # (旧, 新)
|
||||
|
||||
def test_update_preserves_binding(self):
|
||||
port = self.available_port()
|
||||
old_sha, new_sha = self.make_src_repo()
|
||||
self.register_pg("pg1", 18905, password="upd-pw")
|
||||
dsn = "postgresql://agent:upd-pw@127.0.0.1:18905/agent"
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", port,
|
||||
f"--addr 127.0.0.1:{port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("dev", 18809,
|
||||
"--addr 127.0.0.1:18809 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"),
|
||||
extra_rows=[("dbref", "pg1/agent"), ("db", "pg"),
|
||||
("source", "dev@%s" % old_sha)])
|
||||
@@ -698,7 +670,7 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
旧进程占口 → 报「非本服务」。env_config 须按实例清空 SVC_PIDCHECK 等。
|
||||
"""
|
||||
old_sha, new_sha = self.make_src_repo()
|
||||
ports = {name: self.available_port() for name in ("a", "b")}
|
||||
ports = {"a": free_test_port(), "b": free_test_port()}
|
||||
old_pids = {}
|
||||
for name, port in ports.items():
|
||||
d = self.runtime(name)
|
||||
@@ -762,12 +734,11 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
return env
|
||||
|
||||
def test_seed_puts_with_temporary_bearer_key_when_auth_on(self):
|
||||
port = self.available_port()
|
||||
self.register_pg("pg1", 18831, password="seed-pw-1")
|
||||
dsn = "postgresql://agent:seed-pw-1@127.0.0.1:18831/agent"
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", port,
|
||||
f"--addr 127.0.0.1:{port} --data %s --plugins %s --auth auto --dsn %s"
|
||||
self.seed_agent("dev", 18832,
|
||||
"--addr 127.0.0.1:18832 --data %s --plugins %s --auth auto --dsn %s"
|
||||
% (d / "data", d / "plugins", dsn))
|
||||
result = self.svc("dev", "start", env=self.model_env())
|
||||
self.assertIn("已播种设置:主模型", result.stdout)
|
||||
@@ -783,12 +754,11 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertIn("apikey revoke --dsn %s --owner op --name svc-seed" % dsn, cli)
|
||||
|
||||
def test_seed_writes_fallbacks_and_vision(self):
|
||||
port = self.available_port()
|
||||
self.register_pg("pg1", 18951, password="seed-pw-v")
|
||||
dsn = "postgresql://agent:seed-pw-v@127.0.0.1:18951/agent"
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", port,
|
||||
f"--addr 127.0.0.1:{port} --data %s --plugins %s --auth auto --dsn %s"
|
||||
self.seed_agent("dev", 18952,
|
||||
"--addr 127.0.0.1:18952 --data %s --plugins %s --auth auto --dsn %s"
|
||||
% (d / "data", d / "plugins", dsn))
|
||||
result = self.svc("dev", "start", env=self.model_env(
|
||||
SVC_MODEL_FALLBACKS="ZHIPU/GLM-5.3-FlashX,ZHIPU/GLM-5.3-Flash",
|
||||
@@ -804,12 +774,11 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertIn('"capable": "deepseek-v4.1-flash,qwen3.8-flash"', body_part)
|
||||
|
||||
def test_seed_owner_override_skips_account_lookup(self):
|
||||
port = self.available_port()
|
||||
self.register_pg("pg1", 18833, password="seed-pw-2")
|
||||
dsn = "postgresql://agent:seed-pw-2@127.0.0.1:18833/agent"
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", port,
|
||||
f"--addr 127.0.0.1:{port} --data %s --plugins %s --auth auto --dsn %s"
|
||||
self.seed_agent("dev", 18834,
|
||||
"--addr 127.0.0.1:18834 --data %s --plugins %s --auth auto --dsn %s"
|
||||
% (d / "data", d / "plugins", dsn))
|
||||
result = self.svc("dev", "start", env=self.model_env(SVC_SEED_OWNER="boss"))
|
||||
self.assertIn("已播种设置:主模型", result.stdout)
|
||||
@@ -819,10 +788,9 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertNotIn("user list", cli) # 显式指定即跳过账号解析
|
||||
|
||||
def test_seed_puts_without_credentials_when_auth_off(self):
|
||||
port = self.available_port()
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", port,
|
||||
f"--addr 127.0.0.1:{port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("dev", 18835,
|
||||
"--addr 127.0.0.1:18835 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"))
|
||||
result = self.svc("dev", "start", env=self.model_env())
|
||||
self.assertIn("已播种设置:主模型", result.stdout)
|
||||
@@ -845,10 +813,9 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
return db
|
||||
|
||||
def test_seed_replaces_old_system_secret(self):
|
||||
port = self.available_port()
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", port,
|
||||
f"--addr 127.0.0.1:{port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("dev", 18836,
|
||||
"--addr 127.0.0.1:18836 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"))
|
||||
db = self.plant_old_chat_secret("dev", "sk-old-secret")
|
||||
result = self.svc("dev", "start", env=self.model_env())
|
||||
@@ -863,10 +830,9 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertEqual(active[0], "sk-stub-model")
|
||||
|
||||
def test_seed_warns_when_old_secret_stays_active(self):
|
||||
port = self.available_port()
|
||||
d = self.runtime("dev")
|
||||
self.seed_agent("dev", port,
|
||||
f"--addr 127.0.0.1:{port} --data %s --plugins %s --auth auto"
|
||||
self.seed_agent("dev", 18837,
|
||||
"--addr 127.0.0.1:18837 --data %s --plugins %s --auth auto"
|
||||
% (d / "data", d / "plugins"))
|
||||
self.plant_old_chat_secret("dev", "sk-old-secret")
|
||||
result = self.svc("dev", "start", env=self.model_env(SVC_STUB_KEEP_OLD_SECRET="1"))
|
||||
@@ -904,7 +870,7 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
run(work, "push", "-u", "origin", "main")
|
||||
return remote, work
|
||||
|
||||
def rdm_env(self, remote, agent_name="rdm1"):
|
||||
def rdm_env(self, remote):
|
||||
env = dict(self.env)
|
||||
env.update({
|
||||
"SVC_RDM_SRC_DIR": str(self.root / "rdmcache"),
|
||||
@@ -913,8 +879,6 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
"SVC_MODEL_KEY": "sk-stub-model",
|
||||
"SVC_MODEL_NAME": "stub-model",
|
||||
})
|
||||
env["SVC_%sWEB_ADDR" % agent_name.upper()] = (
|
||||
"127.0.0.1:%d" % self.available_port())
|
||||
return env
|
||||
|
||||
def http_get(self, url, timeout=2):
|
||||
@@ -935,10 +899,10 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
return "http://%s:%s/" % (host, port)
|
||||
|
||||
def test_rdm_source_deploy_end_to_end(self):
|
||||
port = self.available_port()
|
||||
remote, work = self.make_rdm_repo()
|
||||
env = self.rdm_env(remote)
|
||||
result = self.svc("rdm1", "deploy", "--from", "rdm", "--port", port, env=env)
|
||||
result = self.svc("rdm1", "deploy", "--from", "rdm", "--port", str(free_test_port()),
|
||||
env=env)
|
||||
d = self.runtime("rdm1")
|
||||
# U2:组合产物入不可变 release,经 current 指针解析;插件空集、工作台随 release。
|
||||
active = d / "current"
|
||||
@@ -962,10 +926,9 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertNotIn("user list", self.cli_record())
|
||||
|
||||
def test_rdm_companion_missing_web_fails_deploy(self):
|
||||
port = self.available_port()
|
||||
remote, work = self.make_rdm_repo(with_web=False)
|
||||
env = self.rdm_env(remote, "rdm2")
|
||||
result = self.svc("rdm2", "deploy", "--from", "rdm", "--port", port,
|
||||
env = self.rdm_env(remote)
|
||||
result = self.svc("rdm2", "deploy", "--from", "rdm", "--port", str(free_test_port()),
|
||||
env=env, check=False)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertIn("ui-missing", result.stdout)
|
||||
@@ -974,9 +937,9 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertIsNone(self.kv("rdm2web", "kind"))
|
||||
|
||||
def test_source_switch_requires_flag(self):
|
||||
port = self.available_port()
|
||||
remote, work = self.make_rdm_repo()
|
||||
env = self.rdm_env(remote, "sw1")
|
||||
env = self.rdm_env(remote)
|
||||
port = str(free_test_port())
|
||||
# 先以 local 源部署(内嵌 UI 桩一律 200)。
|
||||
self.svc("sw1", "deploy", "--from", "local", "--port", port)
|
||||
old = self.kv("sw1", "source")
|
||||
@@ -997,7 +960,6 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertIn("rdm workbench", body)
|
||||
|
||||
def test_rdm_source_commits_and_update_track(self):
|
||||
port = self.available_port()
|
||||
remote, work = self.make_rdm_repo()
|
||||
env = self.rdm_env(remote)
|
||||
# commits rdm 列出来自独立 RDM 源仓的可部署提交。
|
||||
@@ -1005,7 +967,7 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
self.assertIn("RDM 仓提交", result.stdout)
|
||||
self.assertIn("rdm v1", result.stdout)
|
||||
# 部署 v1 → 远端推进 v2 → update 检测可更新并真更新(source 保持 rdm@ 轨道)。
|
||||
self.svc("rdm1", "deploy", "--from", "rdm", "--port", port, env=env)
|
||||
self.svc("rdm1", "deploy", "--from", "rdm", "--port", str(free_test_port()), env=env)
|
||||
old = self.kv("rdm1", "source")
|
||||
(work / "marker").write_text("v2\n")
|
||||
subprocess.run(["git", "add", "."], cwd=work, env=self.env, timeout=60, check=True)
|
||||
@@ -1030,7 +992,9 @@ class SvcDbRecoveryTests(unittest.TestCase):
|
||||
htdocs = self.root / "static-site"
|
||||
htdocs.mkdir()
|
||||
(htdocs / "index.html").write_text("<html>static-ok</html>\n")
|
||||
port = self.available_port()
|
||||
with socket.socket() as reservation:
|
||||
reservation.bind(("127.0.0.1", 0))
|
||||
port = reservation.getsockname()[1]
|
||||
result = self.svc(
|
||||
"uiqa", "deploy", "--kind", "static",
|
||||
"--dir", str(htdocs), "--addr", "127.0.0.1:%d" % port)
|
||||
|
||||
@@ -413,6 +413,51 @@ class IdentityTests(tsr.SvcAutoRecoverTests):
|
||||
hang.chmod(0o755)
|
||||
self.assertIn("rc=1", probe(hang), "探测挂死须有界返回无能力")
|
||||
|
||||
def test_U1_adopt_identityless_artifact_reports_pending(self):
|
||||
"""已装制品无 identity 能力:adopt 的 ensure 静默跳过,marker 留在 pending——
|
||||
输出必须如实报 pending,不得宣称 db_marker=established。"""
|
||||
self.seed_agent("adx", 9454)
|
||||
self.start_ok("adx")
|
||||
iid = self.manifest("adx")["instance_id"]
|
||||
self.svc("adx", "stop")
|
||||
self.install_noid_bin("adx")
|
||||
result = self.svc("adx", "adopt", "--confirm", iid)
|
||||
self.assertNotIn("db_marker=established", result.stdout)
|
||||
self.assertIn("db_marker=pending", result.stdout)
|
||||
self.assertEqual(self.manifest("adx")["identity"]["db_marker"], "pending")
|
||||
|
||||
def test_U1_update_pre_v27_schema_refused_with_migrate_guidance(self):
|
||||
"""候选有 identity 能力但库 schema 早于身份表:停服前拒绝,给出
|
||||
备份→stop→migrate→再部署的指引,而非「外宾行」误导;不自动迁移。"""
|
||||
cand = self.root / "stub-agent-system-noschema"
|
||||
cand.write_text(
|
||||
'#!/usr/bin/env bash\ncase "${1:-}" in identity) '
|
||||
'echo "identity ${2:-ensure}: identity table missing" >&2; exit 1 ;; '
|
||||
'*) exec "%s" "$@" ;; esac\n' % self.stub)
|
||||
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)
|
||||
self.start_ok("scx")
|
||||
self.set_kv("scx", "source", "dev@%s" % old_sha)
|
||||
mpath = self.runtime("scx") / "manifest.json"
|
||||
m = json.loads(mpath.read_text(encoding="utf-8"))
|
||||
m["identity"]["db_marker"] = "pending"
|
||||
mpath.write_text(json.dumps(m, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
pid = self.pid_of("scx")
|
||||
bin_before = (self.runtime("scx") / "bin" / "agent-system").read_bytes()
|
||||
|
||||
result = self.svc("update", "scx", check=False)
|
||||
self.assertNotEqual(result.returncode, 0, result.stdout)
|
||||
self.assertIn("migrate --data", result.stdout)
|
||||
self.assertIn("AGENT_SYSTEM_DSN", result.stdout)
|
||||
self.assertNotIn("外宾行", result.stdout)
|
||||
self.assertTrue(self.running("scx"), "拒绝必须发生在停旧服务之前")
|
||||
self.assertEqual(self.pid_of("scx"), pid)
|
||||
self.assertEqual((self.runtime("scx") / "bin" / "agent-system").read_bytes(),
|
||||
bin_before, "旧制品不得被覆盖")
|
||||
self.svc("scx", "stop")
|
||||
|
||||
def test_I3_start_ensures_sqlite_identity(self):
|
||||
self.seed_agent("idb1", 9441)
|
||||
self.start_ok("idb1")
|
||||
|
||||
@@ -21,6 +21,7 @@ import shutil
|
||||
import socket
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
@@ -28,6 +29,9 @@ import urllib.error
|
||||
import urllib.request
|
||||
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]
|
||||
|
||||
REAL = os.environ.get("SVC_TEST_REAL") == "1"
|
||||
@@ -165,7 +169,7 @@ class RealComboTests(tsr.SvcAutoRecoverTests):
|
||||
def test_U3_real_agent_identity_lifecycle_sqlite(self):
|
||||
"""真实组合二进制:领养 ensure → verify → 两次裸重启身份/锚点稳定;
|
||||
capabilities v1 探测契约。"""
|
||||
port = self.available_port()
|
||||
port = free_test_port()
|
||||
self.seed_real("rc1", port, self.real_bin)
|
||||
result = self.svc("rc1", "start")
|
||||
self.assertIn("已启动", result.stdout)
|
||||
@@ -212,7 +216,7 @@ class RealComboTests(tsr.SvcAutoRecoverTests):
|
||||
"""真实 RDM 组合二进制:identity 接线(Backend 公共 CLI)→ serve 门禁 →
|
||||
领域层可初始化。"""
|
||||
rdm_bin = Path(REAL_RDM_BIN).resolve()
|
||||
port = self.available_port()
|
||||
port = free_test_port()
|
||||
self.seed_real("rcr", port, rdm_bin)
|
||||
result = self.svc("rcr", "start")
|
||||
self.assertIn("已启动", result.stdout)
|
||||
@@ -236,10 +240,10 @@ class RealComboTests(tsr.SvcAutoRecoverTests):
|
||||
self.env["RDM_DISABLE_EXEC_TOOLS"] = "1"
|
||||
env = dict(self.env)
|
||||
env.update(ci_git_env())
|
||||
agent_port = self.available_port()
|
||||
env.update({"SVC_RDM_SRC_DIR": str(self.root / "rdmcache"),
|
||||
"SVC_RDM_ORIGIN": RDM_ORIGIN})
|
||||
result = self.svc("rcf", "deploy", "--from", "rdm", "--port", agent_port,
|
||||
agent_port = free_test_port()
|
||||
result = self.svc("rcf", "deploy", "--from", "rdm", "--port", str(agent_port),
|
||||
env=env, check=False)
|
||||
if result.returncode != 0:
|
||||
log = self.runtime("rcf") / "svc.log"
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
"""svc.sh 超级管理员凭据文件路径登记回归(superadmin_envfile=)。
|
||||
|
||||
deploy/start/restart 时传一次 AGENT_SYSTEM_ENV_FILE:svc 校验(普通文件、属当前用户、
|
||||
无组/其他权限位)后只登记路径;裸 restart 与 recover(清空环境的生命周期子进程)
|
||||
复用该路径转发给 serve;权限过宽在停旧服务前拒绝;空值清除登记;svc.env/svc.log
|
||||
绝不出现文件内容。桩二进制与夹具复用 tests/test_svc_recover.py。
|
||||
"""
|
||||
import importlib.util
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
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)
|
||||
_spec.loader.exec_module(tsr)
|
||||
|
||||
# 桩 serve 额外记录实际收到的 AGENT_SYSTEM_ENV_FILE(未设置记 <unset>)。
|
||||
_ANCHOR = '> "$trace_dir/serve.txt"\n'
|
||||
assert _ANCHOR in tsr.STUB
|
||||
STUB = tsr.STUB.replace(
|
||||
_ANCHOR,
|
||||
_ANCHOR + r""" printf 'AGENT_SYSTEM_ENV_FILE=%s\n' "${AGENT_SYSTEM_ENV_FILE-<unset>}" >> "$trace_dir/serve.txt"
|
||||
""", 1)
|
||||
|
||||
PASSWORD = "Dummy-SA-Pw-9f4c2e71-do-not-leak"
|
||||
UNSET = "<unset>"
|
||||
|
||||
|
||||
class SuperAdminEnvfileTests(tsr.SvcAutoRecoverTests):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
shutil.copy2(SOURCE / "scripts" / "svc_manifest.py",
|
||||
self.root / "scripts" / "svc_manifest.py")
|
||||
self.stub.write_text(STUB)
|
||||
for key in ("AGENT_SYSTEM_ENV_FILE", "SUPER_ADMIN_OWNER", "SUPER_ADMIN_PASSWORD"):
|
||||
self.env.pop(key, None)
|
||||
self.secrets = self.root / "secrets"
|
||||
self.secrets.mkdir(mode=0o700)
|
||||
self.cred = self.make_cred("sa.env", 0o600)
|
||||
|
||||
# ---- 夹具 ----
|
||||
|
||||
def make_cred(self, name, mode):
|
||||
p = self.secrets / name
|
||||
p.write_text("SUPER_ADMIN_OWNER=op\nSUPER_ADMIN_PASSWORD=%s\n" % PASSWORD)
|
||||
p.chmod(mode)
|
||||
return p
|
||||
|
||||
def env_with(self, **extra):
|
||||
env = dict(self.env)
|
||||
env.update(extra)
|
||||
return env
|
||||
|
||||
def served_envfile(self):
|
||||
for line in (self.trace / "serve.txt").read_text().splitlines():
|
||||
if line.startswith("AGENT_SYSTEM_ENV_FILE="):
|
||||
return line.split("=", 1)[1]
|
||||
return None
|
||||
|
||||
def fresh_trace(self):
|
||||
f = self.trace / "serve.txt"
|
||||
if f.exists():
|
||||
f.unlink()
|
||||
|
||||
def assert_same_path(self, recorded, expected):
|
||||
self.assertIsNotNone(recorded)
|
||||
self.assertTrue(os.path.isabs(recorded), recorded)
|
||||
self.assertEqual(os.path.realpath(recorded), os.path.realpath(expected))
|
||||
|
||||
def assert_no_secret(self, name, *outputs):
|
||||
for fname in ("svc.env", "svc.log", "manifest.json", "serve.env", "history.jsonl"):
|
||||
p = self.runtime(name) / fname
|
||||
if p.exists():
|
||||
self.assertNotIn(PASSWORD, p.read_text(errors="replace"), fname)
|
||||
for out in outputs:
|
||||
self.assertNotIn(PASSWORD, out)
|
||||
|
||||
def start_with_cred(self, name, cred=None):
|
||||
result = self.svc(name, "start",
|
||||
env=self.env_with(AGENT_SYSTEM_ENV_FILE=str(cred or self.cred)))
|
||||
self.assertIn("已启动", result.stdout)
|
||||
return result
|
||||
|
||||
# ---- 用例 ----
|
||||
|
||||
def test_sa_start_records_path_and_bare_restart_reuses_it(self):
|
||||
self.seed_agent("dev", tsr.free_fixture_port())
|
||||
r1 = self.start_with_cred("dev")
|
||||
self.assert_same_path(self.kv("dev", "superadmin_envfile"), self.cred)
|
||||
self.assert_same_path(self.served_envfile(), self.cred)
|
||||
status = self.svc("dev", "status")
|
||||
self.assertIn("超管凭据文件", status.stdout)
|
||||
self.fresh_trace()
|
||||
r2 = self.svc("dev", "restart") # 环境里不再有 AGENT_SYSTEM_ENV_FILE
|
||||
self.assertIn("已启动", r2.stdout)
|
||||
self.assert_same_path(self.served_envfile(), self.cred)
|
||||
self.assert_same_path(self.kv("dev", "superadmin_envfile"), self.cred)
|
||||
self.assert_no_secret("dev", r1.stdout, r2.stdout, status.stdout)
|
||||
|
||||
def test_sa_relative_path_recorded_absolute(self):
|
||||
self.seed_agent("dev", tsr.free_fixture_port())
|
||||
result = self.svc("dev", "start",
|
||||
env=self.env_with(AGENT_SYSTEM_ENV_FILE="secrets/sa.env"))
|
||||
self.assertIn("已启动", result.stdout)
|
||||
self.assert_same_path(self.kv("dev", "superadmin_envfile"), self.cred)
|
||||
|
||||
def test_sa_recover_forwards_recorded_path(self):
|
||||
self.seed_agent("dev", tsr.free_fixture_port())
|
||||
self.start_with_cred("dev")
|
||||
old_pid = self.kill9("dev")
|
||||
self.fresh_trace()
|
||||
result = self.svc("recover", "--quiet")
|
||||
self.assertIn("recover 汇总:拉回 1", result.stdout)
|
||||
new_pid = self.pid_of("dev")
|
||||
self.assertTrue(self.pid_alive(new_pid))
|
||||
self.assertNotEqual(new_pid, old_pid)
|
||||
self.assert_same_path(self.served_envfile(), self.cred)
|
||||
self.assert_same_path(self.kv("dev", "superadmin_envfile"), self.cred)
|
||||
self.assert_no_secret("dev", result.stdout)
|
||||
|
||||
def test_sa_loose_permissions_refused_before_stop(self):
|
||||
self.seed_agent("dev", tsr.free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
pid = self.pid_of("dev")
|
||||
before = (self.runtime("dev") / "svc.env").read_bytes()
|
||||
loose = self.make_cred("loose.env", 0o644)
|
||||
result = self.svc("dev", "restart", check=False,
|
||||
env=self.env_with(AGENT_SYSTEM_ENV_FILE=str(loose)))
|
||||
self.assertNotEqual(result.returncode, 0, result.stdout)
|
||||
self.assertIn("权限过宽", result.stdout)
|
||||
self.assertIn(str(loose), result.stdout)
|
||||
self.assertEqual(self.pid_of("dev"), pid)
|
||||
self.assertTrue(self.pid_alive(pid), "拒绝时不得停掉运行中的服务")
|
||||
self.assertEqual((self.runtime("dev") / "svc.env").read_bytes(), before)
|
||||
self.assert_no_secret("dev", result.stdout)
|
||||
|
||||
def test_sa_missing_file_refused_before_stop(self):
|
||||
self.seed_agent("dev", tsr.free_fixture_port())
|
||||
self.start_ok("dev")
|
||||
pid = self.pid_of("dev")
|
||||
result = self.svc("dev", "restart", check=False,
|
||||
env=self.env_with(AGENT_SYSTEM_ENV_FILE=str(self.secrets / "nope.env")))
|
||||
self.assertNotEqual(result.returncode, 0, result.stdout)
|
||||
self.assertIn("nope.env", result.stdout)
|
||||
self.assertTrue(self.pid_alive(pid))
|
||||
self.assertEqual(self.pid_of("dev"), pid)
|
||||
|
||||
def test_sa_empty_value_clears_key(self):
|
||||
self.seed_agent("dev", tsr.free_fixture_port())
|
||||
self.start_with_cred("dev")
|
||||
self.assertIsNotNone(self.kv("dev", "superadmin_envfile"))
|
||||
self.fresh_trace()
|
||||
self.svc("dev", "restart", env=self.env_with(AGENT_SYSTEM_ENV_FILE=""))
|
||||
self.assertIsNone(self.kv("dev", "superadmin_envfile"))
|
||||
self.assertEqual(self.served_envfile(), "")
|
||||
self.fresh_trace()
|
||||
self.svc("dev", "restart")
|
||||
self.assertEqual(self.served_envfile(), UNSET)
|
||||
|
||||
def test_sa_explicit_environment_overrides_record(self):
|
||||
self.seed_agent("dev", tsr.free_fixture_port())
|
||||
self.start_with_cred("dev")
|
||||
self.fresh_trace()
|
||||
r = self.svc("dev", "restart", env=self.env_with(SUPER_ADMIN_OWNER="op"))
|
||||
self.assertEqual(self.served_envfile(), UNSET, "显式凭据环境优先,不回落登记路径")
|
||||
self.assert_same_path(self.kv("dev", "superadmin_envfile"), self.cred)
|
||||
other = self.make_cred("other.env", 0o600)
|
||||
self.fresh_trace()
|
||||
self.svc("dev", "restart", env=self.env_with(AGENT_SYSTEM_ENV_FILE=str(other)))
|
||||
self.assert_same_path(self.served_envfile(), other)
|
||||
self.assert_same_path(self.kv("dev", "superadmin_envfile"), other)
|
||||
self.assert_no_secret("dev", r.stdout)
|
||||
|
||||
def test_sa_recorded_file_gone_warns_and_skips(self):
|
||||
self.seed_agent("dev", tsr.free_fixture_port())
|
||||
self.start_with_cred("dev")
|
||||
self.cred.unlink()
|
||||
self.fresh_trace()
|
||||
result = self.svc("dev", "restart")
|
||||
self.assertIn("已不存在", result.stdout)
|
||||
self.assertEqual(self.served_envfile(), UNSET)
|
||||
|
||||
|
||||
# 只跑本文件用例;继承来的自恢复用例由 tests/test_svc_recover.py 负责。
|
||||
for _name in dir(tsr.SvcAutoRecoverTests):
|
||||
if _name.startswith("test_") and _name not in SuperAdminEnvfileTests.__dict__:
|
||||
setattr(SuperAdminEnvfileTests, _name, None)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -493,6 +493,26 @@ class TaskLifecycleTests(unittest.TestCase):
|
||||
self.assertTrue((self.repo / "component/content").is_file(), result.stdout)
|
||||
self.assertEqual(self.git(self.repo, "config", "--get", "submodule.component.url").stdout.strip(), str(self.root / "component"))
|
||||
|
||||
def test_finish_recycles_submodule_when_index_listing_exceeds_pipe_buffer(self):
|
||||
self.add_submodule()
|
||||
# Entries sorting after the gitlink must outlast a 64 KiB pipe once an early-exit reader stops.
|
||||
bulk = self.repo / "zz-bulk"
|
||||
bulk.mkdir()
|
||||
for i in range(2000):
|
||||
(bulk / f"entry-{i:05d}-{'p' * 40}").write_text("")
|
||||
self.git(self.repo, "add", "zz-bulk")
|
||||
self.git(self.repo, "commit", "-m", "Add bulk entries")
|
||||
self.git(self.repo, "push", "origin", "main")
|
||||
wt = self.worktree()
|
||||
self.git(wt, "submodule", "update", "--init", "--recursive")
|
||||
listing = self.git(wt, "ls-files", "--stage").stdout
|
||||
before_gitlink, after_gitlink = listing.split("\tcomponent\n", 1)
|
||||
self.assertTrue(before_gitlink.rsplit("\n", 1)[-1].startswith("160000 "), listing[:200])
|
||||
self.assertGreater(len(listing.encode()), 64 * 1024)
|
||||
self.assertGreater(len(after_gitlink.encode()), 2 * 64 * 1024)
|
||||
result = self.task(wt, "finish")
|
||||
self.assertFalse(wt.exists(), result.stdout)
|
||||
|
||||
def test_finish_requires_writer_exit_confirmation(self):
|
||||
wt = self.worktree()
|
||||
result = self.task(wt, "finish", check=False, env=self.env)
|
||||
|
||||
Reference in New Issue
Block a user