Merge authoritative main into the task branch

Why: include the current integration baseline before verification.

Evidence: fetched origin/main=ba81d638a6c845d0acde6896f4001bed096ba26c.

Impact: integrate upstream changes without rewriting task history.

Refs: task-sync
This commit is contained in:
2026-10-18 11:40:16 +00:00
6 changed files with 356 additions and 2 deletions
@@ -0,0 +1,260 @@
# feature/u8-sqlite-instance-guard-r2 任务卡
任务主记录:PR #615 https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/615;状态:已完成(合入前闭合);owner:weichen
## 基线与授权
- 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= b5e23daed2f0ea46261979de1d62e4de0cfa88f9
- 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-u8-sqlite-instance-guard-r2(worktree);分支:feature/u8-sqlite-instance-guard-r2
- 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false
- 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=14dd98bb26f46929106a9d4b7b76b137d9d996953e3466612d8140a2ad7bb426;triage_receipt=/tmp/cleanup-20261018/u8r2-triage.json;receipt_sha256=7c12187e848a9851e0ae47b2fb9345de393ff5dcd1625be6e3fd952e087a8074
- 授权与交付终点:U8 方案与实施计划由用户 2026-10-12 批准(见旧卡 .task/feature-u8-sqlite-instance-guard.md,PR #482);2026-10-18 worktree 收尾会话中用户授权“U8 同步主干后推进到合入”。本任务交付 Backend PR #210 合入后的 Dev gitlink 固定、U8 规格与 svc 文档;终点为 Dev PR 合入并收口,关闭 #482(被本任务取代)
- 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进)
- 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md)
- 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入)
- 人工决策点:仅限目标/范围/验收变更、重大取舍、高后果操作或无法自动判定的业务验收
- 停止条件(沿用旧卡):需改 scripts/svc.sh、对外 HTTP 契约、登录页,或需 live 实例/共享 PG 写入/部署
- 当前阶段:实施与自检;当前增量:I1(U8 移植到当前主干)
- 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位)
## 目标与非目标
同一 SQLite 数据文件只允许一个活动服务运行时;应用连接核验 SQLite WAL+FULL,PostgreSQL 拒绝 fsync/full_page_writes/synchronous_commit 为 off(R11/R12,语义与旧卡一致)。旧 Backend PR #183 与 Dev PR #482 落后主干并有冲突(迁移号、main.go、PROJECT.md、architecture-baseline.json、gitlink),按治理规则不由 host 手解冲突,改为从当前主干新开 r2 由外部 worker 移植。
Dev 侧产物:`docs/recovery/u8-sqlite-instance-guard.md`、`docs/svc.md` 的 U8 段、`PROJECT.md` 与 `policy/architecture-baseline.json` 的必要登记、`modules/backend` gitlink 固定到 #210 合入后的 main SHA。
Dev pin 最终指向 Backend `276dd73824116fb55064eefef17f069ee1545ada`(Backend 主干含 #211 + U8 + development-manager 对齐)。
非目标:不改 scripts/svc.sh;不在 live 实例故障注入;不写共享 PostgreSQL;账户 CLI 迁移互斥归 U7。
## Agent 实施收据
路由与 fail-stop 规则见根目录 AGENT-EXECUTION.md 与 policy/delivery.md;每次尝试一行,原始日志留在源码外收据路径。
| 单元 ID | 预期范围 | 配置摘要 | 角色 | 候选顺序 | attempt | CLI/模型 | 终态 | transport | fallback | 源码外收据路径 |
|---|---|---|---|---|---|---|---|---|---|---|
| backend-u8-sqlite-instance-guard-port-r2 | Backend cmd/agent-system、cmd/storage-migrate、internal/{app,instanceguard,store} | AGENT-EXECUTION.md | implementation | cursor→agy→pi | 1 | cursor | completed | 7b379d28e74fc695dcda4750ad64f223d41ec5af(parent cac60c08,apply 快进到 Backend feature/u8-sqlite-instance-guard-r2) | 无 | /tmp/cleanup-20261018/u8-backend-port-run.json |
| dev-u8-sqlite-instance-guard-port-r2 | Dev docs/recovery、docs/svc.md、PROJECT.md、policy/architecture-baseline.json、modules/backend | AGENT-EXECUTION.md | implementation | cursor→agy→pi | 1 | cursor | completed | b829de223b7a516f6b3206ed5eb0a3ced43df3d5(parent 0968a86,apply 快进) | 无 | /tmp/cleanup-20261018/u8-dev-port-run.json |
| dev-u8-backend-repin-d7be3fd | modules/backend | AGENT-EXECUTION.md | implementation | cursor→agy→pi | 0 | — | invalid-config(canonical checkout is dirty:apply 后子模块检出未同步;未启动任何尝试,无副作用) | — | 无 | /tmp/cleanup-20261018/u8-dev-repin-run.json |
| dev-u8-backend-repin-d7be3fd | modules/backend | AGENT-EXECUTION.md | implementation | cursor→agy→pi | 1 | cursor | completed(子模块检出对齐到已记录 pin 后重新运行) | 6d9bd7c4306c22f694d0939e8b682e5f57933ea6(parent b829de2,apply 快进) | 无 | /tmp/cleanup-20261018/u8-dev-repin-run2.json |
| backend-u8-dm-pin-align | Backend deps/development-manager | AGENT-EXECUTION.md | implementation | cursor→agy→pi | 1 | cursor | completed | 276dd73824116fb55064eefef17f069ee1545ada(parent d7be3fd,apply 快进,已推送 #210) | 无 | /tmp/cleanup-20261018/u8-backend-dm-bump-run.json |
| dev-u8-backend-repin-276dd73 | modules/backend | AGENT-EXECUTION.md | implementation | cursor→agy→pi | 0 | — | invalid-config(canonical checkout is dirty:evidence 写卡未提交;未启动尝试,无副作用) | — | 无 | /tmp/cleanup-20261018/u8-dev-repin2-run.json |
| dev-u8-backend-repin-276dd73 | modules/backend | AGENT-EXECUTION.md | implementation | cursor→agy→pi | 1 | cursor | completed | 0c645e4524c4709a37104ef00d3383d03cfda738(parent b1a0c29,apply 快进) | 无 | /tmp/cleanup-20261018/u8-dev-repin2-run2.json |
组合指针对齐:Backend #208 引入的 `deps/development-manager` 固定在 `bd3a7de`,Dev 顶层为 `67784f4`(其后代,+22 提交),`make test-pins` 硬失败。最小修复为 #210 追加 gitlink 对齐提交 `276dd73`(Backend `agent-exec validate` 与 `dm governance check` 在新 pin 下通过),Dev pin 改指 `276dd73`,`test-pins` 通过。
扩围记录(用户 2026-10-18 授权):Dev 主干原 pin `b00e192`(管理员密码管理 + 解释器路径修复)从未进入 Backend 主干,直接改 pin 到 U8 会回退已部署功能。经授权先以 Backend PR #211 将其合入 Backend 主干(merge `a670d2b`,PR HEAD `78ae397` CI 通过),再把 #210 与主干同步为 `d7be3fd`(CI 通过)并追加对齐提交 `276dd73`;#210 以 fast-forward 合入后 Backend 主干即为 `276dd73`,与 Dev pin 一致。
## 关联方向与规划
- 适用状态:已有主记录(恢复方案 r3 U8,用户 2026-10-12 批准)
- 主记录路径/版本:modules/documents/docs/proposals/2026-10-12-service-restart-data-recovery.md(r3);规格 docs/recovery/u8-sqlite-instance-guard.md
- 对应里程碑:恢复方案 U8(批次 A0 的隔离半边)
## 需求分析
| ID | 描述 | 项目符合度及依据 | 优先级 | 验收条件 | 依赖 | 结论 |
|---|---|---|---|---|---|---|
| R11 | 同一 SQLite 只允许一个活动服务 | 需补齐;主干无跨进程锁 | P0 | T8-1–T8-5(规格第 5 节) | recovery U1 身份 | 纳入(沿用 2026-10-12 批准) |
| R12 | 应用连接上的耐久档可核验 | 需补齐;DSN 无 synchronous=FULL 也无读回 | P0 | T8-6 | 无 | 纳入(沿用 2026-10-12 批准) |
## 方案与计划
- 方案引用:恢复方案 r3 U8;规格 docs/recovery/u8-sqlite-instance-guard.md(本分支由 worker 移植,迁移号随主干改为 v36)
- 实施计划引用:锁在 OpenSQLite 之前;instance_guard 为迁移 v36;migrate 子命令与 serve 共用 openServiceStore
- 测试计划引用:T8-1–T8-6 见规格第 5 节;PG 耐久在线拒绝只做策略函数单测,真实 PG 读回由 Dev CI test-backend-pg 覆盖
## 阶段记录
| 阶段 | 对象版本 | 状态 | 决策/执行者 | 时间 | 依据 |
|---|---|---|---|---|---|
| 需求拆解 | 恢复方案 r3 U8 | 通过 | 用户 | 2026-10-12 | 旧卡阶段评审;2026-10-18 用户授权同步主干推进到合入 |
| 需求评估 | 同上 | 通过 | 用户 | 2026-10-12 | 同上 |
| 需求表确认 | 同上 | 通过 | 用户 | 2026-10-12 | 同上 |
| 方案设计 | 同上 | 通过 | 用户 | 2026-10-12 | 同上 |
| 实施计划 | 旧卡与规格第 5 节 | 通过 | 用户 | 2026-10-12 | 同上;r2 仅移植,不改语义 |
| 实施与自检 | Backend 276dd73;Dev 3e32999 | 完成 | Cursor worker(5 次成功运行)+ host 集成 | 2026-10-18 | 实施收据表;host 本地组合树 go build/vet/test 与 test-boundaries/test-pins(SELF) |
| 正式测试与验收 | Dev 3e32999(pin Backend 276dd73) | 通过 | Gitea CI | 2026-10-18 | GATE:Dev runs/3775(test、test-pg、svc-realcombo);Backend runs/3773–3774(276dd73)、runs/3763–3764(#211 78ae397) |
| 代码 Review | Backend cac60c0..276dd73、ab12fea..b00e192;Dev b5e23da..3e32999 | 通过 | Host 主模型(非实施会话) | 2026-10-18 | 见“合入与清理 / Review 记录” |
| 合入 | 待定 | 待执行 | — | — | — |
| 合入核验与收口 | 待定 | 待执行 | — | — | — |
## 探索试验与迭代
| 增量/试验 | 假设 | 范围 | 判断和停止条件 | 授权/决策依据 | 结果 | 变更影响 | 需重审补测 |
|---|---|---|---|---|---|---|---|
| 待定义 | — | — | — | 待核对 | NOT_RUN | 待评估 | 待评估 |
## 同步记录
| 节点 | sync_base_sha | tested_head_sha | checked_at |
|---|---|---|---|
| 启动 | b5e23daed2f0ea46261979de1d62e4de0cfa88f9 | b5e23daed2f0ea46261979de1d62e4de0cfa88f9 | 2026-10-18T09:22:34Z |
## 证据(Evidence)
<!-- CMD="<命令>" make evidence 写入;手抄/转写输出视为无效证据(AGENTS.md 证据与验证门禁)。 -->
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|---|---|---|---|---|---|
## 交付调度
- 调度状态:未入队;调度记录:无
- entry_id:无;attempt_id:无;窗口执行者:无
- 固定候选:无(base/head、组件与相关配置引用实际尝试)
- 调度依据:policy/delivery.md#合入调度
- 固定后证据:源码外 RECEIPT;实际结果后按授权原样导入和闭合
## 结果与验证
- 结果:U8 活动服务锁、耐久核验与 migrate 子命令移植到当前主干(Backend #210),Dev pin 与文档更新(本 PR);Backend 主干补齐 Dev 已部署的密码管理(#211);development-manager 组合指针对齐。
- 验证:Backend CI PASS(GATE:78ae397、7b379d2、d7be3fd、276dd73);Dev CI PASS(GATE:3e32999 runs/3775 test/test-pg/svc-realcombo);本地 test-boundaries/test-pins PASS(SELF,证据表);T8 故障注入、部署与运行验收 NOT_RUN(不在本任务范围)。
- 影响与剩余项:新增 Backend 表 v36;未部署。非阻塞记录:Backend `pkg/identity` TestDSNFileMode 依赖 umask 022,本机 umask 0077 下失败(CI 通过);Backend 分支 `fix/execenv-python-sandbox-path`、`feature/auth-one-time-password` 已被 #211 吸收,可在阶段 4 回收。
## 规划进展与下一步
- 本次推进/剩余:U8 实现与 pin 合入;Documents 正文确认状态回写与 U8 运行验收留待后续
- 首选下一任务:worktree 收尾计划的下一条线(RDM settings 同步主干并重做阶段 7)
- 推荐依据:用户 2026-10-18 授权的收尾顺序
- 前置条件/待确认:无
- 预期产物/验收条件:见收尾计划
- 重复任务核对:取代 Dev #482 与 Backend #183;旧任务卡 feature-u8-sqlite-instance-guard 随旧 worktree 放弃式回收
- 启动授权:未授权(建议不自动启动新任务,仍须明确授权)
## 合入与清理
- Review 记录:Host 主模型(未参与实施,worker 为独立 Cursor 进程)于 2026-10-18 审阅 Backend `cac60c0..7b379d2`(17 文件:迁移 v36、compat 36、storage-migrate 表清单、main.go migrate/adopt、DSN synchronous(FULL)+verifyDurability、openServiceStore 保留 S3/team 档拒绝就绪/系统身份核验)、`d7be3fd..276dd73`(仅 gitlink)、#211 `ab12fea..b00e192`(建号/随机重置单事务+审计+no-store)、Dev `b5e23da..3e32999`(文档与参考版仅 v28→v36 差异;PROJECT.md 仅两句;baseline 两条边)。无阻塞问题;重放依据为 Dev CI runs/3775 与 Backend CI runs/3773–3774。
- 冲突记录:旧分支(#482/#183)与主干冲突,不手解,改由 worker 在 R0 `b5e23da` 与 Backend `cac60c0` 上重新移植;#211/#210 经 Gitea 服务端无冲突同步主干(`78ae397`、`d7be3fd`,树已核实等于 merge-tree 结果);Dev task-sync changed=0。组合兼容由 Dev CI test-pg/svc-realcombo 核验。
- CI 记录:Dev 组合候选 `3e32999ca9a22eef866fbf32ba2638375896b4a6` runs/3775(test、test-pg、svc-realcombo)success;Backend `276dd73824116fb55064eefef17f069ee1545ada` runs/3773–3774(vet-and-test、governance)success;最终 HEAD 的 CI 以 PR #615 状态为准(GATE,Gitea Actions)。
- 合入记录:授权依据为用户 2026-10-18“U8 同步主干后推进到合入”及同日密码管理扩围授权。Backend #211 以 HEAD `78ae397` 合入 main(merge `a670d2b`);Backend #210 以 HEAD `276dd73` fast-forward 合入,Backend main = `276dd73824116fb55064eefef17f069ee1545ada`,与本 PR 的 `modules/backend` pin 一致。Dev PR #615 在最终 HEAD CI 通过后由智能体合入;#482 关闭并注明被取代。
- 清理记录: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-18T09:21:46.646488+00:00",
"command": "make precheck",
"exit_code": 0,
"cwd": "/home/weichen/projects/Agent-System-Dev",
"before_head": "b5e23daed2f0ea46261979de1d62e4de0cfa88f9",
"after_head": "b5e23daed2f0ea46261979de1d62e4de0cfa88f9",
"branch": "main",
"target_branch": "main",
"target_head": "b5e23daed2f0ea46261979de1d62e4de0cfa88f9",
"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=b5e23daed2",
"[remote] origin/main=b5e23daed2 ahead=0 behind=0",
"[tree] clean submodules_drift=0 uninit=3",
"[warn] 3 个子模块未初始化——构建/测试前需物化(不阻断写入)",
"[worktrees] linked=9",
"[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] b5e23daed2(完整:b5e23daed2f0ea46261979de1d62e4de0cfa88f9)",
"[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": "db6d6c9f778b033c6a7ee0e6242afdcd5ae114fabf3ebec55d0cba732be99ea5",
"receipt_sha256": "5e0530e3a51eadb4c241dd4bb2332e9f7f5c828bec6d085619ab5cf8ec30551d",
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/5e0530e3a51eadb4c241dd4bb2332e9f7f5c828bec6d085619ab5cf8ec30551d.json"
}
```
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
```json
{
"summary_schema": 1,
"source": "SELF",
"timestamp": "2026-10-18T09:40:43.429397+00:00",
"command": "make test-boundaries test-pins",
"exit_code": 2,
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-u8-sqlite-instance-guard-r2",
"before_head": "a374cb83af0c0b3ffc7fb4b8fb0d2ee9e66cd793",
"after_head": "a374cb83af0c0b3ffc7fb4b8fb0d2ee9e66cd793",
"branch": "feature/u8-sqlite-instance-guard-r2",
"target_branch": "main",
"target_head": "b5e23daed2f0ea46261979de1d62e4de0cfa88f9",
"root_target": "main",
"immediate_target": "main",
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"output_tail": [
"ok modules/backend logging-system",
"ok deps/agent-native agent-intent",
"warn: deps/agent-native deps/development-guidelines is 26b562d2e34f, Dev is 2688fd2be6a5",
"ok deps/agent-native error-code-system",
"ok deps/agent-native logging-system",
"ok deps/agent-squad agent-intent",
"ok deps/agent-squad agent-native",
"warn: deps/agent-squad deps/development-guidelines is 26b562d2e34f, Dev is 2688fd2be6a5",
"ok deps/agent-squad error-code-system",
"ok deps/agent-squad logging-system",
"warn: deps/agent-intent deps/development-guidelines is 26b562d2e34f, Dev is 2688fd2be6a5",
"ok deps/agent-intent error-code-system",
"ok deps/agent-intent logging-system",
"make[1]: *** [Makefile:192: test-pins] Error 1",
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-u8-sqlite-instance-guard-r2'"
],
"output_sha256": "40b4bada2231b745080bd65b82a9eceb94ff042e2654f00c119518698f80ebd4",
"receipt_sha256": "90b6c0864e7dac1838ea43070d823ae13b360c24d6532085c7c09a7c3cfa5603",
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/90b6c0864e7dac1838ea43070d823ae13b360c24d6532085c7c09a7c3cfa5603.json"
}
```
<!-- Local receipt: SELF. Digests detect corruption; they do not authenticate a reviewer or CI. -->
```json
{
"summary_schema": 1,
"source": "SELF",
"timestamp": "2026-10-18T09:45:38.779500+00:00",
"command": "make test-boundaries test-pins",
"exit_code": 0,
"cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-u8-sqlite-instance-guard-r2",
"before_head": "0c645e4524c4709a37104ef00d3383d03cfda738",
"after_head": "0c645e4524c4709a37104ef00d3383d03cfda738",
"branch": "feature/u8-sqlite-instance-guard-r2",
"target_branch": "main",
"target_head": "b5e23daed2f0ea46261979de1d62e4de0cfa88f9",
"root_target": "main",
"immediate_target": "main",
"before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"output_tail": [
"ok modules/backend logging-system",
"ok deps/agent-native agent-intent",
"warn: deps/agent-native deps/development-guidelines is 26b562d2e34f, Dev is 2688fd2be6a5",
"ok deps/agent-native error-code-system",
"ok deps/agent-native logging-system",
"ok deps/agent-squad agent-intent",
"ok deps/agent-squad agent-native",
"warn: deps/agent-squad deps/development-guidelines is 26b562d2e34f, Dev is 2688fd2be6a5",
"ok deps/agent-squad error-code-system",
"ok deps/agent-squad logging-system",
"warn: deps/agent-intent deps/development-guidelines is 26b562d2e34f, Dev is 2688fd2be6a5",
"ok deps/agent-intent error-code-system",
"ok deps/agent-intent logging-system",
"pins ok",
"make[1]: Leaving directory '/home/weichen/projects/Agent-System-Dev-wt-feature-u8-sqlite-instance-guard-r2'"
],
"output_sha256": "79539abc4b3e10f25c6a859b7b8d3e1f1c9b1da1945f117226803949b1c20397",
"receipt_sha256": "19650ed0a61f602519de399d0429fcb00029062249276b9ce43148b1f6db7968",
"receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/19650ed0a61f602519de399d0429fcb00029062249276b9ce43148b1f6db7968.json"
}
```
## 闭合记录(合入前折叠)
- 本卡在 PR #615(https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/615)合入前闭合并归档,随该 PR 一同合入。
- 该 PR 的合并提交即本任务的落地记录。
- 合入后无需回填,也无需单独的闭合 PR。
+2 -1
View File
@@ -54,12 +54,13 @@ U2 起实施与部署分别评审。
svc 启停、异常进程/主机退出后的依赖、配置/数据身份、任务/HITL/副作用、文件与组合备份恢复;
r2 已批准。r3.1 增补 D1 实例身份收尾、D2 可回滚切换、D3 真实组合门禁,并记录 2026-10-12 rdmcand 数据归属:会话不在 dev 库的 rdmcand 窗口内,领域事件为空,不跨实例搬迁。
D1 与 D2 已合入 Backend #184、RDM #291、Dev #485。D3 的 `make test-lifecycle` 跑真实组合用例;登录仍是 NOT_RUN,依赖账户方案 U7/U5,不在本轮公共化账户 CLI。r3 其余未批条款仍待人工评审。
2026-10-12 用户批准 r3 新增(R11/R12、KTD13–15、U8):SQLite 活动服务锁与耐久核验已实施,故障注入、部署与运行验收仍 NOT_RUN。
- [svc 统一部署与一次性登录 Key 方案 r1](modules/documents/docs/proposals/2026-10-12-svc-unified-deploy-and-login-key.md):
二进制/源码统一部署、环境准备、无初始密码开户与账户恢复;一次性 Key 仅兑换设密受限会话;
2026-10-12 已批准;U1/U4 实施启动中;规格见 `docs/svc-deploy/`。
上述账户/恢复方案已有 U1/U4 与 recover/watchdog 基础源码。2026-10-12 的 dev 8788 续查形成账户 r2、恢复 r3 **候选草案**:帮助与能力发现、账户事务/审计/登录接线、SQLite 活动服务隔离及备份恢复增补。新增内容待人工评审;Documents 独立分支 `feature/svc-account-durability-plan` 仅本地保存,不推进本仓 gitlink;本入口原链接仍指固定版。候选交接与核查见[续审记录](docs/svc-deploy/2026-10-12-account-durability-plan-review.md)。
上述账户/恢复方案已有 U1/U4 与 recover/watchdog 基础源码。2026-10-12 的 dev 8788 续查形成账户 r2、恢复 r3 **候选草案**:帮助与能力发现、账户事务/审计/登录接线、SQLite 活动服务隔离及备份恢复增补。新增内容待人工评审;Documents 独立分支 `feature/svc-account-durability-plan` 仅本地保存,不推进本仓 gitlink;本入口原链接仍指固定版。候选交接与核查见[续审记录](docs/svc-deploy/2026-10-12-account-durability-plan-review.md)。U8 规格见 `docs/recovery/u8-sqlite-instance-guard.md`。
## 验证
+65
View File
@@ -0,0 +1,65 @@
# U8 SQLite 活动服务锁与耐久核验
状态:2026-10-12 用户批准恢复方案 r3 的 U8 后编写,随 `feature/u8-sqlite-instance-guard` 实施。
主记录:[服务重启与全量数据恢复方案](../../modules/documents/docs/proposals/2026-10-12-service-restart-data-recovery.md) §7 U8、KTD13、KTD14。
本文件只定本增量的可判定行为。账户 CLI 不迁移、不建库的合同由账户方案 U7 实现。
## 1. 锁对象
活动服务在打开 SQLite(迁移发生在打开内部)之前取得锁,并持有到存储关闭之后。PostgreSQL 档不使用这把锁,沿用迁移咨询锁与执行所有权。
| 规则 | 行为 |
|---|---|
| 路径 | 先 `Abs`,已存在的路径再 `EvalSymlinks`。数据库文件若是符号链接,锁放在解析后的真实文件旁边 |
| 锁文件 | `<db>.lock`。`O_RDWR\|O_CREAT\|O_CLOEXEC\|O_NOFOLLOW`,模式 0600。永不 unlink。数据目录不存在时,活动服务在取锁前创建该目录 |
| 互斥 | `flock(LOCK_EX\|LOCK_NB)`。占用返回「活动服务已在运行」,调用方不得打开库 |
| 替换检测 | 取锁后比较 fd 与路径的 device/inode,不一致则关闭并重试,仍不一致则拒绝 |
| 硬链接 | 解析后的数据库 `nlink>1` 直接拒绝,不能用 adopt 绕过 |
| 平台 | `unix` 实现。其他平台的 SQLite 档拒绝启动。PostgreSQL 档不受此限制 |
| 范围 | 只挡活动服务与 `migrate`。`user`、`apikey`、`identity` 不取这把锁 |
svc 的 `runtime/<name>/.start.lock` 只串行化脚本生命周期,挡不住直接执行二进制。本锁补的是这条缺口。
## 2. 身份记录
两处记录,各管一种替换:
1. 锁文件内容(持有 fd 时读写):`v1 db_dev db_ino instance_id lock_gen`。`lock_gen` 是 16 字节随机值的十六进制。数据库 inode 与记录不一致,或记录还在而数据库文件消失,视为数据库被替换,**打开之前**拒绝。
2. 表 `instance_guard`(迁移 v36,单行):记录锁文件的 device/inode、instance_id 与 `lock_gen`。守卫建立之后,锁文件 inode 与表不一致,或表里已有 `lock_gen` 而锁文件记录缺失/不一致,视为锁根丢失或锁文件被重建(含 inode 被回收后内容丢失),打开并迁移之后拒绝,且不改写表。
尚无锁文件记录、表中也无行:视为首次建立(含旧库升级),写入两边。这是升级路径,不是 adopt。
同时换掉数据库文件和锁文件、新库里也没有本表:无法与真正的首次建立区分。此时若设置了 `AGENT_SYSTEM_INSTANCE_ID`,仍由既有身份校验拒绝错库。该剩余风险写入任务卡,不在本增量用第二套锁文件解决。
`instance_id` 取 `AGENT_SYSTEM_INSTANCE_ID`。表和锁文件都已有非空值且与本次不一致时拒绝。本次为空则不比较,保留表内原值。
显式 `--adopt-instance-guard`(仅 `serve` 与 `migrate`)在已持锁的前提下重写两边记录。它只接受「数据库文件还在但 inode 已变」和「锁记录损坏」。记录还在而数据库文件消失时,adopt 也不打开、不新建空库。锁文件若是指向数据库的符号链接,或与数据库同一 inode,直接拒绝且不写入。硬链接拒绝不受该标志影响。
身份校验在写入守卫之前完成。`AGENT_SYSTEM_INSTANCE_ID` 已设置但与库内身份不符时,不留下本次的 instance id。
## 3. 耐久
SQLite DSN 增加 `synchronous(FULL)`。打开后在**同一应用连接**读回 `journal_mode=wal` 且 `synchronous=2`,否则关闭并拒绝。
PostgreSQL 从应用连接读 `pg_settings`:`fsync`、`full_page_writes`、`synchronous_commit` 任一为 `off`,或读不到,则拒绝。`synchronous_commit=local` 不是 `off`,不拒绝。
写失败必须把错误返回给调用方。测试用 `RLIMIT_FSIZE` 制造失败,并忽略 `SIGXFSZ`,以免进程被信号杀死而看起来像成功。
## 4. migrate 子命令
`agent-system migrate --data <dir>`:SQLite 先取活动服务锁,有服务占用则拒绝,再打开(打开即迁移)并走第 2 节的建立/拒绝规则。
`agent-system migrate --dsn <dsn>` 或环境变量 `AGENT_SYSTEM_DSN`:不取文件锁,迁移仍用 PostgreSQL 咨询锁排队。
## 5. 验收
| 编号 | 判据 |
|---|---|
| T8-1 | 两个进程启动同一 SQLite,后者在打开前失败;前者的 running 与 waiting_hitl 行保持原状态 |
| T8-2 | 符号链接与相对路径进入同一把锁;硬链接拒绝;数据库替换拒绝;锁文件删除后重建拒绝 |
| T8-3 | 持锁进程被 SIGKILL 后,同数据目录可以再次取锁并打开 |
| T8-4 | 本增量:服务持锁期间另一进程的短事务可提交;缺库的 OpenExisting 不建目录;migrate 与持锁服务互斥。账户 CLI 不迁移归 U7 |
| T8-5 | 换端口、工作目录或路径拼写仍落在同一锁文件上 |
| T8-6 | pragma 读自应用连接;超限写入返回错误 |
`tests/test_svc_storage_ownership.py` 依赖 svc 启动路径。本任务不改 `scripts/svc.sh`,该文件留到账户 U5;若届时 svc 入口已稳定,再补脚本级用例。
+26
View File
@@ -907,9 +907,35 @@ scripts/svc.sh prod deploy --env .env.prod # 等价
## 13. 故障排查
SQLite 档的活动服务锁在打开数据库之前取得,锁文件是解析后的
`agent-system.db.lock`(不删除)。同一数据文件只允许一个 `serve` 或 `migrate`。
`svc.sh` 的 `.start.lock` 只串行化脚本,挡不住直接启动二进制。账户短事务命令不拿这把锁。
显式迁移:
```bash
agent-system migrate --data <数据目录>
agent-system migrate --dsn "$AGENT_SYSTEM_DSN" # PostgreSQL,不取文件锁
```
数据库被替换,或守卫建立后锁文件被删掉重建,启动会拒绝。确认是同一次实例后:
```bash
agent-system serve --data <数据目录> --adopt-instance-guard
agent-system migrate --data <数据目录> --adopt-instance-guard
```
硬链接别名不能用 adopt 绕过。PostgreSQL 档在应用连接上读到 `fsync`、
`full_page_writes` 或 `synchronous_commit` 为 off 时拒绝启动。
| 症状 | 处置 |
|---|---|
| `端口被占用(非本服务)` | 按提示的占用者处理,或换端口(`--port` / `set --port --apply`) |
| `instance guard: sqlite service already running` | 已有进程占用同一 SQLite 文件:停掉那个服务,或换独立数据目录。`migrate` 同样会被拒绝 |
| `instance guard: sqlite database replaced` | 库文件 inode 与锁记录不符:确认不是拿错目录后,用 `--adopt-instance-guard` |
| `instance guard: recorded sqlite database is missing` | 锁记录指向的库不在了:恢复该文件,或在确认后 adopt |
| `instance guard: lock file was rebuilt` | 守卫已建立后锁文件被重建:确认后 `--adopt-instance-guard` |
| `instance guard: sqlite database has a hardlink alias` | 去掉硬链接别名后再启动;adopt 不能放行 |
| `durability:` | SQLite 未保持 WAL + synchronous FULL,或 PostgreSQL 的 fsync / full_page_writes / synchronous_commit 为 off |
| `进程启动后即退出` | `scripts/svc.sh <name> logs 200` / postgres 看 `runtime/<name>/pg.log`;history 里有 fail 记录 |
| `20s 健康检查未通过` | 进程活着但未响应:查日志与端口;agent 常见原因是 DSN 指向未启动的库 |
| `该产物不支持部分启动参数` | 提示而非错误:旧版本制品自动省略了不支持的 flag;需要该能力请部署新提交 |
+2
View File
@@ -8,6 +8,7 @@
"backend/app backend/genui",
"backend/app backend/hostapi",
"backend/app backend/ingest",
"backend/app backend/instanceguard",
"backend/app backend/intentgate",
"backend/app backend/intentsem",
"backend/app backend/pgbus",
@@ -31,6 +32,7 @@
"backend/ctxasm backend/vision",
"backend/hostapi backend/store",
"backend/ingest backend/vision",
"backend/instanceguard backend/store",
"backend/pgbus backend/bus",
"backend/pgbus backend/store",
"backend/plugins backend/settings",