From bec1176692353e1cfe04f83abfd02877621017b1 Mon Sep 17 00:00:00 2001 From: weichen Date: Mon, 12 Oct 2026 12:13:54 +0000 Subject: [PATCH] Add accounts help topic and pin durability plan docs Why: make the six instance-bound user commands discoverable (T5-1) and pin Documents r2/r3. Evidence: python3 -m unittest tests.test_svc_user_help; existing test_svc_user_binding still green. Impact: scripts/svc.sh help accounts|user; docs/svc.md topic list; documents gitlink to plan branch tip. Refs: Documents #87; feature/svc-account-durability-plan Co-authored-by: Cursor --- .task/feature-svc-account-durability-plan.md | 12 +-- docs/svc.md | 5 +- modules/documents | 2 +- scripts/svc.sh | 32 +++++- tests/test_svc_user_help.py | 101 +++++++++++++++++++ 5 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 tests/test_svc_user_help.py diff --git a/.task/feature-svc-account-durability-plan.md b/.task/feature-svc-account-durability-plan.md index 7837b865..fce43f6c 100644 --- a/.task/feature-svc-account-durability-plan.md +++ b/.task/feature-svc-account-durability-plan.md @@ -1,16 +1,16 @@ # feature/svc-account-durability-plan 任务卡 -任务主记录:本地文档续审(未创建 PR);状态:本地草案已保存,待人工评审;owner:weichen +任务主记录:Documents https://git-home.u1680832.nyat.app:30319/weichen/Agent-System-Documents/pulls/87 ;Dev PR 待建;状态:进行中(首切片:账户帮助可发现性);owner:weichen ## 基线与授权 - 权威仓:ssh://git@git-ssh.u1680832.nyat.app:31411/weichen/Agent-System-Dev.git;目标分支:main;R0(initial_base_sha)= 2723e452440fc11022c0f6d916cd34cc4cd17335 - 工作区:/home/weichen/projects/Agent-System-Dev-wt-feature-svc-account-durability-plan(worktree);分支:feature/svc-account-durability-plan -- 授权与交付终点:用户要求 svc.sh/账户和持久化调研及详细方案,明确 dev 8788 为故障对象,并指示「继续」。本轮覆盖只读续查、两份既有方案修订、本地文档任务准备及核查;业务代码、账户/DB 变更、迁移、故障注入、部署、推送/PR/合入均未授权。 -- 交付模式:manual(逐阶段人工评审);明确授权后可记为 authorized-continuous -- 自动推进授权:无;授权范围:无;授权来源:无;授权终点:无;停止条件:无(未授权) -- 当前阶段:既有方案续审草案;当前增量:r2 账户与 r3 恢复方案的新缺口、近期实施及验证合同;新增范围未评审 +- 授权与交付终点:用户 2026-10-12 选方案 3「实施到可合入」——本轮终点 = Documents r2/r3 PR 可合 + Dev 首切片(账户帮助可发现性 / T5-1)可合;不含 Documents-U7 后端事务、U8 故障注入、登录页 U5 浏览器接线。 +- 交付模式:authorized-continuous(本切片) +- 自动推进授权:有;授权范围:Documents 方案推送/PR、svc 帮助与 docs、帮助回归测试、Dev pin/PR;授权来源:用户确认清理计划选项 3;授权终点:上述 PR 可合入;停止条件:需改 Backend 账户事务或 live 故障注入则停并交下一任务。 +- 当前阶段:实施与自检;当前增量:I-docs(Documents #87)+ I-help(accounts 主题 / T5-1) - 流程依据:仓库根目录 policy/delivery.md(归档后仍按仓库根目录定位) ## 目标与非目标 @@ -83,7 +83,7 @@ ## 规划进展与下一步 - 本次推进/剩余:待核对(依据实际成果更新适用方案/规划) -- 首选下一任务:待核对(无适用规划、已完成或受阻时如实说明,不虚构任务) +- 首选下一任务:Documents 方案 U7(Backend 账户事务/审计/只读打开)与恢复方案 U8;再接登录页 U5 浏览器接线 - 推荐依据:待填写 - 前置条件/待确认:待核对 - 预期产物/验收条件:待定义 diff --git a/docs/svc.md b/docs/svc.md index df3158ae..de5ba9df 100644 --- a/docs/svc.md +++ b/docs/svc.md @@ -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` 内容同源,以实现为准。 --- @@ -541,7 +541,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. 命令参考:数据库 diff --git a/modules/documents b/modules/documents index 1048e7c6..9c31d3c3 160000 --- a/modules/documents +++ b/modules/documents @@ -1 +1 @@ -Subproject commit 1048e7c6afc2bb4efc541e68575c3fb2669ee332 +Subproject commit 9c31d3c3084914c35dd00c5a2f52690c40e8f5a8 diff --git a/scripts/svc.sh b/scripts/svc.sh index 7949b2ee..1fe47001 100755 --- a/scripts/svc.sh +++ b/scripts/svc.sh @@ -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 user [...] 账户 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 @@ -344,6 +347,7 @@ EOF postgres 实例与内部库 / 配置文件内容快照(敏感值默认掩码)/ 最近事件;带 name 时输出单服务深度报告 scripts/svc.sh history [name] [N] 事件历史(首参为纯数字时视为条数) + scripts/svc.sh user … 账户 CLI(绑定实例 manifest/OpenExisting;详见 help accounts) 变更登记(不动进程,除 --apply): scripts/svc.sh set --tag T... 三类服务通用,合并去重 @@ -375,6 +379,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 user … 转发到实例二进制): + scripts/svc.sh user create --owner [--role user|admin] [--password …] + scripts/svc.sh user list + scripts/svc.sh user reset-password --owner [--password …] + scripts/svc.sh user issue-login-key --owner [--purpose activate|recover] + scripts/svc.sh user revoke-login-key --id + scripts/svc.sh user bootstrap-admin --owner [--password …] + +凭据只在 stdout 展示一次;日志与 history 只记凭据 ID。后端原子开户/改密/审计 +补齐见 Documents 方案 U7(本帮助主题不替代该增量)。 +更完整说明:docs/svc.md、docs/svc-deploy/u1-instance-account-binding.md EOF return 0 ;; db) diff --git a/tests/test_svc_user_help.py b/tests/test_svc_user_help.py new file mode 100644 index 00000000..ee6d895f --- /dev/null +++ b/tests/test_svc_user_help.py @@ -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()