diff --git a/.task/feature-ecs-deploy-dev8788-and-kb.md b/.task/feature-ecs-deploy-dev8788-and-kb.md index e0ba906c..e2d6b08f 100644 --- a/.task/feature-ecs-deploy-dev8788-and-kb.md +++ b/.task/feature-ecs-deploy-dev8788-and-kb.md @@ -9,7 +9,7 @@ - 分支类型:standalone;root_target:main;immediate_target:main;accepting_requirements:false - 需求分流:verdict=NEW;selected_initiative=无;requirement_sha256=420311b8d5e0543749bb9743c05ae6757cc0244c7cfda204e6c28bc92eaf8b87;triage_receipt=/tmp/ecs-kb-deploy-triage.json;receipt_sha256=7a0bf8e0d2b9f1adcac5e58cbe70204473007a4dfd04a96f495dde3c0dd71ddb -- 授权与交付终点:owner 2026-10-22 指令“将当前的二进制服务部署到 imc-tx-ecs:先备份远端机器上的服务和数据库;将本机的知识库也部署在远端上;然后部署本机 8788 上的二进制服务;尽可能减少远端服务下线时间并保证服务可用;更换二进制前先告知 owner 确认”。交付终点:ECS 8788 运行与本机 dev 同版本二进制(sha256 00ead2cf…),ECS 承载本机 kbreg 知识库且检索可用,远端用户数据零丢失,切换带自动回滚并留存冷备与异地副本。仓库写入仅本任务卡。未授权:修改产品代码、改 Caddy 路由/域名、删除远端既有备份或旧二进制、签发登录 Key 或改动账号凭据、owner 确认前执行二进制切换。 +- 授权与交付终点:owner 2026-10-22 指令“将当前的二进制服务部署到 imc-tx-ecs:先备份远端机器上的服务和数据库;将本机的知识库也部署在远端上;然后部署本机 8788 上的二进制服务;尽可能减少远端服务下线时间并保证服务可用;更换二进制前先告知 owner 确认”,并于 2026-10-23 追加“先把配置弄好,superadmin 账号使用 imc-tx-ecs 上的 sundehui,密码 12345678,第一次登录需要重置密码(把已有用户变成超级管理员)”。交付终点:ECS 8788 运行与本机 dev 同版本二进制(sha256 00ead2cf…),ECS 承载本机 kbreg 知识库且检索可用,远端用户数据零丢失,切换带自动回滚并留存冷备与异地副本。仓库写入仅本任务卡。未授权:修改产品代码、改 Caddy 路由/域名、删除远端既有备份或旧二进制、改动 sundehui 以外账号的凭据、owner 确认前执行二进制切换。 - 交付模式:goal-first(目标、范围和交付终点一次确认;范围内连续推进;切换二进制属人工决策点,先备齐再请确认) - 实施执行:agent-per-unit(配置:AGENT-EXECUTION.md) - 合入执行:agent(已授权交付范围内,Review 通过、冲突解决且当前 HEAD 的 CI 通过后合入) @@ -50,9 +50,10 @@ | R1 | 先备份远端服务与数据库 | 符合(owner 指令;沿用 `ecs-dbsnap.py` 在线备份 API) | P0 | 远端 `backups/-warm-pre-ee8c2cd/` 含 data/bin-old/htdocs-old/config + SHA256SUMS 校验通过 + 本机异地副本逐文件 sha 通过 | 无 | 达成(见证据) | | R2 | 本机知识库部署到远端 | 符合(远端无容器运行时且镜像仓库不可达,按同主版本原生 PostgreSQL 16.15 + pgvector 供给) | P0 | ECS 127.0.0.1:5436/kbreg 可连;`schema_migrations` 0001—0005;关键表行数与本机一致;向量检索返回命中 | 无 | 达成(见证据) | | R3 | 部署本机 8788 二进制服务且最小停机 | 符合(owner 指令) | P0 | 运行中 exe sha256=00ead2cf…;本地与域名 HTTP 200;停机窗口 ≤ 演练实测 + 人工确认 | R1、R2 | 待切换(待 owner 确认) | -| R4 | 服务可用性保障(回滚) | 符合(切换脚本含健康门失败自动回滚) | P0 | 演练实例证明新版本可启动并迁移 v35→最新;回滚路径演练可用 | R3 | 达成(演练证据) | +| R4 | 服务可用性保障(回滚) | 符合(切换脚本含健康门失败自动回滚) | P0 | 演练实例证明新版本可启动并迁移 v35→最新;回滚路径演练可用 | R3 | 达成(演练证据:迁移 0.27s、健康 0.27s、日志无错误) | | R5 | 用户数据零丢失 | 符合 | P0 | 冷备 manifest ↔ 切换后逐行对账 `problems=[]`;settings 明文密钥与 `secret_keys` 逐把一致 | R1、R3 | 待切换 | -| R6 | 仓库侧交付(任务卡 + PR) | 符合 | P1 | Review 通过、CI 通过、PR 合入 main、任务卡闭合 | R1—R5 | 待收口 | +| R6 | 超级管理员 = 既有账号 sundehui(口令 12345678,首次登录强制改密) | 符合(owner 2026-10-23 追加指令) | P0 | 库中仅 sundehui 带超管标记且 `is_admin=1`、`must_change_password` 置位;服务以 `SUPER_ADMIN_OWNER=sundehui` 启动日志为 `already established`(无冲突);`POST /api/auth/login` 200 且 `isSuperAdmin=true`、`sessionScope=set_password_only` | R3 | 配置就绪,待切换后验证 | +| R7 | 仓库侧交付(任务卡 + PR) | 符合 | P1 | Review 通过、CI 通过、PR 合入 main、任务卡闭合 | R1—R6 | 待收口 | ## 方案与计划 @@ -129,20 +130,21 @@ - 结果(I1 备份,零停机):远端 `backups/20261022T134400Z-warm-pre-ee8c2cd/`(539 MB、629 文件,SQLite 在线备份 API 取两个库的一致快照 + 文件树 rsync + bin/htdocs/plugins/systemd 单元/Caddyfile/svc 登记 + 活库基线 MANIFEST-LIVE-PRE.json)远端 `sha256sum -c` 通过;本机异地副本 `~/backups/imc-tx-ecs/20261022T134400Z-warm-pre-ee8c2cd/` 逐文件校验通过(629 文件)。线上进程全程未动(pid 4080336 自 2026-10-18 14:20:26 运行至今)。 - 结果(I2 知识库):远端装 PostgreSQL 16.15(PGDG,jammy)+ pgvector 0.8.7,回环 `127.0.0.1:5436`;角色/库 `kbreg`;本机容器 `pg_dump -Fc`(16.15 客户端,同主版本)→ `pg_restore --no-owner --no-privileges --no-comments`(3m09s);`serve.env`(600,仅 KBREG_*)与单元 drop-in `20-serve-env.conf` 已就位(daemon-reload,未重启服务)。 - 结果(I3 演练,零停机):`stage/rdm-ee8c2cd-20261022T055052Z-637762/bin/agent-system` sha256 与本机运行中制品一致(`00ead2cf…`);复制 data(367 MB)后以 8789 启动新二进制 + KBREG 环境:**1.02 s 健康(HTTP 200)、schema v35→v51 自动迁移完成、users 18→19(多出的就是本次演练的超级管理员账号)、conversations/runs/messages 不变、日志无 error/panic/fatal**;冷拷 data 实测 1.14 s、只拷库 0.05 s。 -- 验证(逐项,证据均为 SELF 收据):远端热备 SHA 通过 PASS;本机异地副本 SHA 通过 PASS;知识库指纹(本机容器 vs ECS,UTC 下逐表 md5:regulations/editions/index_entries/revision_members/legal_nodes/retrieval_chunks)完全一致 PASS;知识库端到端(SSH 隧道 → 远端 kbreg → `kbreg migrate` + `kbreg search`)`schema is up to date` + 检索命中已发布修订 `rev_corpus_legacy_20261018` PASS(状态 degraded/vector_index_unavailable 与本机一致,因本机向量索引同样为空,非本次引入);演练迁移与健康 PASS;切换后验证(运行中 exe sha、本地/域名 200、schema 51、`compare` 逐行对账 `problems=[]`、明文密钥与 secret_keys 一致)待 I4 后执行 NOT_RUN。 +- 结果(I3b 超级管理员演练,零停机):因为产品只在启动时从 `SUPER_ADMIN_*` 建立超管,且**拒绝把已有账号直接指定为超管**(`configured owner belongs to an existing account without the super-admin marker`),采用与产品 bootstrap 同形的等价写入(`~/ecs-ops/seed-superadmin.py`:一个事务内更新 `users` 的 `is_admin/is_super_admin/password_hash(argon2id)/must_change_password/auth_version/credential_origin/initialization_state/state_version` + 按 v49 动作集写 `system_grants`(12 项)+ `account_events` + `audit_events`)。演练实测:阶段 A `--auth off` 迁移 v35→v51 用时 **0.27 s**;阶段 B 提升 sundehui(用户数仍为 18,未新增账号);阶段 C 以 `--auth on` + `SUPER_ADMIN_OWNER=sundehui` 启动,日志 `super admin: account "sundehui" already established`,健康 **0.27 s**;`POST /api/auth/login` 返回 `isSuperAdmin=true`、`sessionScope=set_password_only`(首次登录只能改密),`/api/me/profile` 403 `set-password session only`,日志无 error/panic/conflict。 +- 配置就绪(远端,零停机):`runtime/dev/super-admin.env`(600,`SUPER_ADMIN_OWNER=sundehui`);drop-in `20-serve-env.conf` 同时引用 `serve.env` 与 `super-admin.env`(已 `daemon-reload`,未重启服务);待用口令哈希文件 `~/ecs-stage/seed.hash`(600)已就位;切换脚本 `~/ecs-ops/cutover-ee8c2cd.sh` 已按 A/B/C 三阶段重写并通过语法检查。 +- 验证(逐项,证据均为 SELF 收据):远端热备 SHA 通过 PASS;本机异地副本 SHA 通过 PASS;知识库指纹(本机容器 vs ECS,UTC 下逐表 md5:regulations/editions/index_entries/revision_members/legal_nodes/retrieval_chunks)完全一致 PASS;知识库端到端(SSH 隧道 → 远端 kbreg → `kbreg migrate` + `kbreg search`)`schema is up to date` + 检索命中已发布修订 `rev_corpus_legacy_20261018` PASS(状态 degraded/vector_index_unavailable 与本机一致,因本机向量索引同样为空,非本次引入);演练迁移与健康 PASS;超管提升与受限登录 PASS;切换后验证(运行中 exe sha、本地/域名 200、schema 51、`compare` 逐行对账 `problems=[]`、明文密钥与 secret_keys 一致)待 I4 后执行 NOT_RUN。 - 影响与剩余项: - - **切换需要超级管理员凭据**:新二进制在 `--auth on` 下没有 `SUPER_ADMIN_OWNER`/`SUPER_ADMIN_PASSWORD` 会直接退出(远端现状无 super-admin.env)。切换须由 owner 给出这两个值(建议 owner=`weichen`,库内已有该账号且 is_admin=1),否则无法在不降级鉴权的前提下上线。 - - 知识库检索状态与本机一致为 degraded(本机 `retrieval_chunks.embedding` 全为空,向量索引无可用 embedding);恢复向量索引属后续独立增量,不在本次范围。 - - 回滚:切换脚本含自动回滚(旧二进制 + 冷备 data + 旧 htdocs + 旧 drop-in);回滚会丢失切换后新产生的数据,且需回退超级管理员标记,需 owner 决策。 - - 远端知识库使用原生 PostgreSQL 16.15(非容器,远端无容器运行时且不可达镜像仓库);与 container-storage-plan 的容器决策之间可用同主版本 dump/restore 迁移,不阻塞其 U2/U6。 + - 切换脚本含 A(迁移)→ B(超管提升)→ C(正式启动)三阶段,任一阶段健康门失败即自动回滚(旧二进制 + 冷备 data + 旧 htdocs + 旧 drop-in);回滚会丢失切换后新产生的数据,需 owner 决策。 + - `sundehui` 现为 `is_admin=1` + `is_super_admin=1`(产品要求超管必须同时是管理员);其口令哈希写入时 `auth_version+1`,旧会话失效。 + - 首次登录的受限会话(`set_password_only`,10 分钟)只能用于改密;改密后 `must_change_password` 置 0(由产品自己的 `POST /api/auth/password` 完成,无需人工介入)。 ## 规划进展与下一步 -- 本次推进/剩余:I1—I3 完成(零停机);剩余 I4 切换(待 owner 确认 + 超级管理员凭据)、I5 收口(对账、证据、Review、PR/CI、合入)。 +- 本次推进/剩余:I1 备份 / I2 知识库 / I3 演练 / I3b 超管配置 已完成(全程零停机);剩余 I4 切换(待 owner 确认执行)、I5 收口(对账、密钥校验、Review、PR/CI、合入)。 - 首选下一任务:本任务 I4—I5(不新建任务)。 -- 推荐依据:owner 2026-10-22 指令的切换确认与凭据;`policy/delivery.md#人工决策点与连续执行`。 -- 前置条件/待确认:(1) owner 确认现在执行二进制切换;(2) owner 提供 `SUPER_ADMIN_PASSWORD`(`SUPER_ADMIN_OWNER` 建议 `weichen`)。 -- 预期产物/验收条件:ECS 8788 运行 exe sha256 `00ead2cf…`、本地与域名 200、schema v51、`compare` `problems=[]`、知识库可用、停机窗口 ≤ 5 s。 +- 推荐依据:owner 2026-10-22 指令的切换确认;`policy/delivery.md#人工决策点与连续执行`。 +- 前置条件/待确认:owner 确认现在执行二进制切换(配置已全部就位,不再缺凭据)。 +- 预期产物/验收条件:ECS 8788 运行 exe sha256 `00ead2cf…`、本地与域名 200、schema v51、`compare` `problems=[]`、sundehui 登录为受限会话且能改密、知识库可用、停机窗口 ≤ 10 s。 - 重复任务核对:已核对 `.task/archive/feature-ecs-sync-local-dev.md`(2026-10-18 同机切换)与 feature/container-storage-plan(容器化形态,O1 生产迁移不在其范围);无重复在途任务。 - 启动授权:未授权(建议不自动启动新任务,仍须明确授权) @@ -382,3 +384,125 @@ "receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/37a1f94a8b71c31ff766ac284ea2599dac2df25c12e065b33d2a91c7602e9632.json" } ``` + + +```json +{ + "summary_schema": 1, + "source": "SELF", + "timestamp": "2026-10-22T23:08:23.850986+00:00", + "command": "ssh -o BatchMode=yes imc-tx-ecs 'bash ~/ecs-ops/rehearsal-superadmin.sh'", + "exit_code": 4, + "cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-ecs-deploy-dev8788-and-kb", + "before_head": "68ca3f5fb1b29ea18a70a30230ef9a2930241b1c", + "after_head": "68ca3f5fb1b29ea18a70a30230ef9a2930241b1c", + "branch": "feature/ecs-deploy-dev8788-and-kb", + "target_branch": "main", + "target_head": "6ced938a126981ee2a4fab671a2da94f49cb56c5", + "root_target": "main", + "immediate_target": "main", + "before_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "after_tracked_diff_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "output_tail": [ + "[rehearsal] copying data 2026-10-23T07:08:25+08:00", + "seed failed: no such column: is_super_admin" + ], + "output_sha256": "6df9fb628078ad393282ce681f1fb69e6034f689237fe97e6a692da490a3f3ab", + "receipt_sha256": "4000943bcb84808a35cd147886eea4d6fd81a2982a9d8db37e6239d874d7789c", + "receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/4000943bcb84808a35cd147886eea4d6fd81a2982a9d8db37e6239d874d7789c.json" +} +``` + + +```json +{ + "summary_schema": 1, + "source": "SELF", + "timestamp": "2026-10-22T23:08:52.895328+00:00", + "command": "ssh -o BatchMode=yes imc-tx-ecs 'bash ~/ecs-ops/rehearsal-superadmin.sh'", + "exit_code": 1, + "cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-ecs-deploy-dev8788-and-kb", + "before_head": "68ca3f5fb1b29ea18a70a30230ef9a2930241b1c", + "after_head": "68ca3f5fb1b29ea18a70a30230ef9a2930241b1c", + "branch": "feature/ecs-deploy-dev8788-and-kb", + "target_branch": "main", + "target_head": "6ced938a126981ee2a4fab671a2da94f49cb56c5", + "root_target": "main", + "immediate_target": "main", + "before_tracked_diff_sha256": "04b1dba8da2af5a53de394e5e62fdf4a46ab168aa73346127d42bd67546e5c20", + "after_tracked_diff_sha256": "04b1dba8da2af5a53de394e5e62fdf4a46ab168aa73346127d42bd67546e5c20", + "output_tail": [ + "[rehearsal] copying data 2026-10-23T07:08:54+08:00", + "[phase A] migrate-only start (auth off)", + "[phase A] healthy after .529100167s (pid=1593264)", + "2026/10/23 07:08:54 storage tier: auto (dialect=sqlite pool=0/0 idleSec=0 lifetimeSec=0 schema=v51)", + "2026/10/23 07:08:55 agent-system serving on http://127.0.0.1:8789 (data: /home/ubuntu/weichen/Agent-System-Dev/runtime/dev/rehearsal/data, plugins: /home/ubuntu/weichen/Agent-System-Dev/runtime/dev/plugins)", + "[phase A] schema_max = 51", + "[phase B] elevate existing account", + "Traceback (most recent call last):", + " File \"/home/ubuntu/ecs-ops/seed-superadmin.py\", line 116, in ", + " sys.exit(main())", + " File \"/home/ubuntu/ecs-ops/seed-superadmin.py\", line 85, in main", + " instance = db.execute(\"SELECT instance_id FROM agent_system_identity\").fetchone()[0] or \"\"", + "TypeError: 'NoneType' object is not subscriptable" + ], + "output_sha256": "bbeabf52756b45a9e3fc87670b7bdbbdcab5b5b35ab5682138d45462939306b5", + "receipt_sha256": "a88ff605df0338fb6cb529f9ab45900833c0120a528ee835b0b11628f9e66b50", + "receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/a88ff605df0338fb6cb529f9ab45900833c0120a528ee835b0b11628f9e66b50.json" +} +``` + + +```json +{ + "summary_schema": 1, + "source": "SELF", + "timestamp": "2026-10-22T23:09:02.539134+00:00", + "command": "ssh -o BatchMode=yes imc-tx-ecs 'bash ~/ecs-ops/rehearsal-superadmin.sh'", + "exit_code": 0, + "cwd": "/home/weichen/projects/Agent-System-Dev-wt-feature-ecs-deploy-dev8788-and-kb", + "before_head": "68ca3f5fb1b29ea18a70a30230ef9a2930241b1c", + "after_head": "68ca3f5fb1b29ea18a70a30230ef9a2930241b1c", + "branch": "feature/ecs-deploy-dev8788-and-kb", + "target_branch": "main", + "target_head": "6ced938a126981ee2a4fab671a2da94f49cb56c5", + "root_target": "main", + "immediate_target": "main", + "before_tracked_diff_sha256": "39b40acc28963d9413e554781ec8e65ed09774268e49cecb19496ac456e9a4d3", + "after_tracked_diff_sha256": "39b40acc28963d9413e554781ec8e65ed09774268e49cecb19496ac456e9a4d3", + "output_tail": [ + "[rehearsal] copying data 2026-10-23T07:09:03+08:00", + "[phase A] migrate-only start (auth off)", + "[phase A] healthy after .267909096s (pid=1593451)", + "2026/10/23 07:09:04 storage tier: auto (dialect=sqlite pool=0/0 idleSec=0 lifetimeSec=0 schema=v51)", + "2026/10/23 07:09:04 agent-system serving on http://127.0.0.1:8789 (data: /home/ubuntu/weichen/Agent-System-Dev/runtime/dev/rehearsal/data, plugins: /home/ubuntu/weichen/Agent-System-Dev/runtime/dev/plugins)", + "[phase A] schema_max = 51", + "[phase B] elevate existing account", + "seeded: {'owner': 'sundehui', 'is_admin': 1, 'is_super_admin': 1, 'must_change_password': , 'auth_version': 2, 'credential_origin': '', 'initialization_state': 'initialized', 'has_pw': 1} system_grants_allowed: 12", + "[phase C] start with auth on, super admin owner=sundehui", + "[phase C] healthy=1 after=.268576176s http=200", + "2026/10/23 07:09:06 super admin: account \"sundehui\" already established", + "2026/10/23 07:09:06 storage tier: auto (dialect=sqlite pool=0/0 idleSec=0 lifetimeSec=0 schema=v51)", + "2026/10/23 07:09:06 agent-system serving on http://127.0.0.1:8789 (data: /home/ubuntu/weichen/Agent-System-Dev/runtime/dev/rehearsal/data, plugins: /home/ubuntu/weichen/Agent-System-Dev/runtime/dev/plugins)", + "[check] login as sundehui", + "{\"auth\":true,\"isAdmin\":true,\"isSuperAdmin\":true,\"mustChangePassword\":,\"name\":\"\",\"owner\":\"sundehui\",\"passwordChangeMode\":\"\",\"passwordSet\":,\"sessionScope\":\"set_password_only\"}", + "", + "HTTP=200", + "[check] profile with session", + "forbidden: set-password session only", + "", + "HTTP=403", + "[check] super-admin gated endpoint", + "admin_users_http=403", + "[check] schema and account state", + "schema_max = 51", + "super admins = [('sundehui',)]", + "users total = 18", + "[check] log errors:", + "[rehearsal] done 2026-10-23T07:09:06+08:00" + ], + "output_sha256": "690a87c97b30c36d517a47c90a6d951c307a094feefbf525ac6e7124c2f5c421", + "receipt_sha256": "47666f79ea5cbd315d5de3f89da4f90e19956fba77e4f880cb3f9bfca7c9df95", + "receipt_path": "/home/weichen/.cache/agent-system-dev/evidence/47666f79ea5cbd315d5de3f89da4f90e19956fba77e4f880cb3f9bfca7c9df95.json" +} +```