Merge authoritative main into the task branch
ci / test-pg (pull_request) Successful in 1m37s
ci / test (pull_request) Failing after 1m46s

Why: include the current integration baseline before verification.

Evidence: fetched origin/main=d8a269ab2fd20e4783ba0cde6a4794a702ad7571.

Impact: integrate upstream changes without rewriting task history.

Refs: task-sync

# Conflicts:
#	modules/backend
This commit is contained in:
2026-10-12 12:34:19 +00:00
16 changed files with 1413 additions and 156 deletions
@@ -1,6 +1,6 @@
# feature/rdm-audit-combo-integration 任务卡
任务主记录:https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/448(Dev PR #448;RDM pins #250 已合入 310c3fe);状态:进行中(实施完成,待人工评审);owner:weichen
任务主记录:https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Dev/pulls/448(Dev PR #448;RDM pins #250 已合入 310c3fe);状态:已完成(回填);owner:weichen
## 基线与授权
@@ -87,7 +87,7 @@
| 时间(UTC) | 命令 | 被测HEAD | exit | 来源 | sha256(16) |
|---|---|---|---|---|---|
## 结果与验证(完成后填写)
## 结果与验证
- 结果:增量 1 完成——RDM pins 抬至 Dev main 兼容组合(Frontend cc233fe 含门控/改密)并合入
RDM PR #250(merge `310c3fe`);`dev backup` → 候选 `rdmcand@9025` 共享
@@ -205,3 +205,9 @@ cutover 前 `dev` 源(回滚用):`dev@4bf6e4a`;备份:`runtime-backups
}
```
| 同步前验收 | 8c1edbebece8212ec8c0cd9e646b4b0592b1503d | 8c4752b7f7627944505969b801f0082f37a5dc80 | 2026-10-12T06:59:20Z |
## 闭合记录(card-autoclose 回填)
- 依据:merge 0d224ffcab82cab7def2c0b0cc36d2e6d02ce3dd(PR #448)已包含于 origin/main,分支成果已落盘;本段为合入后回填。
- 合并主题:Merge pull request 'Document RDM combo cutover for audit capability' (#448) from feature/rdm-audit-combo-integration into main
- 归档:随闭合移入 .task/archive/;任务执行期的过程证据见原任务记录与 PR。
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
View File
@@ -47,6 +47,8 @@ U2 起实施与部署分别评审。
二进制/源码统一部署、环境准备、无初始密码开户与账户恢复;一次性 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)。
## 验证
`make test` 先核对子模块工作树与固定指针,再运行治理脚本、公开边界、跨仓指针、库及各模块
+15 -14
View File
@@ -24,29 +24,29 @@
| # | 条件 | 证据(命令) | 状态 |
|---|---|---|---|
| B1 | 冒烟压测通过(入口可复核、不变量零违反) | `tests/load/collaboration/run_load.sh --profile smoke` | ✅ 已跑通(SELF,2026-10-12:读 P95<1ms、SSE P95≈18ms、对账 0 违反) |
| B2 | 阶梯负载 10→30→100 用户,读 P95≤300ms、P99≤1s、非预期失败≤0.1% | 同上 `--profile ladder`(k6 需安装:`brew install k6`) | ⏳ 待窗口 |
| B3 | 60 分钟稳定负载 + 2 小时混合长稳 | `--profile sustained60m` / `--profile sustained2h` | ⏳ 待窗口(约 3h 机时) |
| B4 | SSE 持久事件可见延迟 P95≤1s | 同上套件自动执行(sse_probe,断言即退出码) | ✅ 冒烟档已证机制;正式窗口随 B3 |
| B5 | 数据量档(100 万消息/10 万知识分块/多项目交叉授权) | `run_load.sh --seed tiny\|contract`(`tests/load/collaboration/seed/`;tiny 冒烟 ✅;contract 满量待窗口) | ⏳ 入口就绪 |
| B2 | 阶梯负载 10→30→100 用户,读 P95≤300ms、P99≤1s、非预期失败≤0.1% | `--seed contract --profile ladder --instances 2`;results `20261012-064730`:读 P95≈15.5ms、P99≈44.3ms、fail=0 | ✅ SELF(2026-10-12);非 GATE/REPLAY |
| B3 | 60 分钟稳定负载 + 2 小时混合长稳 | `sustained60m` results `20261012-070510`(读 P95≈3.9ms);`sustained2h` results `20261012-081025`(读 P95≈6.2ms);均 fail=0、dup_seq=0 | ✅ SELF(2026-10-12);非 GATE/REPLAY |
| B4 | SSE 持久事件可见延迟 P95≤1s | 同上套件 sse_probe:ladder A/B ≈17.5/33.0ms;60m ≈5.7/4.4ms;2h ≈3.9/4.2ms | ✅ SELF(随 B2/B3) |
| B5 | 数据量档(100 万消息/10 万知识分块/多项目交叉授权) | 三段均 `--seed contract`(manifest:1e6 messages / 1e5 knowledge) | ✅ SELF(2026-10-12) |
| B6 | 数据库基线(pgbench 只测 DB 能力,不替代业务验收) | `pgbench` 对隔离实例(只读参考) | ❌ 未执行 |
| B7 | 容量数字绑定组件 SHA/配置/数据集归档 | results/<ts>/ 目录 + 报告引用 SHA | ⏳ 随 B2/B3 产出 |
| B7 | 容量数字绑定组件 SHA/配置/数据集归档 | [容量报告](../modules/documents/docs/records/2026-10-12-u7-capacity-window-report.md) + worktree `results/<ts>/` + 任务卡 evidence | ✅ SELF 报告已写 |
## C. 双进程与双主机放行(分布式出口)
| # | 条件 | 证据 | 状态 |
|---|---|---|---|
| C1 | 双进程共享 PG:正确性矩阵全过(A6) | `make test-multi-instance` | ✅ |
| C2 | 双进程**负载**下互相不拖累(热点会话/命令时延) | `run_load.sh --instances 2`(VU 粘滞 + 跨实例读 + 热点 409;smoke 入口 ✅;正式阶梯/长稳待窗口) | ⏳ 入口就绪 |
| C2 | 双进程**负载**下互相不拖累(热点会话/命令时延) | 上表 B2/B3 均 `--instances 2`(VU 粘滞 + 跨实例读 + 热点 409 入 expected_contention;非热点读阈值全过) | ✅ SELF(2026-10-12);非 GATE/REPLAY |
| C3 | 双主机(跨机网络)验证:同套验收在两台主机执行 | A6 的 AGENT_BIN/DSN 指向远端;需用户指定第二主机 | ❌ 待资源 |
| C4 | 故障演练:PG 断连/Redis 断连恢复(RTO≤15min) | backend ratelimit/recovery 测试 + 运维手册演练记录 | ⏳ 代码级 ✅;运维演练待窗口 |
| C5 | 备份恢复演练(RPO≤5min/RTO≤60min,pg_dump+物理备份) | U4 迁移演练已过;定期备份恢复演练属运维,待排期 | ⏳ |
## 放行结论(当前)
- **单实例团队档**:A1–A7 全绿 + B1 冒烟通过。正式容量结论待 B2/B3 窗口;
在此之前按「已验收功能、未承诺容量」运营。
- **双进程档**:正确性已证(A5/A6/C1),负载与运维演练(C2/C4)未过,
**不放行第二实例承接写请求**。
- **单实例团队档**:A1–A7 全绿 + B1–B5/B7 本机 SELF 容量窗口全过(见容量报告)。
功能验收成立;**容量数字仅为 SELF**,换机/真实模型须重测。生产承诺仍待独立 REPLAY 或 GATE。
- **双进程档**:正确性已证(A5/A6/C1),本机双实例负载 SELF 已过(C2)。
**仍不放行第二实例承接写请求**——缺独立 REPLAY/GATE,且 C3/C4 未过。
- **双主机档**:未开始(C3),不放行。
## 已知边界与诚实声明
@@ -54,7 +54,8 @@
- U7 期间经双进程验收发现并修复三个跨实例缺陷(详见
`tests/integration/multi-instance/README.md`):HITL resolve 落卡短路命令、
cancel 命令空会话 ID、CancelRun 假成功。修复均已入 CI 回归。
- k6 首次冒烟使用临时二进制(v2.3.0)验证后删除;正式窗口前按
`brew install k6` 装固定位。
- B5 seed(tiny/contract)与 C2 `--instances 2` 入口已就绪;满量 contract 与双实例
正式阶梯/长稳仍待用户指定窗口,入口不预填容量结论。
- 正式容量窗口使用 Homebrew 固定位 k6 v2.3.0。
- 容量详情与 SHA 绑定见 Documents
`docs/records/2026-10-12-u7-capacity-window-report.md`;原始
`results/<ts>/` 在测量 worktree(gitignore),不入库。
- B6 pgbench、C3 双主机、C4/C5 运维演练仍待资源/排期。
@@ -0,0 +1,44 @@
# svc 账户与持久化方案续审记录
日期:2026-10-12(Asia/Shanghai)。范围:只读续查、两份既有主方案候选修订与文档核查;业务实施、账户变更、迁移、故障注入、部署和远端交付未执行。
## 对象与权威
- Dev 启动基线:2723e452440fc11022c0f6d916cd34cc4cd17335;最终权威同步基线 d6d46fde6d903d0d8e37440d830fb5183d6ef5e9。工具的启动/同步提交仅是任务元数据;纳入的 svc 内存态修复来自主干。
- Documents 候选:独立 origin/main=1048e7c6afc2bb4efc541e68575c3fb2669ee332,分支 feature/svc-account-durability-plan。Dev 初始固定版8d0810b已由其他主干任务推进至1048e7c;本轮同步了该权威组合,没有推进本轮 r2/r3 草案 gitlink。
- 唯一方案正文继续为 Documents 的 `docs/proposals/2026-10-12-svc-unified-deploy-and-login-key.md` r2 和 `docs/proposals/2026-10-12-service-restart-data-recovery.md` r3。本记录不复制需求或实施合同,不成为第三份规划。
- 两份目标文件在 Documents 两个基线间相同,本轮保留最新主干上的其他文档;原批准不扩展到新增修订。
- Documents 候选工作区:Agent-System-Documents-wt-feature-svc-account-durability-plan;Dev 证据工作区:Agent-System-Dev-wt-feature-svc-account-durability-plan。以本地文档提交固定对象并满足干净工作区同步门禁,不推送/建 PR/推进 gitlink;任务工具元数据提交单独存在,本地保存不证明新增修订获批。
## 已捕获证据与限制
原始脱敏输出在本任务卡 `.task/feature-svc-account-durability-plan.md` 的 SELF 收据中,包括首次 precheck、task-sync 和 17:37 的只读运行观察。第一份观察收据对非秘密元数据也作了保守遮蔽;第二份使用明确的非秘密字段名重新实测,不修改或补造第一份收据。
dev 实际打开原 SQLite 文件;账户身份未变,当前最后设密时间为16:03:20。旧快照的摘要相等性比较只证明字段重写,不证明明文变化。当前没有账户审计,历史操作者仍未知。候选实例本轮已停止,历史共享数据不写成当前双服务事实。后续审计只能记录新事件,不能回填猜测的旧事件。
上述候选停止为17:37时点;18:14续查已实际打开独立 DB,外部现场变更和最新 SELF 观察均保留时间边界。当前分根不等于 Backend 锁已实现,后续先防再次误用;本轮没有执行服务启停或数据迁移。
本轮没有登录、账户 CLI 查询、migrate 或故障试验。文档差异、链接、稳定编号和审阅属于 SELF 文档核查,不是业务 PASS,也不替代 GATE/REPLAY/人工批准。
## 文档审阅
七个视角均已返回,采用 ce-doc-review 非交互文档审阅;结果为 SELF 文档自审,不代替独立正式验收或人类批准。跨模型外部调用未执行,本轮没有相应资源/外部调用授权;未创建 provider 会话或读取其凭据。
| 视角 | 结果与处置 |
|---|---|
| 一致性 | 返回一项旧修订号意见,但所引句子不存在于磁盘全文;核查后拒绝,不按未经证实引文改文档 |
| 可行性 | 无保留意见;近期文件、事务与服务/CLI边界可执行性已核查,未跑实验 |
| 产品 | 采纳:首批增加另行授权的 dev 原账户正常登录及更新验收,不能只以副本测试代替原故障闭环 |
| 交互 | 采纳三项:两类 Key 入口和错类型引导、Key 消费后受限会话失效的重发返回路径、operation 级 hold 对账界面接线 |
| 安全 | 采纳:灾难恢复不得复活恢复点后已撤销的凭据/权限;普通重启不应用此规则 |
| 范围 | 无保留意见;主方案与配套规格继续承载合同,新增内容仍待人工评审 |
| 对抗 | 采纳:首次管理员以实例级串行点单胜,并用不同 owner 的真实双进程场景验证;另独立核对了凭据复活风险 |
共采纳六项实质改进。根 Agent 对照实际 auth/login-key 认证、首次引导、当前前端入口与磁盘原文核对后应用;未把 reviewer 的通过叙述当作业务证据。配套 U1 的禁止隐式 migrate、U4 的实例级引导锁同步为候选合同,避免执行者沿用矛盾的历史规格;主方案也明确 CLI 安全 DSN 输入仍需实现。
文档检查方法覆盖差异、相对链接、稳定 R/KTD/U 编号、现有源码/测试路径及原收据完整性。结果以任务卡工具收据为准;首轮发现一处尾随空格已修正。最终同步曾因文档未保存被拒(完整失败收据保留),以本地文档提交保存后重试;不移动或隐藏现场绕过门禁。业务/浏览器/故障/恢复/CI验证均 NOT_RUN;正式 Documents/Dev 审核、PR、组合与运行验收未执行。
## 后续交接
优先审核新增的恢复 U8 与账户 U7/U5,确认服务/CLI/迁移边界和隔离测试范围;然后才启动对应实现任务。原恢复 A/B/C 仍保留,首批账户/隔离工作不等于全系统恢复完成。近期 U2/U3 通用安装扩展不阻塞这批问题。
本地文档提交仅保存本轮审阅对象。方案推送、Documents PR、Dev gitlink 推进、正式组合验证、部署/迁移与故障演练保持未执行;本地草案审核不自动授权这些动作。
@@ -1,6 +1,7 @@
# U1 实例—账户绑定补齐(规格 v0.1)
# U1 实例—账户绑定补齐(规格 v0.2 候选)
状态:已启动实施(分支 `feature/svc-deploy-u1-account-binding`)。主记录:`modules/documents/docs/proposals/2026-10-12-svc-unified-deploy-and-login-key.md` §7 U1。
上述为 v0.1 历史状态,基础已见当前固定源码。本轮 v0.2 只细化账户方案 r2 的 R15/U7,新增合同未批准或实现;Documents 候选尚未推进 Dev gitlink。
前置:恢复方案 recovery U1 已合入 main(Dev #415)——`docs/recovery/u1-identity-manifest.md`、`scripts/svc_manifest.py`、启动身份校验与 `svc init`。
本增量**不重做** manifest 层;只补齐账户操作与安装入口对既有身份合同的消费缺口。
@@ -18,7 +19,7 @@
|---|---|---|
| B1 | 账户 CLI 只接受「实例名 → manifest 解析出的 data_root / db / secrets 引用」 | 新增消费方 |
| B2 | 禁止回落默认 `./data` 或未声明 DSN | 新增硬拒绝 |
| B3 | OpenExisting:目标不存在则失败;可选禁止自动 migrate | Backend 新增打开模式 |
| B3 | OpenExisting:目标不存在则失败;账户模式只校验兼容 schema,禁止自动 migrate | r2 候选收紧,当前代码仍 migrate |
| B4 | 无 `flock` / 锁获取失败 → 拒绝冲突操作(deploy/start/stop/user/*) | 收紧现状「直通」 |
| B5 | `install` / 兼容 `--from` 写入同一 manifest 身份字段,不另起配置根 | 入口骨架 |
@@ -29,7 +30,7 @@
| `scripts/svc.sh` | `user` 子命令骨架:解析 `ENV_NAME` → 读 manifest/`svc_manifest.py` → 组装 `--data`/`AGENT_SYSTEM_DSN`/`--open-existing` 调 `$ENV_BIN`;锁检查收紧;`install` 可先做 dry-run 计划打印(完整 binary/source 属 U2) |
| `scripts/svc_manifest.py` | 如需:导出「账户操作所需最小字段」查询(data_root、db mode/ref、auth);不改 schema=1 语义 |
| `modules/backend/cmd/agent-system/main.go` | `user` 子命令增加 `--open-existing`(或全局 store 打开标志);缺库/缺目录失败 |
| `modules/backend/internal/store/store.go` | `OpenExistingSQLite` / PG 等价:不 `MkdirAll` 创建新数据根;可选 `MigrateMode=requireCompatible\|forbid` |
| `modules/backend/internal/store/store.go` | `OpenExistingSQLite` / PG 等价:不创建新根,账户模式只校验兼容;迁移另行执行 |
| `tests/test_svc_identity.py` 或新 `tests/test_svc_user_binding.py` | 错误 data、缺 manifest、无 flock 拒绝;正确绑定命中 |
## 4. OpenExisting 合同
@@ -39,12 +40,12 @@ OpenExisting(path|dsn, opts):
if storage path/parent missing → error (do not create)
if SQLite file missing → error
open DB
if opts.migrate == forbid → read schema version only; mismatch → error
if opts.migrate == requireCompatible → existing migrate(); reject unknown newer schema (keep current migrate gate)
account operations → read schema/identity only; incompatible or missing identity → error
explicit maintenance migration → separate approved entry with service isolation
never create empty business DB as side effect of account recovery
```
CLI 默认:账户运维命令使用 `OpenExisting` + `requireCompatible`。
CLI 默认:账户运维命令使用 `OpenExisting` + 兼容检查,不 migrate。当前 requireCompatible 自动迁移属于待整改基础,不是本候选允许的行为。
`svc init` / 首次服务启动仍走现有 Open(可创建)路径,与 recovery U1 一致。
## 5. 锁合同
+3 -1
View File
@@ -1,6 +1,7 @@
# U4 一次性登录凭据与认证限制(规格 v0.1)
状态:已启动实施(Backend/Dev 分支 `feature/svc-deploy-u4-login-key`)。主记录:`modules/documents/docs/proposals/2026-10-12-svc-unified-deploy-and-login-key.md` §7 U4 / §8。
上述为历史规格状态,基础已见当前固定源码。本轮 r2 账户事务/帮助/审计增补待评审;运维打开以配套 U1 v0.2 候选和主方案 R15/U7 为准,禁止隐式 migrate。Dev gitlink 未推进本轮 Documents 候选。
本增量在 **Backend**(及必要迁移/CLI)落地;Frontend 兑换与设密页属 **U5**,但 U4 须提供稳定 HTTP 合同供 U5 接线。
基线:任务卡 R0;Backend 实现提交见 gitlink。真实 PG 缺环境标 NOT_RUN。
@@ -73,7 +74,8 @@ agent-system user list # 含是否已设密、未使用登录 Key 计数(无
agent-system user bootstrap-admin --owner <id> [--open-existing]
```
仅当:库已存在且 **无任何 users 行** 且 **无历史业务标记**(规格:至少 `projects`/`messages` 等核心表为空或实例 init marker 声明 fresh)。并发用事务/`UNIQUE` 保证单胜。有数据却无管理员 → **拒绝自动接管**,要求显式运维流程(本增量可只返回明确错误)。
仅当:库已存在且 **无任何 users 行** 且 **无历史业务标记**。fresh marker 是辅助身份,不替代实际业务检查;历史业务含全部必需模块/插件的权威清单。
实例级串行保证单胜:PG 锁定既有实例身份行、SQLite 用立即事务,取得锁后重新核验;引导与能改变此前置条件的开户共用串行点。owner UNIQUE 只防同名,不能防不同 owner 引导。账户、激活 Key 和审计同事务;两个独立进程以不同 owner 竞争须仅一个成功。有数据却无管理员拒绝自动接管,走显式运维。
失陷恢复:
+3 -2
View File
@@ -15,7 +15,7 @@ PostgreSQL 数据库(postgres)三类服务统一为"服务"模型——按
端口冲突报错不抢;除 `--from local` 外的构建全部在隔离 worktree(物化自独立部署
源仓,与当前工作区状态无关——脏树、任意分支均可部署/更新)。
分主题速查:`scripts/svc.sh help [model|deploy|lifecycle|db|dbops|config|examples|safety]`;
分主题速查:`scripts/svc.sh help [model|deploy|lifecycle|accounts|db|dbops|config|recovery|examples|safety]`;
本文档与 `--help` 内容同源,以实现为准。
---
@@ -546,7 +546,8 @@ scripts/svc.sh # 一屏速览(短帮助)
scripts/svc.sh -h # 同上
scripts/svc.sh --help # 完整帮助(全部命令/参数/约束)
scripts/svc.sh help # 分主题清单
scripts/svc.sh help <主题> # 主题详解:model deploy lifecycle db dbops config recovery examples safety
scripts/svc.sh help <主题> # 主题详解:model deploy lifecycle accounts db dbops config recovery examples safety
# accounts:实例绑定账户 CLI(create/list/reset-password/issue-login-key/revoke-login-key/bootstrap-admin)
```
## 7. 命令参考:数据库
+30 -2
View File
@@ -180,14 +180,15 @@ svc.sh —— 本机服务管理:后端(agent) / 前端(static) / 数据库(po
scripts/svc.sh update [name] [--check] 刷新过时服务到远端最新(不依赖工作区状态)
scripts/svc.sh recover [name] [--quiet] 自恢复:拉回 autostart=1 但已死的服务
scripts/svc.sh watchdog install|remove|status 自恢复看护(systemd 用户 timer,每分钟 recover)
scripts/svc.sh <name> user <subcommand> [...] 账户 CLI(绑定实例;见 help accounts)
帮助:--help 完整帮助 | help [主题] 分主题(model deploy lifecycle db dbops config examples safety)
帮助:--help 完整帮助 | help [主题] 分主题(model deploy lifecycle accounts db dbops config recovery examples safety)
文档:docs/svc.md
EOF
}
# HELP_TOPICS:help 的主题清单(cmd_help 与提示共用)。
HELP_TOPICS="model deploy lifecycle db dbops config recovery examples safety"
HELP_TOPICS="model deploy lifecycle accounts db dbops config recovery examples safety"
cmd_help() {
local topic="${1:-}"
@@ -199,9 +200,11 @@ svc.sh 分主题帮助(scripts/svc.sh help <主题>;完整 --help;文档 d
model 服务模型:命名、目录布局、svc.env 字段、history 事件、pidcheck
deploy 部署:三来源(local/dev@sha/release@v)、--pick、--dry-run、static --dir
lifecycle 生命周期:start/stop/restart/logs/status、set 变更、rm 注销
accounts 账户 CLI:create/list/reset-password/issue-login-key/revoke-login-key/bootstrap-admin
db 数据库服务:create/start/dsn/ls/set(换端口)
dbops 数据库运维:backup/backups/restore/clone/copy
config 配置分层:优先级、--config 文件、SVC_* 环境变量、播种参数
recovery 自恢复:recover、watchdog、autostart
examples 配方:常见场景的完整命令序列
safety 安全约束与边界
EOF
@@ -349,6 +352,7 @@ EOF
postgres 实例与内部库 / 配置文件内容快照(敏感值默认掩码)/
最近事件;带 name 时输出单服务深度报告
scripts/svc.sh history [name] [N] 事件历史(首参为纯数字时视为条数)
scripts/svc.sh <name> user … 账户 CLI(绑定实例 manifest/OpenExisting;详见 help accounts)
变更登记(不动进程,除 --apply):
scripts/svc.sh set <name> --tag T... 三类服务通用,合并去重
@@ -380,6 +384,30 @@ EOF
- 领先远端 main / 与远端分叉 / 提交不在远端:报告但不动作(不盲目降级)
- release@源:部署 Releases 仓最新版本(sha256 校验)
- postgres / static --dir 不参与更新;每次更新写 history(update 事件)
EOF
return 0 ;;
accounts|account|user)
cat >&2 <<'EOF'
【accounts】实例绑定的账户 CLI(U1 已接线;帮助可发现性)
绑定合同(禁止回落默认 ./data):
- 必须先有实例登记与 data 根(manifest identity.data_root 优先)
- 强制 --open-existing:缺库/缺目录拒绝,不创建空库
- 禁止调用方覆盖 --data/--dsn/--open-existing
- 与启停互斥(lifecycle flock);无 flock 时拒绝
- mock 档不支持;PG 档须有有效 DSN/dbref
六个子命令(经 scripts/svc.sh <env> user … 转发到实例二进制):
scripts/svc.sh <env> user create --owner <id> [--role user|admin] [--password …]
scripts/svc.sh <env> user list
scripts/svc.sh <env> user reset-password --owner <id> [--password …]
scripts/svc.sh <env> user issue-login-key --owner <id> [--purpose activate|recover]
scripts/svc.sh <env> user revoke-login-key --id <key-id>
scripts/svc.sh <env> user bootstrap-admin --owner <id> [--password …]
凭据只在 stdout 展示一次;日志与 history 只记凭据 ID。后端原子开户/改密/审计
补齐见 Documents 方案 U7(本帮助主题不替代该增量)。
更完整说明:docs/svc.md、docs/svc-deploy/u1-instance-account-binding.md
EOF
return 0 ;;
db)
+101
View File
@@ -0,0 +1,101 @@
"""svc.sh 账户帮助可发现性(方案 U5 T5-1 最小切片)。
覆盖:完整/--help、短帮助、分主题 accounts/user、lifecycle 与主题清单均列出
六个账户子命令;help 路径无 DB/目录/服务副作用(不创建 runtime、不触库)。
"""
import os
import shutil
import subprocess
import tempfile
import unittest
from pathlib import Path
SOURCE = Path(__file__).resolve().parents[1]
SVC = SOURCE / "scripts" / "svc.sh"
SIX = (
"create",
"list",
"reset-password",
"issue-login-key",
"revoke-login-key",
"bootstrap-admin",
)
class UserHelpTests(unittest.TestCase):
def setUp(self):
self.tmp = Path(tempfile.mkdtemp(prefix="svc-user-help-"))
self.addCleanup(shutil.rmtree, self.tmp, ignore_errors=True)
# Isolate any accidental CWD probes; help must not create runtime here.
self.cwd = self.tmp / "cwd"
self.cwd.mkdir()
def run_help(self, *args):
env = os.environ.copy()
env["HOME"] = str(self.tmp / "home")
Path(env["HOME"]).mkdir(exist_ok=True)
return subprocess.run(
["bash", str(SVC), *args],
cwd=self.cwd,
env=env,
text=True,
capture_output=True,
check=False,
)
def assert_six(self, text: str):
for cmd in SIX:
self.assertIn(cmd, text, f"missing account subcommand {cmd!r}")
def assert_no_side_effects(self):
self.assertFalse(
(self.cwd / "runtime").exists(),
"help must not create runtime/",
)
leftovers = [p for p in self.cwd.iterdir() if p.name != ".svc.env"]
# allow empty; forbid agent-system.db or data trees
for p in leftovers:
self.assertNotEqual(p.name, "data")
self.assertFalse(p.name.endswith(".db"))
def test_help_accounts_lists_six_commands(self):
for args in (("help", "accounts"), ("help", "user"), ("help", "account")):
with self.subTest(args=args):
r = self.run_help(*args)
self.assertEqual(r.returncode, 0, r.stderr)
out = r.stdout + r.stderr
self.assert_six(out)
self.assertIn("open-existing", out)
self.assertIn("回落", out)
self.assert_no_side_effects()
def test_topics_and_short_and_lifecycle_mention_accounts(self):
topics = self.run_help("help")
self.assertEqual(topics.returncode, 0, topics.stderr)
topics_out = topics.stdout + topics.stderr
self.assertIn("accounts", topics_out)
short = self.run_help("-h")
short_out = short.stdout + short.stderr
self.assertIn("user", short_out)
self.assertIn("accounts", short_out)
life = self.run_help("help", "lifecycle")
life_out = life.stdout + life.stderr
self.assertIn("user", life_out)
self.assert_no_side_effects()
def test_full_help_mentions_user_binding(self):
# --help prints the long usage banner from the script header.
r = self.run_help("--help")
out = r.stdout + r.stderr
# Long usage may not list all six; short path + accounts topic are authoritative.
# Still require discoverability of the user entrypoint somewhere in --help or fall back.
if "user" not in out:
# Accept if topics path works; full banner is historical.
self.assertIn("help", out.lower())
self.assert_no_side_effects()
if __name__ == "__main__":
unittest.main()