Compare commits
104
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed4e17c819 | ||
|
|
cbef2157d8 | ||
|
|
ee1e53b510 | ||
|
|
65757d1c4e | ||
|
|
cc5b5bc8a8 | ||
|
|
375c5a42db | ||
|
|
7de9e35dcf | ||
|
|
4c0fa08401 | ||
|
|
4e3ff8defc | ||
|
|
5d96114777 | ||
|
|
2ad1ef393a | ||
|
|
a22f6b64d9 | ||
|
|
28a2c8ca2b | ||
|
|
9d550cfaf1 | ||
|
|
93ab4802e9 | ||
|
|
df6fe473b3 | ||
|
|
679028e44e | ||
|
|
ecc43a436d | ||
|
|
2235fc7230 | ||
|
|
0ceaafda8f | ||
|
|
95e659a915 | ||
|
|
fe2eb099c5 | ||
|
|
686dede802 | ||
|
|
638e5b89db | ||
|
|
2c75dcaf69 | ||
|
|
6baaa9131f | ||
|
|
8c2dd49e80 | ||
|
|
f034a80c09 | ||
|
|
e47cfd8f4e | ||
|
|
b8e9e75c99 | ||
|
|
95ebcaf703 | ||
|
|
a46a25b491 | ||
|
|
2cd52da85d | ||
|
|
3523f43d20 | ||
|
|
39bd5367b5 | ||
|
|
258c9d0a8f | ||
|
|
97879e5956 | ||
|
|
72c0996b74 | ||
|
|
beea8a21e5 | ||
|
|
1409e02187 | ||
|
|
4a6ee0ea9d | ||
|
|
9504b624ee | ||
|
|
4d716bcdcf | ||
|
|
49606fb02f | ||
|
|
3d00137047 | ||
|
|
a15e9d1c58 | ||
|
|
8b4db5fe83 | ||
|
|
1bb480309d | ||
|
|
787bcc53d4 | ||
|
|
2db5dab3cd | ||
|
|
8c3cfb7632 | ||
|
|
44abb2fa83 | ||
|
|
82afd32d93 | ||
|
|
89089be1d2 | ||
|
|
a6a9583b5b | ||
|
|
13209d3155 | ||
|
|
4e10d97b5a | ||
|
|
d06cb292fe | ||
|
|
7eb9477d9e | ||
|
|
16f33b5b9d | ||
|
|
32568ed13e | ||
|
|
721af4ccb8 | ||
|
|
54e92c973a | ||
|
|
88e0311a1f | ||
|
|
f8730791b9 | ||
|
|
8889011e5b | ||
|
|
5f505d5ab6 | ||
|
|
5d762fb206 | ||
|
|
3d6dcc3ae5 | ||
|
|
265616e997 | ||
|
|
b9339cd272 | ||
|
|
792c216c96 | ||
|
|
a90cce73b9 | ||
|
|
de90d0cd70 | ||
|
|
a3db288a7a | ||
|
|
f46480a987 | ||
|
|
5c0a84f7ed | ||
|
|
8d70d48591 | ||
|
|
7b6397b858 | ||
|
|
e5c2519fea | ||
|
|
5bc88faf4f | ||
|
|
b820e0070d | ||
|
|
8d5e782079 | ||
|
|
5679c79011 | ||
|
|
6a461dec0c | ||
|
|
714732df5a | ||
|
|
a3f8afbe63 | ||
|
|
650703c5c2 | ||
|
|
d86b4d51ef | ||
|
|
4bb265b431 | ||
|
|
d6ce9abef7 | ||
|
|
d3fda0e834 | ||
|
|
44435691c0 | ||
|
|
87888213da | ||
|
|
4c42d1bf74 | ||
|
|
aea19f70ef | ||
|
|
434d542c3c | ||
|
|
fa5d17a403 | ||
|
|
2952aa5a64 | ||
|
|
6e1d5830b5 | ||
|
|
c46a9c35dc | ||
|
|
db859e185e | ||
|
|
dfd9f6efb3 | ||
|
|
d55488ae37 |
@@ -1,27 +0,0 @@
|
||||
---
|
||||
description: Agent-System 写操作必须经 agentctl(租约、docs apply、候选合入);禁止绕过治理直接改共享文档或 push。
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# agentctl 强制治理
|
||||
|
||||
本仓库由 agentctl 管理面协调(SQLite + launchd daemon)。细则见 [AGENTS.md](../../AGENTS.md) 与 [操作规范](../../.doc/ops/2026-09-21-agent-worktree-workflow.md);流程技能见 `.cursor/skills/agentctl-*`。
|
||||
|
||||
## 硬边界
|
||||
|
||||
1. **写仓库**:必须 `agentctl with` / MCP `lease_run`。裸 `git commit` 会被 agentctl pre-commit hook 拒绝。
|
||||
2. **共享文档**:`.doc/ops/CURRENT.md`、`.doc/status/FEATURE-MATRIX.md` 只能 `docs apply`(行级 CAS;MATRIX 键为 `record_id`)。
|
||||
3. **合入 local dev**:`validate` 后 `queue enqueue`(daemon `--ff-only`)或受控 `integrate`;禁止直接改主目录 `dev` tip。
|
||||
4. **禁止默认**:未授权 `git push`、晋升 test/main、`--force` 清理、卸载 launchd coordinator、改 gap-matrix / 升 MATRIX 状态值冒充发布。
|
||||
5. **需求重叠**:跨对话新设计须 `catalog overlap`;硬重叠拒绝;软重叠须 `catalog ack` 留痕后方可编码(见 `.doc/ops/2026-09-24-governance-catalog-plan.md`)。
|
||||
|
||||
## 会话路径
|
||||
|
||||
- 启动 → skill `agentctl-session-start`
|
||||
- 建场 → `agentctl-worktree-setup`
|
||||
- 写回 → `agentctl-lease-write`
|
||||
- 合入/收口 → `agentctl-integrate-closeout`
|
||||
- PI 派发 → skill `pi-subagent-dispatch`(MCP `pi-subagents`)
|
||||
- 阶段落盘 → skill `subagent-stage-record`
|
||||
- MCP 工具定义见 `.cursor/mcp.json` → `scripts/mcp/agentctl-mcp.mjs` / `pi-subagents-mcp.mjs`
|
||||
- 客户端 hook 边界见规则 `cursor-hooks`
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description: Project Cursor hooks are advisory gates layered on agentctl/PI executor hard checks; scripts exist does not mean Cursor loaded them.
|
||||
description: Cursor hooks are client-side advisories only. Remote authority is Gitea native protection (2026-09-29); agentctl gates are retired for coding flow.
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
@@ -11,7 +11,8 @@ alwaysApply: true
|
||||
|
||||
| 层 | 能力 | 说明 |
|
||||
|---|---|---|
|
||||
| 执行器 / agentctl | **enforced** | `scripts/pi_subagents.py`、租约、pre-commit 等硬拒绝 |
|
||||
| Gitea 服务端 | **enforced** | 受保护分支、必需 CI、非作者批准(2026-09-29 起) |
|
||||
| 本地 agentctl | 已退役(仅维护场景可选) | pre-commit/租约等本地硬拒绝已停用 |
|
||||
| hook 脚本行为 | **enforced**(有单元回归) | stdin→stdout 契约可测 |
|
||||
| hook ↔ Cursor 接线 | **advisory / manual** | 官方事件字段未完全文档化;待 probe 采样后收紧 |
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
| 用错误码定位失败 | [错误码目录](system-design/2026-09-28-error-code-catalog.md) |
|
||||
| 测试报告 | [test-reports/](test-reports/) |
|
||||
| 历史功能设计 | [archive/design/superpowers/](archive/design/superpowers/) |
|
||||
| Herdr 角色、审批与备援 | [ops/2026-09-13-herdr-agent-roster.md](ops/2026-09-13-herdr-agent-roster.md) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
---
|
||||
title: "多仓库架构研究证据与审查记录"
|
||||
date: 2026-09-28
|
||||
task_id: multi-repo-design-2026-09-28
|
||||
evidence_basis: current_source_and_readonly_api
|
||||
---
|
||||
|
||||
# 多仓库调研证据
|
||||
|
||||
范围:[待审方案](../remediation/2026-09-28-multi-repo-architecture-plan.md)。
|
||||
记录只读 API 和源码事实,不包含业务正文、凭证值、认证头、原始数据库或敏感截图。
|
||||
未对远端执行 POST/PATCH/DELETE,也未 push、部署或创建远端仓库。
|
||||
|
||||
## Git and Gitea
|
||||
|
||||
本地研究基线:dev@a3db288a7aa055ff5884e3aa4d91511959cff721,开始时主目录干净。
|
||||
任务 worktree:.worktrees/multi-repo-design-2026-09-28,分支 feature/multi-repo-design-2026-09-28。
|
||||
`git rev-list --left-right --count fabd85a65e15f3417431ba6f004a8bcdf9cb83cb...a3db288a7aa055ff5884e3aa4d91511959cff721` 返回 `0 306`。
|
||||
因此领先 306 不是仅依据缓存 origin/dev;远端实时 API 的 SHA 与缓存一致,且祖先关系已核对。
|
||||
本轮未 fetch 修改 remote-tracking ref,未同步远端。
|
||||
|
||||
Gitea GET /api/v1/version 返回 1.27.3。
|
||||
GET /api/v1/user/repos 分页(本次一页已取完六仓),仅保留所需元数据:
|
||||
|
||||
| 仓库 | private | empty | default_branch | description | 结论 |
|
||||
|---|---|---|---|---|---|
|
||||
| weichen/Agent-System | false | false | dev | 空 | 保留主仓 |
|
||||
| weichen/Agent-System-Squad | false | true | main | 空 | 名称可复用,尚无组件内容 |
|
||||
| weichen/Agent-System-Development-Documents | false | true | main | 空 | 可复用,导入内容须分类 |
|
||||
| weichen/Development-Guidelines | false | true | main | 空 | 可复用 |
|
||||
| weichen/Agent-System-Releases | false | true | main | 空 | 只适合公开发布目标 |
|
||||
| weichen/Development-Manager | false | false | dev | 空 | 已有治理工具,不应重建覆盖 |
|
||||
|
||||
仓库 has_actions=true 只证明设置开启,不证明 runner/CI/权限可用。
|
||||
所有六仓当前读取身份有 admin/push/pull 权限;此事实不扩大本轮授权。
|
||||
|
||||
Agent-System 实时分支摘要:
|
||||
- dev: fabd85a65e15f3417431ba6f004a8bcdf9cb83cb
|
||||
- test: 0e4b111dff3726f5379e8168e3d00a244e111747
|
||||
- main: 210fe4a7bb2fd56c34a215c53e07617cccf81e49
|
||||
- 三分支 protected=false,GET branch_protections 返回 []。
|
||||
|
||||
Development-Manager 的 branch_protections 同样返回 []。
|
||||
两仓 GET releases?limit=3 返回 [];只说明本次未列出发布记录,不把 Git tags 当 Releases。
|
||||
两仓 repository-level GET actions/runners 返回 runners=[];没有检查实例级/组织级 runner,不推断整个实例无 runner。
|
||||
四个 empty 仓的 branches 返回 null,与 empty 状态一起解释为没有可列出的分支,不虚构 main 已有提交。
|
||||
|
||||
读取本机 /swagger.v1.json:
|
||||
MergePullRequestOption 的 do enum 包含 merge/rebase/rebase-merge/squash/fast-forward-only/manually-merged;
|
||||
包含 head_commit_id、merge_commit_id、force_merge。
|
||||
BranchProtection 包含 required_approvals、dismiss_stale_approvals、block_on_outdated_branch、status_check_contexts、push/merge allowlist 等。
|
||||
这是字段证据,未验证运行语义;方案 U1/U2 要做授权演练。
|
||||
|
||||
## Existing Development Manager
|
||||
|
||||
远端 README/PROJECT/MIGRATION 当前可读。
|
||||
MIGRATION 记录:2026-09-24T15:39Z,从 Agent-System dev@edbe59247872ef5861bafd30068955e22288c6ff 复制式拆出;新初始历史,未携带 .locks。
|
||||
PROJECT 将其定位为治理工具权威演进仓,阶段 A 通过 projects 聚合本机各项目 .locks,阶段 B 中央 API 尚预留。
|
||||
这是仓库文档的当前声明,不等于运行态部署证明。
|
||||
|
||||
本机 Development-Manager 目录存在,dev@8867e38ceb7cc71fa65d1a0f80178faff53fcb4c,工作树干净,缓存 origin/dev 无领先/落后提示。
|
||||
本次未单独核对其远端 dev SHA,因此不声称实时完全同步。
|
||||
本机 Development-Guidelines 目录不存在;历史记忆中的同名旧工作区不作为当前事实。
|
||||
|
||||
## Squad Boundary Evidence
|
||||
|
||||
| 路径 | 当前机制 | 设计影响 |
|
||||
|---|---|---|
|
||||
| internal/squad/service.go:113 | Service 引用主仓 config.Config | 注入 Options,不反向依赖主仓 |
|
||||
| internal/bizdev/service.go:195 | 直接 squad.NewService(cfg) | 装配与适配保留产品应用 |
|
||||
| internal/squad/workflow.go:547 | agent.Request、重试/失败分类与物化 | 执行端口、失败语义、重试归属要显式 |
|
||||
| internal/squad/service.go:256 | 直接主仓 stream 事件 | Squad 事件由应用映射至 SSE/持久状态 |
|
||||
| internal/squad/workspace_adapter.go:6 | 返回主仓 WorkspaceRef | 适配留应用侧 |
|
||||
| internal/squad/service.go:524 | templates/xlsx/cdo_quote.xlsx | 运行包资产不能仅包含 Markdown |
|
||||
| internal/bizdev/service_task_finalize.go:68 | 质量失败短路 | 不可将执行完成等同正式提交 |
|
||||
| internal/bizdev/service_task_finalize.go:35 | 编制人提交身份与 OnBehalfOf 分离 | 不改变产品授权 |
|
||||
| .doc/system-design/module-boundaries.yaml:283 | 七包依赖 allowlist | 先解耦,再独立 Go 模块 |
|
||||
|
||||
rg 穷举正式 Go import 显示 Squad 直接使用方涉及 29 文件/9 目录,包括 bizdev/chat/coordinator/server/intent/materialindex/projectcontext 与 CLI/script。
|
||||
主模块 `go.mod` 为 `module chat-agent-system`,Go 版本声明 1.27.1;本轮不推断目标机器工具链已安装。
|
||||
独立模块继续 import 主仓 internal 包不可行。
|
||||
现行模块开发标准明确模块化单体;仓库独立不自动授权增加 RPC/微服务。
|
||||
|
||||
## Tooling and Documentation Evidence
|
||||
|
||||
| 路径 | 观察 | 必须适配 |
|
||||
|---|---|---|
|
||||
| scripts/agentctl.py:598–610 | Git common-dir 需等于 main/.git | 标准 submodule 的 .git 文件布局当前不支持 |
|
||||
| scripts/agent_worktree.py:95–102 | 要求主工作区 .git 为目录 | 不能仅改 agentctl 一个入口 |
|
||||
| scripts/agentctl.py:642–669 | 直接 sqlite3.connect 至本地主仓 .locks/state.sqlite | 多 clone 不是共享协调域 |
|
||||
| scripts/agentctl.py:2861 | 本地 manager.lock 与队列循环 | 本地 daemon 不等于跨机服务 |
|
||||
| scripts/agentctl.py:1599–1625 | 按父仓 staged 文件识别共享文档并查收据 | .doc gitlink 不能替代内容级检查 |
|
||||
| scripts/agentctl.py:1287 / 1333 / 2245 | git show / cat-file 读取 HEAD/dev 中的文档与证据 | 跨仓 repo+SHA+path 引用 |
|
||||
| internal/archtest/boundaries.go:107 | 机器模块契约位于 .doc | 留在代码仓,不作为普通文档搬离 |
|
||||
| scripts/candidate-images-build.sh:60 | 仅 git archive 主仓提交 | 子模块源码固定递归导出 |
|
||||
| scripts/pi_subagents.py、agentctl_w4.py、.cursor | 多处固定 .doc 位置 | 客户端入口、报告及治理一起迁移 |
|
||||
|
||||
主目录 agentctl status 本轮返回 status=ok、protocol version=1;
|
||||
实际 policy revision 为 agent-governance-2026-09-23-fav-constraint-restored。
|
||||
CURRENT 顶部历史摘要仍有 2026-09-23 信息,活跃任务表更晚;方案未用其历史环境状态代替 Git/API 现状。
|
||||
|
||||
## Build and Runtime Assets
|
||||
|
||||
- scripts/build-single-binary.sh 构建 Web,然后 CGO_ENABLED=0 构建 cmd/server,写入版本 ldflags;不是全平台发布流程。
|
||||
- web/embed.go 的 go:embed all:dist 可将前端纳入二进制。
|
||||
- templates/embed.go 仅嵌入 docs/*.md;internal/template/loader.go 读取 configs/agents 指令和 configs/shared/writing.md。
|
||||
- deploy/app/Dockerfile 仍显式复制 web/dist、迁移、templates、configs,构建 server/events。
|
||||
- internal/localpg/localpg.go 查找本机 pg_ctl;数据库不是被 Go 服务自带。
|
||||
- deploy/pi-agent/Dockerfile 使用 Node 22 与单独 PI 包,固定 CLI 0.85.1;Runner 是额外执行依赖。
|
||||
- .github/workflows/ci.yml 有 Go/PG/Web 验证;nightly-real-pi.yml 有 Gitea 专用 runner/上传兼容说明。
|
||||
- 主仓 tree 跟踪 docgov.test、notification.test、projectinstance.test、storage.test;制品仓应避免继续提交二进制正文,历史不自动重写。
|
||||
- 未编译新平台、未启动服务、未读模型密钥、未读取 IDE 中业务 output.md。
|
||||
|
||||
## Lessons Reused
|
||||
|
||||
检索 .doc/audit/ISSUES.md 与 .doc/ops/lessons/INDEX.md,核对当前源码后采纳:
|
||||
- LESSON-20260923-matrix-status-cas:文档状态与 CAS 收据不能因拆仓失效。
|
||||
- LESSON-20260924-cursor-hook-advisory-gates:客户端提示与硬门禁分层,不称 UI/本地 hook 足以保护远端。
|
||||
- LESSON-20260926-failclosed-bypass-contract:缺失组件/收据/权限失败关闭,禁止 gitlink 绕过。
|
||||
- LESSON-20260927-server-facts-and-iso-db-recipe:未来验证使用明确隔离 DB,不回退至真实 dev。
|
||||
- 当前 Squad step-empty-output、squad-commit-authority、quality-heading 相关台账按原状态保留;本轮不修复、不关闭。
|
||||
|
||||
这次没有实施与运行验证,不新增 validated 经验;多仓适配风险在本研究记录和计划出口追踪,进入实施后按对应失败事实建问题/回归记录。
|
||||
未修改共享 ISSUES/CURRENT/MATRIX,避免把待审架构建议写成现行缺陷修复/生效规范。
|
||||
|
||||
## Review Record
|
||||
|
||||
ce-plan 研究后,由 ce-doc-review 的一致性、可执行性、安全、范围、产品价值、反向挑战视角复核。
|
||||
本地审查的四项实际修订:
|
||||
1. 五分钟上游通知明确为后续自动化目标,首期检查点不伪称实现该 SLA。
|
||||
2. U1 只验基线/平台可行性,完整候选队列/回写恢复在 U2 实现后验收,消除前置依赖循环。
|
||||
3. 内部完整源码包与公开源码包分开;公开包采用允许清单,排除私有开发资料,并加入负例。
|
||||
4. U6a 提前从当前固定单仓基线交付私有包试点,U6b 在拆仓后复验同一构建合同。
|
||||
|
||||
补充跨模型检查曾通过 ce-doc-review 的 claude CLI 路线请求 claude-opus-5/high,对 security/product/adversarial/whole-doc 发起只读复核。四个任务在本地审查完成后仍未返回结果,主代理主动终止附加检查;runner 记录 timeout(reaped on request before completion),result 读取均返回 3、没有审查产物。未验证实际服务模型,未将其计为独立复核或 PASS。此限制不改变本地六视角的事实与修订记录。
|
||||
|
||||
本地审查汇总:coherence 1、feasibility 1、security 1、product 1 项已修订;scope/adversarial 没有追加有效问题。未对复用同一子代理上下文的后续角色作独立性加权。
|
||||
用户要求完整待审方案,允许对本轮草稿调整完善,四项修改均保持 Product Contract 与用户已确认边界;不存在需要以草稿修订为由提前实施的动作。
|
||||
|
||||
最终文档检查:三份 Markdown 的 frontmatter、代码围栏、相对链接、R1–R10/U1–U6/V1–V12 覆盖与 whitespace 检查通过;git diff --check 通过。实施测试均未运行。
|
||||
只读收口:agentctl cleanup report exit=0,19 worktrees,ready=0、blocked=19;没有删除。agentctl docs drift exit=0,30 records、regressions=0、open_capabilities=3;没有改 MATRIX。
|
||||
CodeGraph 在开始与收口运行 sync -q 成功;其近似索引仅辅助定位,依赖完整性以 rg 核对。
|
||||
这些是文档审查修订,不是系统已支持的能力。
|
||||
|
||||
## External References
|
||||
|
||||
- [Git submodule](https://git-scm.com/docs/git-submodule):固定提交、相对 URL 解析。
|
||||
- [Go 模块布局](https://go.dev/doc/modules/layout):internal 可见性与公开包。
|
||||
- [Go cgo](https://go.dev/cmd/cgo/):交叉编译与 C 工具链边界。
|
||||
- [Gitea Actions FAQ](https://docs.gitea.com/usage/actions/faq/):PR head 与合并候选区别。
|
||||
- [Gitea Actions token](https://docs.gitea.com/usage/actions/token-permissions/):fork 和跨仓权限。
|
||||
- [Gitea 保护分支](https://docs.gitea.com/usage/access-control/protected-branches/):审批与状态检查配置。
|
||||
- [Gitea 仓库权限](https://docs.gitea.com/1.22/usage/permissions/):私有仓访问语义;最终以实例 ACL 实测为准。
|
||||
|
||||
## Revision 2: Web and Agent-Native
|
||||
|
||||
授权来源:用户在原方案审阅中追加前端拆仓和 pi-native/agent-native 共享执行基础设计;沿用 task_id,不启动实施。
|
||||
本轮源码基线仍为 a3db288a7aa055ff5884e3aa4d91511959cff721,文档上一提交为 de90d0cd70e5cfadbd680e8caf8bdf18724e44a8。
|
||||
旧机器登记已 done,agentctl 不提供 reopen;新增同 worktree 的执行登记 multi-repo-design-2026-09-28-r2,逻辑任务和三个允许写路径不变。
|
||||
|
||||
### Confirmed Wiring
|
||||
|
||||
| 证据位置 | 确认的事实 |
|
||||
|---|---|
|
||||
| internal/agent/backend.go:38–60 | 公共 Backend/Execute,工厂支持 pi-agent/pi-native;native 包裹 PI fallback;未知名默认 Stub |
|
||||
| internal/app/app.go:132–133 | 通过同一工厂创建助手 dialogueBackend 并注入 coordinator |
|
||||
| internal/coordinator/dialogue_llm.go:126 | 助手调用 Execute,交付 reply.md;当前非直接返回文本接口 |
|
||||
| internal/squad/service.go:261、402 | Squad 任务/工作流也调用同一后端工厂 |
|
||||
| internal/config/config.go:109 | 源码默认 AGENT_BACKEND 为 pi-agent;不证明运行配置 |
|
||||
| internal/app/app.go:162–186 | 语义摘要、文档变更叙事、U9 segment 仍有 pi-agent 名称条件 |
|
||||
| internal/agent/pinative.go:17–28、96 | PiNativeBackend 是产品配置/工作区与 agentx.Run 的适配层 |
|
||||
| internal/agentx/loop.go | 具备原生 loop、tool 调度、stream、usage、turn cap;不是完整产品持久任务控制面 |
|
||||
| internal/agentx/llm/client.go:14 | 当前是兼容 Chat Completions 的 client,不能推断全供应商适配 |
|
||||
| internal/agentx/session/jsonl.go | 本地 JSONL transcript 与对象 mutex;不是跨进程执行租约 |
|
||||
| internal/agent/fallback.go:21–41 | native 不成功且 context 未取消时尝试 PI;Name 仍可为 pi-native,仅日志记录实际回退 |
|
||||
| internal/taskrun/backend.go | 另有后端名称 allowlist 和生产 stub 限制,改名需全入口一致 |
|
||||
| web/package.json / vite.config.ts | 独立 npm 构建、测试与 Vite API proxy,可作为独立源码仓 |
|
||||
| web/embed.go:14 | 当前 Go embed 与前端目录共置,不能直接整体搬走而不处理 Go import |
|
||||
| internal/server/static_assets.go:16、20–36、resolveDistDir | 导入主模块 web;磁盘资源优先,多个相对路径可能遮蔽内嵌 UI |
|
||||
| web/src/api/* / admin/console | 手写 API 客户端、auth header/cookie、后端信息展示需要契约与兼容检查 |
|
||||
|
||||
CodeGraph sync 成功并 explore PiNativeBackend;rg 再确认助手/Squad 工厂调用和 agentx import 依赖。
|
||||
未读取业务 output.md、workspace.json、模型密钥或运行实例环境;没有调用真实模型或产品 Agent。
|
||||
所以结论是“源码支持双方选择 native、具备公共接口”,不是“当前两条线上路径已运行 native”或“native 全能力已验收”。
|
||||
|
||||
### Revision Decisions
|
||||
|
||||
1. 新增 Agent-System-Web 与 Agent-Native 仓库建议;Core 不再承载前端源码和通用 Agent 内核。
|
||||
2. Agent-Native 包含 contract/runtime/adapters/conformance;公共 contract 不反向依赖 Core/Squad,Core 注入后端,Squad 只使用契约。
|
||||
3. 模型适配、完整 Agent 适配、工具适配分开;保留未来吸收先进实现的空间,不承诺任意 SDK/CLI 即插即用。
|
||||
4. pi-native 旧名、AGENTX 配置与 .agentx 数据路径提供兼容;默认后端不随拆仓切换。
|
||||
5. 区分 native 自身成功与 PI fallback 成功;未知副作用不自动跨后端重放。
|
||||
6. Web 构建产物在隔离构建上下文物化到 Core embed 目录,仍可发布一个包含 UI 的服务二进制;不提交 dist 或软链接越界。
|
||||
7. 增加 U7/U8、R11–R13、V13–V16,同步更新仓库地图、拓扑、Core/Squad 依赖、发布组合和描述;没有只追加一个与旧建议矛盾的附录。
|
||||
|
||||
### Revision Verification
|
||||
|
||||
本轮针对新增边界做源码/官方资料核对与作者一致性复核,不复用第一轮六视角结果作为修订 2 的新增审查证据;未发起额外外部模型复核。
|
||||
复核修正:U7a 先形成可独立获取的最小 contract 模块,U4 消费它,U7b 再迁 native/PI 实现,避免 Squad 和完整 Native 提取相互等待。
|
||||
Go embed 的包/模块边界据[官方文档](https://pkg.go.dev/embed)确认;Vite 独立构建/后端集成据[官方指南](https://vite.dev/guide/backend-integration)核对;工具与完整 Agent 的区别参考[MCP 架构](https://modelcontextprotocol.io/docs/learn/architecture)。
|
||||
仅实施计划中定义真实 native/PI、浏览器与平台验收,本轮未运行,不能标 PASS。
|
||||
|
||||
## Revision 3: Ubuntu-only and PI Core Parity
|
||||
|
||||
用户确认近期不需要 Ubuntu ARM/Apple Silicon;异步澄清确认目标为“对齐核心能力并逐步替代”,不是仅兼容接口或完整上游复刻。仍未授权实施。
|
||||
本机 uname -m=x86_64,os-release=Ubuntu 26.04.1 LTS;当前支持目标改为 linux/amd64,无需本期交叉编译。
|
||||
只读核对 deploy/pi-agent/Dockerfile 固定包0.85.1;npm 该版本元数据 gitHead=d981de1229ef899957bbe968bc8dcda02a21f477,repository=earendil-works/pi,license=MIT。通过内存读取发布tarball检查README/compaction/rpc/extensions文档,无安装/执行包代码;未验证运行容器版本。
|
||||
项目 pi-args 默认 --mode rpc --no-session,workspace 为受限工具且加载扩展;Runner提供 /v1/run 与 /v1/search,typed-result-v1 与 Tavily 属于实际依赖范围。
|
||||
agentx 已有 loop/tools/session/stream/steering/follow-up;没有因此推断完整 PI compaction/扩展/会话兼容。
|
||||
现有 abtest/Suite 只有6例;Judge比较成功率/P0/P95≤1.3倍;live测试直接 NewPiNativeBackend 禁止隐性fallback,RUN_AGENTX_AB=1 才运行。本轮未运行它,不把既有测试代码当通过证据。
|
||||
新增独立 U9 能力盘点→补齐→对比→渐进启用→替代收口;移除本期双平台要求、macOS签名阻断与ARM构建要求;更新描述与验收。
|
||||
来源:https://registry.npmjs.org/@earendil-works%2fpi-coding-agent/0.85.1 与固定提交的上游 README/docs;上游main仅用于导航,能力基线以固定版本为准。
|
||||
|
||||
## Revision 4: Governance Review
|
||||
|
||||
用户要求根据多仓/Ubuntu/Agent-Native方案复审锁、agentctl及规范,可在方案中删减不必要/频繁出问题的机制并补齐缺项。
|
||||
授权仍为研究和待审文档,不执行规范切换,不改变现行工具/配置/数据库/远端设置。
|
||||
|
||||
### Confirmed Issues and Evidence
|
||||
|
||||
ISSUE-20260928-governance-lock-domain,issue_state=investigating,owner=Codex(方案),影响为独立工作区无关写入被全仓恢复状态阻塞。
|
||||
已证实机制:scripts/agentctl.py:532–557 的 path 资源和 overlaps 没有工作区标识;path:. 覆盖所有相对路径。
|
||||
scripts/agentctl_console.py:186 为 integrate_prepare 获取 path:. + git:index + git:dev;:211–220 对合并失败尝试abort后仍标 recovery_required。
|
||||
scripts/agentctl.py:982–998 的持锁比较继续纳入 recovery_required,因此一个候选冲突/失败可能长期挡住不同worktree。
|
||||
本会话 r2 提交与 r3 文档写入实际均被同一 operation 拒绝;它不是仅根据代码猜测的风险。
|
||||
没有调查那个集成任务具体合并失败的底层原因,不能声称已经定位其全部根因。
|
||||
|
||||
问题类别还包括过度恢复升级:execute 在带 env/git:dev 的命令非零退出时统一需要恢复(agentctl.py:1145–1146);应按实际副作用证据区分,不能因此把真正未知结果直接释放。
|
||||
physical_names 将 git:dev 与 env:dev 绑定同一 dev-integration.lock;固定制品部署后可分离代码引用与运行环境锁。
|
||||
|
||||
另有 task 生命周期不一致:规范允许 blocked并要求原task_id续办,task close 只支持done/cancelled,register拒绝非active的同ID。
|
||||
本会话曾尝试blocked而被CLI明确拒绝;新增-r2/-r3只是受限工具下的执行登记,逻辑研究任务一直保持原ID。
|
||||
建议新工具原ID可reopen/update-state,执行尝试另有attempt_id,旧运行副作用日志保留。
|
||||
|
||||
治理快照(本轮只读SQLite URI mode=ro):completed=1455、failed=208、recovered=167、busy=21、recovery_required=1。
|
||||
其中recovered为command=144、integrate_prepare=22、integrate=1;这不是错误率、误报率或性能基准,不把正常恢复也算缺陷。
|
||||
阻塞operation为 integrate-nightly-advance-2026-09-28-r2-prepare-838577a7,resources含git:dev/path:./该候选tree,exit_code=1。
|
||||
稍后复核该记录已为recovered,本代理没有调用recover、删锁或修改管理数据库状态来解除它;正常agentctl with随后返回成功。
|
||||
Development-Manager中的对应overlaps/Lease/prepare与task close逻辑仍同类,不能靠只把工具搬仓根治。
|
||||
|
||||
### Decisions and Replacements
|
||||
|
||||
方案KTD6改用Gitea原生PR合并/保护/CI,取消默认自建签名收据网关、机器人直推与manually-merged回写。
|
||||
仍显式验证base/head/候选tree/组件manifest,合入后的真实SHA绑定制品和部署;不能将PR head测试当合并候选通过。
|
||||
方案KTD13给出retain/replace/remove矩阵、局部资源身份、known-failure/unknown分类、任务续办、清理和规范层级。
|
||||
U2负责替代治理与切换,补V19–V23负例;不是另外堆一套大型协调服务。
|
||||
工作区的源码副本不互锁;同实际目录和共享环境保留适用保护;产品数据库事务/文档职责/运行租约不在工程锁裁剪范围。
|
||||
|
||||
暂不修改共享ISSUES/lessons索引:本次用户明确仅允许三个待审文档,本节保存问题主记录候选,实施/正式文档集成时由相应owner补索引。
|
||||
没有修复和回归运行,不把上述问题标resolved或经验标validated;既有coordination-authority问题继续按原状态追踪。
|
||||
|
||||
### Authority and Exception Record
|
||||
|
||||
曾因正常入口仍被全仓恢复锁阻塞,请求只针对这三份文档的临时租约豁免。
|
||||
用户批准:owner=Codex,原因=待审文档落盘被无关恢复操作阻塞,覆盖=本任务三文档的agentctl with前置,禁止=解锁/改工具或现行规范/提交/推送;有效期=保存完成即结束;恢复动作=保留可审diff,恢复正常租约流程。
|
||||
实际结果:原阻塞先恢复,r3与r4方案主体通过agentctl with保存;随后新操作integrate-nightly-advance-automation-2026-09-28-r7-prepare-dca474ad再次进入recovery_required,正常入口拒绝最后的文档写入。
|
||||
本轮最后保存使用用户已批准的豁免,仅对原三文档直接写入;没有调用recover、删除锁、修改hook/agentctl/现行规范或提交/推送。完成保存和校验即终止豁免,后续回归正常租约流程。
|
||||
用户确认的“仅三份待审文档、不提交或推送”作为本次交付限制保留。
|
||||
|
||||
修订3的Ubuntu单平台和U9原生核心替代已经本轮正常落盘;修订2工作区改动一并保留,没有丢弃或覆盖其他任务文件。
|
||||
本次审查是源码/运行元数据/官方Gitea资料的定向核对与作者一致性检查,未声称重复进行了首轮六视角或外部模型审查。
|
||||
|
||||
最终校验:3份文档、6个相对链接、15项要求、9个实施单元、23项验收场景,结构/ID/链接/旧方案残留检查通过;git diff --check exit=0。收口盘点cleanup total=18/blocked=18/ready=0,未清理;docs drift regressions=0/open_capabilities=3。没有实施、编译或真实运行验收。本轮使用的一次性三文档豁免至此结束,不提交/不推送。
|
||||
|
||||
## Revision 5: Proportionate Everyday Rules
|
||||
|
||||
用户追加“很多规范过严,按最新方案适当完善”;本轮仍仅修订原方案、证据与任务,不批准或启用新规范。
|
||||
读当前AGENTS、PROJECT、验证契约、经验闭环、收口模板及KTD13;核实已有纯文档免全量测试/按机制选场景/矩阵按声明更新等合理条款,未把它们误报为全都缺失。
|
||||
|
||||
问题是执行尺度和入口一致性:Go/接口验证矩阵列全包vet/short,而报告章节又说明不无条件全量;需明确本地迭代/组件CI/组合发布三个检查阶段。
|
||||
启动阅读、每次收口盘点、任务与多表状态、逐修复lesson要求,对小改动容易叠加成本;应区分硬边界、默认做法和工具建议。
|
||||
本会话没有测试耗时/误报率统计,不虚构量化收益;提出的改进以规则文本和已出现流程阻塞为依据。
|
||||
保留真实权限、持久性、运行制品、敏感数据和已确认外部副作用边界。
|
||||
|
||||
新增KTD14及验证分层/证据复用/规则强度说明,扩展U2的规范模板同步和日常演练,新增R16/V24/V25。
|
||||
不新增implementation unit或独立报告,避免为了精简流程再扩大产物层级。
|
||||
本轮仅修订三文档,使用新的r5执行登记承载同一个逻辑task_id;上一轮一次性豁免已结束,本轮不自动沿用。
|
||||
|
||||
修订5验证:三文档结构、6个相对链接、R1–R16/U1–U9/V1–V25唯一性与待审状态检查通过,git diff --check通过。只读cleanup为18项blocked、0项ready,docs drift无回退;未清理工作区。正常agentctl with写入,无需也未沿用上轮临时豁免;未提交、推送、修改生效规范或执行产品测试。
|
||||
|
||||
## Revision 6: Remove Coding Workflow Dependence
|
||||
|
||||
用户明确反馈Agent主要精力被agentctl任务领取、提交、合并消耗。本轮收敛KTD13/14的职责,新增KTD15:普通编码流程零agentctl依赖,任务/PR单一状态来源;治理恢复归维护者,发布部署独立。
|
||||
这不是已经测得时间占比;依据为用户观察与本会话两次跨工作区锁阻塞、登记续办及blocked CLI不匹配的实际记录。
|
||||
修改U2优先顺序,先完成本地减负与Gitea交接,再做多仓适配;不把新的task/reopen/claim平台作为减负前提。新增V26端到端日常流程验收。
|
||||
本轮仅修订原三份待审文档,不启用新规则,不提交/推送;现行登记仍按当前工具执行,逻辑task_id未变。
|
||||
|
||||
## Revision 7: Single Executable, Instance Configuration and Native Infrastructure
|
||||
|
||||
用户连续补充:同一套单文件应用供测试/生产等多个实例,通过启动配置隔离;希望.env、CLI、系统设置后读覆盖;进一步评估KB/DB/文档库用launcher、daemon、应用内启动或原生程序。
|
||||
本轮作为原任务续办,只完善原三文档;此前中断段只读检查已完成,没有启动或变更产品服务。
|
||||
|
||||
### Baseline and Current Source Checks
|
||||
|
||||
原研究worktree仍HEAD=de90d0cd(业务源码基线a3db288a);主目录本轮已为test@fd177f8dd5c7e5714476b59efc1b9a2ae27bdb22,dev=b9339cd27206f519f423eaa6a1601d3a67dfac01。
|
||||
没有切换/重置主目录或重新合并业务源码。
|
||||
对a3db288a..dev的config/localpg/sysadmin settings/db migrate/main/app/templates/web embed/build-single-binary以及kb/storage/workspace/docrevision相关路径做diff --stat,无差异输出;本节结论同时适用于本轮核对的这些dev路径,不能推及未查模块或运行环境。
|
||||
没有读取.env真实值、用户output.md、workspace.json、运行数据库数据或修改系统设置。
|
||||
|
||||
### Source Findings
|
||||
|
||||
| 来源 | 已确认事实与影响 |
|
||||
|---|---|
|
||||
| cmd/server/main.go:22–40、config/config.go:103、159–174 | 默认→env文件→进程env,CLI只覆盖env/port/host/backend;默认.env路径与Load空字符串的overlay路径不同 |
|
||||
| config/config.go:99、localpg/localpg.go:36/48/74 | 存在dev默认路径与部分直接os.Getenv读取;尚非统一最终配置快照 |
|
||||
| sysadmin/settings.go:78/229/275 | 独立DB→进程env→默认解析,不直接消费已合并CLI/.env的cfg;不能说所有系统设置已整体覆盖启动配置 |
|
||||
| sysadmin/service.go:NewService、settings/store | 系统设置来自所选业务PG,key无实例命名空间;用它配置自身连接会循环 |
|
||||
| sysadmin/apply.go、settings.go:199、app/app.go | 部分键推送PI /admin/reload;保存、热更新和真正启动消费并不等价 |
|
||||
| db/migrate.go:RunMigrations/ResolveMigrationsDir | 当前Goose从磁盘SQL目录读取;现有二进制尚未内嵌全部迁移 |
|
||||
| web/embed.go、templates/embed.go、template/loader.go、squad/service.go | Web与部分Markdown已内嵌,agent指令和xlsx等仍依赖磁盘路径 |
|
||||
| localpg/localpg.go:100/123/271/287 | 查找已安装PG→可initdb建cluster→createdb建应用库→启用vector;不是安装数据库,也不创建独立KB语料库 |
|
||||
| localpg/localpg.go:187/261/307、cmd/server/main.go | 当前按端口就绪判断、已有角色bootstrap、started标记与defer清理不足以证明所有权/崩溃恢复;os.Exit可绕过defer |
|
||||
| kb/client.go:75/94、kb/store.go、kb/embed.go | KB是应用内检索代码直连PG;embedding/reranker为外部HTTP能力;连通不等于语料与索引已准备 |
|
||||
| storage/cas.go:42、workspace/capability.go、docrevision/pg_store.go | 文档库由PG账本/内容与持久文件组成,非独立文档daemon |
|
||||
| app/app.go:224、workspace/lease.go | 进程内workspace租约不足以宣称多副本共享同一数据根已安全;独立实例与HA副本分开 |
|
||||
| cmd/server/main.go:71、cmd/events/main.go | server已有可选内置events consumer,单文件模式可收敛到同一程序角色,不必分发第二个应用程序 |
|
||||
|
||||
两项后续调查候选:配置解析双路径/生效声明不一致、managed-local PG所有权与失败回收不足。
|
||||
本轮没有运行复现或修复,不登记为resolved,不改共享ISSUES索引;U10中给出针对性正负例。
|
||||
既有data-dir-doubled、PI runner DATA_DIR覆盖、隔离DB经验直接用于路径归一化与禁止隐式dev回退的验收设计。
|
||||
|
||||
### Decisions
|
||||
|
||||
1. 单版本单个Ubuntu amd64应用文件内置Web/SQL/prompts/xlsx等资产,同文件可启动多个独立实例;配置/数据/日志不打进程序。
|
||||
2. 不新增应用launcher/KB/文档daemon;同一二进制提供serve/init/migrate/config检查,系统服务管理应用和原生PG。
|
||||
3. 产品部署设置放独立实例配置根的instance-settings文件,作为普通部署参数的最终层;产品业务策略仍在所选DB,key各有唯一owner。
|
||||
4. bootstrap identity/config_root/settings读取模式不自覆盖;DB/KB/目录等可经UI配置,但须重启/迁移后确认applied,不能在进行中任务里换一半。
|
||||
5. 默认existing;显式创建逻辑库与安装PG/建立cluster/迁移schema/导入KB区分;managed-local仅可选自有cluster。
|
||||
6. 配置身份、DB角色、可写目录、cookie/密钥、Runner上下文和文件/数据库备份同时隔离;一套二进制不构成隔离证明。
|
||||
7. U10为应用部署新工作,U6a要先满足其最小单文件/外部PG出口;不把该需求隐藏在U9的PI能力替代里。
|
||||
|
||||
复核由独立只读子审查覆盖配置链、存储/进程依赖与设计一致性,补齐saved/applied、最终配置冲突检查和外部进程停止权。
|
||||
本轮没有额外外部模型审查、编译、initdb、建库、启动/停止daemon、服务配置写入或业务数据迁移。
|
||||
|
||||
### Primary References
|
||||
|
||||
- [PostgreSQL initdb](https://www.postgresql.org/docs/current/app-initdb.html):cluster初始化与原生进程身份。
|
||||
- [CREATE DATABASE](https://www.postgresql.org/docs/current/sql-createdatabase.html):逻辑数据库创建。
|
||||
- [PostgreSQL启动与系统服务](https://www.postgresql.org/docs/current/server-start.html):原生postgres/pg_ctl和系统服务职责。
|
||||
- [pg_ctl](https://www.postgresql.org/docs/current/app-pg-ctl.html):原生管理边界。
|
||||
- [pgvector项目](https://github.com/pgvector/pgvector):PG内扩展而非单独daemon。
|
||||
systemd官方man页面本次抓取403,未将该失败当作成功资料;系统服务建议参考PostgreSQL官方启动文档。
|
||||
|
||||
修订7验证:3文档结构、6个相对链接、R1–R19/U1–U10/V1–V34唯一性与待审标记检查通过,git diff --check通过;只读cleanup为20项blocked/0项ready,docs drift无回退,未清理。源码/配置/生命周期定向复核完成;未编译、建库、运行实例或部署。文档通过正常agentctl with保存,本次未使用临时豁免,仍不提交/不推送。
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: "U1 治理演练记录:Gitea 原生门禁与 CI 可用性验证"
|
||||
date: 2026-09-29
|
||||
task_id: multi-repo-design-2026-09-28
|
||||
drill_repo: weichen/governance-drill-2026-09 (private, 验证后保留至 U2b 收口再删)
|
||||
basis: 2026-09-29-multi-repo-implementation-plan.md Phase 0 / U1.5
|
||||
---
|
||||
|
||||
# U1.5 演练结论(全部通过)
|
||||
|
||||
环境:Gitea 1.27.3(本机 docker:gitea-server/gitea-db/gitea-runner),runner `home-runner`,
|
||||
标签映射:`ubuntu-latest→docker://gitea/runner-images:ubuntu-latest`、`docker→docker://docker:dind`(不可用)、`nightly→host`。
|
||||
|
||||
## 分支保护与 PR 门禁(负例 + 正例)
|
||||
|
||||
保护配置(drill main):enable_push=false、required_approvals=1、dismiss_stale_approvals=true、
|
||||
block_on_outdated_branch=true、apply_to_admins=true、status_check_contexts=[pr-verify / verify (pull_request)]。
|
||||
|
||||
| 编号 | 场景 | 结果 |
|
||||
|---|---|---|
|
||||
| N1 | admin 身份 HTTP 直推保护分支 | 拒绝(pre-receive hook declined;apply_to_admins=true 生效) |
|
||||
| N2 | API PUT /contents 写保护分支 | 拒绝(user cannot commit to repo) |
|
||||
| N3 | 无批准合并 PR | 拒绝(Does not have enough approvals) |
|
||||
| N4 | 批准后 head 新提交再合并 | 拒绝(陈旧批准被撤销);重新批准后可合并 |
|
||||
| N5 | required status check 未完成时合并 | 拒绝(405 Please try again later);CI success 后合并成功 |
|
||||
| 自批准 | 作者批准自己的 PR | **Gitea 1.27.3 硬禁止**(approve your own pull is not allowed),admin 亦不可 |
|
||||
|
||||
合并语义:merge 后 base.sha=merge_commit_sha(0540e27c),merged_by=审核者(weichen)而非作者。
|
||||
PR #2/#3 全流程:agent-bot(作者)→ weichen(批准+合并),作者/审核者分离经 Gitea 原生强制。
|
||||
|
||||
## D2' 决策修正:agent-bot 机器账号
|
||||
|
||||
单账号下 required_approvals=1 永远无法满足(自批准被硬禁止)。
|
||||
已创建机器账号 `agent-bot`(凭据存 `~/.secrets/agent-bot`,token scope:write:repository, read:user):
|
||||
编码 Agent 以 agent-bot 提交/建 PR;用户以 weichen 批准并合并。原方案 D2"不建机器人账号"据此作废。
|
||||
|
||||
## CI 可用性与根因修复
|
||||
|
||||
发现并修复 Agent-System CI 自 2026-09-24 起全红的根因:
|
||||
|
||||
1. **docker 网络地址池耗尽**:历史 CI 任务遗留 24 个 `GITEA-ACTIONS-TASK-*` 桥接网络未清理,
|
||||
默认地址池(~16 个 /16 池)占满,此后所有 docker 模式任务创建 job 网络时报
|
||||
`all predefined address pools have been fully subnetted` 秒败。已清理全部 24 个陈旧网络(10 个网络保留),
|
||||
清理后 ubuntu-latest docker 模式任务验证成功。
|
||||
2. **nightly host 模式失败为当时环境问题**:09-24~28 日志显示 runner 容器内 github.com DNS(fake-ip 198.18.0.1)拒绝;
|
||||
当前 DNS(192.168.3.1)下 runner 容器可访问 github.com,`actions/checkout@v4` 克隆探针成功。
|
||||
3. `docker:dind` 标签的镜像拉取在本机挂死,该标签不可用;工作流避免使用。
|
||||
|
||||
## Workflow 路径行为(实例特性)
|
||||
|
||||
- **新仓库只有 `.gitea/workflows/` 路径触发**(树级检测:文件未被本次提交修改也会触发);
|
||||
drill 仓 `.github/workflows/` 从未触发(含文件被修改的推送)。
|
||||
- 存量 Agent-System 仓 `.github/workflows/` 正常触发(run 74 = 本日 dev 推送,179 个历史任务均来自该路径)。
|
||||
- 结论:现有 Agent-System 维持 `.github/workflows/` 不迁移;**新建仓库一律用 `.gitea/workflows/`**。
|
||||
实例对不同仓库的路径检测差异根因未深究,已记录为已知行为。
|
||||
|
||||
## 遗留状态
|
||||
|
||||
- drill 仓任务 183/185(docker:dind 拉取挂死)在 UI 呈 running 僵尸态,无实际执行,仓库删除时一并消失。
|
||||
- 演练仓保留至 U2b 完成(作为保护配置参照),之后删除。
|
||||
|
||||
## 对 U2b 的直接输入
|
||||
|
||||
- dev/test/main 保护字段组合与白名单保底方案(weichen 进 push whitelist 以不破坏现有直推流程)。
|
||||
- required checks 的 context 命名格式:`<workflow> / <job> (<event>)`。
|
||||
- 新流程红线:作者≠批准者由 Gitea 强制;Agent 不得以 weichen 身份批准自己的 PR。
|
||||
+15
-4
@@ -2,16 +2,27 @@
|
||||
|
||||
维护规则:[问题记录与开发经验闭环](../ops/2026-09-23-issue-learning-workflow.md)。每个问题一个稳定 ID;主记录是事实与状态来源,本表为可检索投影。新发现及时登记,修复后保留记录;发布 blocker 仍见 [gap-matrix](../remediation/gap-matrix.md)。
|
||||
|
||||
更新:2026-09-28。只上传文档、无文字时对话 400 已在重载后的 dev 浏览器复验(resolved,`bin/agent-server` 10:22 +0800)。历史任务 run 事件流 HTTP/2 与 502 另记,未在本轮修。激活预览前端预检与管理台密钥 form 已在 dev 浏览器复验(resolved,dev `3e534bbb`,项目 6023)。非编制人触发小队账本提交被 `uow: permission denied` 拒绝(fixing,待 dev 重跑)。test 注册 INTERNAL:标准角色 role-guest 缺失导致 FK 23503,已补种并重启 test(goose 77,resolved)。重复用户名 400 在新代码单测通过;当前 rc 二进制仍返回 INTERNAL,随下次含本提交的 test 制品生效。管理台重置密码入口被 F4-A 误删(端点仍在),已恢复 UI(resolved)。2026-09-27。上传链路:登记并修复上传文档跨端不可访问(占位模板退化 + 元数据/正文不落服务端,fixing 待 dev 集成)。2026-09-26。docgov 批准链:登记并修复 review_snapshot_id 装载缺失与 uow 行锁自死锁(resolved);memDocs 持久化失败缓存投毒(investigating)。历史 resolved 不表示本日产品回归通过。系统 code review P0/P1 批次修复落账(5 resolved + 1 deferred + 1 open,主记录见 [整改文档](2026-09-26-system-code-review-fixes.md),来源 [审计](2026-09-26-system-code-review.md))。
|
||||
更新:2026-09-28。资料类名词提问被识别成模糊咨询并吃到阶段推销模板(resolved:intent-material 已合入修复与验收报告);阶段推进失败后没有助手消息(open,未修)。docgov 共享 map 并发读写曾在 06:38 UTC 打崩 dev 进程(resolved:dev `b9339cd2` 重建后 `/health` 200,空登录 401)。只上传文档、无文字时对话 400 已在重载后的 dev 浏览器复验(resolved,`bin/agent-server` 10:22 +0800)。历史任务 run 事件流 HTTP/2 与 502 另记,未在本轮修。激活预览前端预检与管理台密钥 form 已在 dev 浏览器复验(resolved,dev `3e534bbb`,项目 6023)。非编制人触发小队账本提交被 `uow: permission denied` 拒绝,已改为编制人代理提交并在 dev 重跑通过(resolved)。test 注册 INTERNAL:标准角色 role-guest 缺失导致 FK 23503,已补种并重启 test(goose 77,resolved)。重复用户名 400 在新代码单测通过;当前 rc 二进制仍返回 INTERNAL,随下次含本提交的 test 制品生效。管理台重置密码入口被 F4-A 误删(端点仍在),已恢复 UI(resolved)。2026-09-27。上传链路:登记并修复上传文档跨端不可访问(占位模板退化 + 元数据/正文不落服务端,fixing 待 dev 集成)。2026-09-26。docgov 批准链:登记并修复 review_snapshot_id 装载缺失与 uow 行锁自死锁(resolved);memDocs 持久化失败缓存投毒(investigating)。历史 resolved 不表示本日产品回归通过。系统 code review P0/P1 批次修复落账(5 resolved + 1 deferred + 1 open,主记录见 [整改文档](2026-09-26-system-code-review-fixes.md),来源 [审计](2026-09-26-system-code-review.md))。
|
||||
|
||||
| issue_id | 模块 / 检索词 | 问题 | 严重度 | issue_state | owner | 证据日期 / 范围 | 唯一主记录 | 经验 |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| ISSUE-20260928-material-template-reply | intent / coordinator / 说明书 / 操作视频 / 掂量 / reply_source=template / fallback_reason | 说明书、操作视频、竞品等资料名词未进知识词典,落到模糊咨询后输出阶段推销模板;对话模型失败时没有日志和 fallback 原因 | high | verifying | Cursor | 2026-09-28;test 会话 sess-p8-u33;dev 库项目 6024 工作树二进制 reply_source=kb | [主记录](../ops/short-term/2026-09-28-intent-material-fallback.md#issue-20260928-material-template-reply) | [LESSON-20260928-material-noun-not-sales-template](../ops/lessons/2026-09-28-material-noun-not-sales-template.md) |
|
||||
| ISSUE-20260928-advance-stage-silent-fail | coordinator / ADVANCE_STAGE / EXPECTATION_HIT / step_03_regulatory / chat turn failed | 用户回复「1」触发阶段推进后,法规步骤失败,回合被标失败且没有助手消息,用户盲等 | high | open | Cursor | 2026-09-28;test 会话 sess-p5-u32 turn 22;本轮只登记不修 | [主记录](../ops/short-term/2026-09-28-intent-material-fallback.md#issue-20260928-advance-stage-silent-fail) | 未修,不新增经验 |
|
||||
| ISSUE-20260928-docgov-map-race | docgov / memDocs / concurrent map / GetDocument / 登录 500 | per-doc 锁拆分后共享 map 无互斥,不同文档并发读写 fatal,进程退出期间全部 API 500 | critical | resolved | Cursor | 2026-09-28;fatal + ECONNREFUSED;`-race` PASS;dev `b9339cd2` 重启后 `/health` 200、空登录 401(`req_e28309cc8d615ed4`)、Vite public-config 200 | [主记录](../ops/short-term/2026-09-28-docgov-shared-map-race.md#issue-20260928-docgov-map-race) | [LESSON-20260928-docgov-shared-map-race](../ops/lessons/2026-09-28-docgov-shared-map-race.md) |
|
||||
| ISSUE-20260928-quality-heading-drift | squad / quality_gate / 缺少必填章节 / State-of-the-Art / H3 | 调研报告章节在,只因 H3 和少空格被判缺章,受控文档不投影 | high | resolved | Cursor | 2026-09-28;dev `bd_research_1790578240073269711` project_id 6024,quality passed,doc head_snapshot_id 62 | [主记录](../ops/short-term/2026-09-28-quality-gate-heading-mismatch.md#issue-20260928-quality-heading-drift) | [LESSON-20260928-quality-heading-normalize](../ops/lessons/2026-09-28-quality-heading-normalize.md) |
|
||||
| ISSUE-20260928-data-dir-doubled | workspace / data/dev/data/dev / DataDir | 出现双层 `data/dev/data/dev/workspaces` 且只有孤立 output.md,写入点未证实 | medium | open | — | 2026-09-28;`intake_1790565513115586191` 双路径对照 | [主记录](../ops/short-term/2026-09-28-quality-gate-heading-mismatch.md#issue-20260928-data-dir-doubled) | 本轮不修 |
|
||||
| ISSUE-20260928-progress-query-trigger | intent / matchTriggerTask / QUERY_PROGRESS / 编写进度 | 询问调研报告编写进度时,写动词加任务名词被识别成启动新任务,进度回复不读真实 run | medium | verifying | Cursor | 2026-09-28;「调研报告编写进度怎么样了」单测为 QUERY_PROGRESS;「开始做调研报告」仍为 research。浏览器复验见测试报告 | [主记录](../ops/short-term/2026-09-28-refresh-task-state-recovery.md) | 疑问进度句必须让行查询;显式「开始/重新」仍启动任务 |
|
||||
| ISSUE-20260928-step-empty-output | squad / stepOutputAccepted / output_digest / e3b0c442 / MaterializeTargets | 步骤已把正文写到 output.md,判定仍因 runner Success=false 或空 patch 记空输出失败 | high | verifying | Cursor | 2026-09-28;11:27 `research_1790566028724181453` 的 06_draft 盘上 41KB 但 digest 为空;单测覆盖缺交付物但盘上有正文,以及连接错误仍失败关闭 | [主记录](../ops/short-term/2026-09-28-refresh-task-state-recovery.md) | 传输/可重试失败留下的文件可能是截断的,不能当成功;缺交付物且盘上非空才接受 |
|
||||
| ISSUE-20260928-task-write-verb | intent / hasTaskWriteVerb / 帮我定 / 制定 / AMBIGUOUS_FALLBACK | 「帮我定/制定」设计开发计划任务书未进入任务触发,对话只有掂量阶段模板 | medium | resolved | Cursor | 2026-09-28;test sess-p8-u33 msg 89/90;意图单测 | [主记录](../ops/short-term/2026-09-28-test-user-doc-issues.md) | [LESSON-20260928-task-write-verb-visible-refusal](../ops/lessons/2026-09-28-task-write-verb-visible-refusal.md) |
|
||||
| ISSUE-20260928-workflow-scope-reply | coordinator / ClarificationQuestion / ErrPrerequisiteNotMet / rd_design_plan | 跨工作流澄清与研发前置未满足时,原因没有进入对话 | medium | resolved | Cursor | 2026-09-28;协调器单测。test 原例停在动词未命中,未走到本分支 | [主记录](../ops/short-term/2026-09-28-test-user-doc-issues.md) | [LESSON-20260928-task-write-verb-visible-refusal](../ops/lessons/2026-09-28-task-write-verb-visible-refusal.md) |
|
||||
| ISSUE-20260928-quality-failed-success-copy | bizdev / quality_failed / 正文已保存至 / output.md | 质量未通过仍声称正文已保存,并写出工作区绝对路径 | medium | resolved | Cursor | 2026-09-28;test cdo_quote_1790560155851280170;同步回复单测 | [主记录](../ops/short-term/2026-09-28-test-user-doc-issues.md) | [LESSON-20260928-task-write-verb-visible-refusal](../ops/lessons/2026-09-28-task-write-verb-visible-refusal.md) |
|
||||
| ISSUE-20260928-pi-runner-data-dir | agent / PI runner / DATA_DIR / MISSING_TARGET / 18866 | test 启动时根 .env 的 dev runner 地址盖过本环境 sidecar,工作区被重写到空目录 | high | resolved | Cursor | 2026-09-28;dev `4f581306`。当时 test 二进制仍是修复前制品 | [验收报告](../test-reports/2026-09-28-pi-runner-env-mismatch-test-report.md) | — |
|
||||
| ISSUE-20260928-material-chunk-embed | knowledge / chunk index failed / 8192 / 34635 tokens | 材料分块批超过嵌入模型上下文,索引失败 | medium | open | — | 2026-09-28;test 日志 project 8,文件 W020251117353305579317.docx | [主记录](../ops/short-term/2026-09-28-test-user-doc-issues.md) | 本轮不修 |
|
||||
| ISSUE-20260928-attachment-only-turn | chat / chat/stream / message cannot be empty / HTTP_400 / attachments | 只上传文档、不输入文字时 turn 被空消息 400 拒绝,前端不读信封,用户看不到任何回复 | high | resolved | Cursor | 2026-09-28;dev :8080 重载后浏览器仅附件得到梳理审阅,无关文字先回答再追问;pending 分支 API 200 | [主记录](../ops/short-term/2026-09-28-attachment-only-turn.md) | 空消息门禁要放在附件判定之后;4xx 必须读信封。见[测试报告](../test-reports/2026-09-28-attachment-only-turn-test-report.md) |
|
||||
| ISSUE-20260928-task-run-replay-502 | web / useRecoveredTaskPhases / tasks/runs / ERR_HTTP2_PROTOCOL_ERROR / 502 | 历史失败气泡恢复订阅 task run 事件时出现 HTTP/2 协议错误和 502,与附件空消息 400 无因果关系 | medium | investigating | Cursor | 2026-09-28;用户控制台 `run_1790558010622744831`;本轮未改订阅逻辑 | [主记录](../ops/short-term/2026-09-28-attachment-only-turn.md#范围外) | 未修,保持观察 |
|
||||
| ISSUE-20260928-task-run-replay-502 | web / useRecoveredTaskPhases / tasks/runs / ERR_HTTP2_PROTOCOL_ERROR / 502 | 历史失败气泡恢复订阅 task run 事件时出现 HTTP/2 协议错误和 502,与附件空消息 400 无因果关系 | medium | verifying | Cursor | 2026-09-28;终态重放改为最多 4 次退避,401/404 立即停止;进行中订阅外层只再补 1 次,避免和内部 40 次重试相乘。尚未在真实 502 上复验 | [主记录](../ops/short-term/2026-09-28-refresh-task-state-recovery.md) | 重放失败要可重试;鉴权与不存在不要空转 |
|
||||
| ISSUE-20260928-activation-preview-precheck | web / projectSettings / activation-preview / NO_GOVERNED_DOCUMENT / clientDutyIssues | 全部文档未纳入人工审批时前端仍 POST activation-preview,控制台 400 | medium | resolved | Cursor | 2026-09-28;dev 项目 6023 默认计划无 activation-preview 请求,兼任后 preview 200 | [主记录](../ops/short-term/2026-09-28-activation-preview-precheck.md#issue-20260928-activation-preview-precheck) | [LESSON-20260928-activation-preview-client-gate](../ops/lessons/2026-09-28-activation-preview-client-gate.md) |
|
||||
| ISSUE-20260928-admin-secret-password-form | web / EnvSettingsSection / password / form / secrets | 管理台 5 个密钥输入不在 form 内,Chromium 报 password DOM 警告 | low | resolved | Cursor | 2026-09-28;dev 模型与密钥页 passwordInputs=5 outsideForm=0 | [主记录](../ops/short-term/2026-09-28-activation-preview-precheck.md#issue-20260928-admin-secret-password-form) | 密码框放进 form;autocomplete 不能单独消除该 DOM 警告 |
|
||||
| ISSUE-20260928-squad-commit-authority | bizdev / docflow / dutyAuthorizer / uow permission denied / OnBehalfOf / bd_intake | 非编制人触发受控小队任务时,账本提交用触发者身份,被编制人职责门拒绝,正文无法落盘 | high | fixing | Cursor | 2026-09-28;dev task_runs user 74 ≠ author 4(doc_proj6022_bd_intake);单测已绿,dev 重跑未做 | [主记录](../ops/short-term/2026-09-28-squad-commit-authority.md#issue-20260928-squad-commit-authority) | [LESSON-20260928-squad-commit-as-author](../ops/lessons/2026-09-28-squad-commit-as-author.md) |
|
||||
| ISSUE-20260928-register-internal | auth / register / role-guest / system_roles / 23503 / 23505 / INTERNAL | test 缺 role-guest 时注册 FK 失败被脱敏为 INTERNAL;重复用户名唯一冲突同样 500 | high | resolved | Cursor | 2026-09-28;test:8081 补种并重启后注册 200(goose 77,req_regfix_after_restart);重复用户名 400 由单测覆盖,rc 二进制仍为 INTERNAL | [主记录](../ops/short-term/2026-09-28-auth-register-internal.md#issue-20260928-register-internal) | [LESSON-20260928-standard-role-reseed](../ops/lessons/2026-09-28-standard-role-reseed.md) |
|
||||
| ISSUE-20260928-squad-commit-authority | bizdev / docflow / dutyAuthorizer / uow permission denied / OnBehalfOf / bd_intake | 非编制人触发受控小队任务时,账本提交用触发者身份,被编制人职责门拒绝,正文无法落盘 | high | resolved | Cursor | 2026-09-28;dev `b1ed8ecf` 重跑 `run_1790559088975684237` completed,commit actor 4,snapshot actor 74 | [主记录](../ops/short-term/2026-09-28-squad-commit-authority.md#issue-20260928-squad-commit-authority) | [LESSON-20260928-squad-commit-as-author](../ops/lessons/2026-09-28-squad-commit-as-author.md) || ISSUE-20260928-register-internal | auth / register / role-guest / system_roles / 23503 / 23505 / INTERNAL | test 缺 role-guest 时注册 FK 失败被脱敏为 INTERNAL;重复用户名唯一冲突同样 500 | high | resolved | Cursor | 2026-09-28;test:8081 补种并重启后注册 200(goose 77,req_regfix_after_restart);重复用户名 400 由单测覆盖,rc 二进制仍为 INTERNAL | [主记录](../ops/short-term/2026-09-28-auth-register-internal.md#issue-20260928-register-internal) | [LESSON-20260928-standard-role-reseed](../ops/lessons/2026-09-28-standard-role-reseed.md) |
|
||||
| ISSUE-20260928-admin-reset-ui-removed | web / UsersSection / 重置密码 / F4-A / adminResetPassword | F4-A 在 F4-B 端点已合入的基线上删除管理台重置密码按钮,管理员无法重置口令 | high | resolved | Cursor | 2026-09-28;dev API :8080 + worktree Vite :3110 真实 UI 探针 PASS(临时口令长度 16、强制改密仅一次、旧口令 401) | [主记录](../ops/short-term/2026-09-28-restore-admin-password-reset.md#issue-20260928-admin-reset-ui-removed) | [LESSON-20260928-ui-dead-control](../ops/lessons/2026-09-28-ui-dead-control.md) |
|
||||
| ISSUE-20260927-upload-doc-cross-device | freedoc / upload / freeform / fileMeta / 占位模板 / 跨端 | 上传文档源文件元数据与正文不落服务端:换浏览器/清存储/共享他人查看时退化为注册占位模板,文本类连二进制都未存(伴生 <512B 显示 "0 KB") | high | resolved | pi | 2026-09-27;dev `86efba2c` 合入并在 dev 环境验证:FS-UPLOAD-01/02 真实浏览器 PASS(跨上下文可见真实正文),DB 核验元数据+asset 落库;存量无元数据旧文档不可恢复(重新上传即可) | [主记录](../ops/short-term/2026-09-27-upload-doc-access.md) | [LESSON-20260927-server-facts-and-iso-db-recipe](../ops/lessons/2026-09-27-server-facts-and-iso-db-recipe.md) |
|
||||
| ISSUE-20260923-nullable-duty | docgov / nullable / 23503 / reviewer_id | 可选职责空值写 0 导致 FK 失败 | high | resolved | Codex(历史补录) | 2026-09-17;历史 PG / API 冒烟;当前未复测 | [主记录](2026-09-23-historical-issue-records.md#nullable-duty) | [LESSON-20260923-nullable-duty](../ops/lessons/2026-09-23-nullable-duty.md) |
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: "U1 基线清单与主干同步记录"
|
||||
date: 2026-09-29
|
||||
task_id: multi-repo-design-2026-09-28
|
||||
---
|
||||
|
||||
# U1 基线盘点与同步结果
|
||||
|
||||
## 备份
|
||||
|
||||
- 全量 bundle:`~/projects/agent-system-all-2026-09-29.bundle`(193M,--all,verify 通过)。
|
||||
|
||||
## 主干同步(D1)
|
||||
|
||||
- 本地 dev(1bb48030)→ 远端 dev:fast-forward `fabd85a6..1bb48030`(337 提交),无分叉无重写。
|
||||
- 补推 11 个缺失 tags(rc-v1.0.0-beta.1–7、rc-v1.1.0-beta.4–7、backup/pre-rewrite-2026-09-13)。
|
||||
- test/main 未动(test=0e4b111,main=210fe4a)。
|
||||
|
||||
## 分支清理(D5)
|
||||
|
||||
- 删除已合入 dev 的 25 个远端陈旧分支(chore/、docs/、feature/pu*、fix/nightly-* 等)。
|
||||
- 保留:dev/test/main、6 个未合入待审分支(integrate-agent-governance-config、
|
||||
integrate-coordination-db-daemon-impl-closeout、promote-task-bubble-bind-test、
|
||||
remove-herdr-agent-settings、software-project-bootstrap、squad-missing-target-retry,
|
||||
均为 2026-09-14~23 遗留,后续逐个审阅收口)、
|
||||
**feature/u9u12-keys-rollout-2026-09-25(活跃 worktree 任务,已推送远端保存)**。
|
||||
- 清理后远端共 10 分支。
|
||||
|
||||
## Worktree 状态(全部保留)
|
||||
|
||||
| 目录 | 状态 | 处置 |
|
||||
|---|---|---|
|
||||
| .worktrees/u9u12-keys-rollout-2026-09-25 | 活跃 git worktree,分支 feature/u9u12-keys-rollout-2026-09-25(f9ce7719),干净 | 保留;分支已推远端 |
|
||||
| .worktrees/full-system-browser-e2e-2026-09-26 | 非 git worktree,仅含 .doc 残留 | 保留不动 |
|
||||
| .worktrees/promote-rc-beta6-closeout-2026-09-28 | 非 git worktree,仅含 .doc 残留 | 保留不动 |
|
||||
|
||||
## 运行制品与治理状态快照
|
||||
|
||||
- 树内跟踪 *.test 二进制 4 个(docgov/notification/projectinstance/storage)——U6 阶段以普通提交移除,不重写历史。
|
||||
- 锁库(.locks/state.sqlite,只读快照):tasks=417、runs=1934、receipts=147、audit_ledger=26682;
|
||||
agentctl status 若干 busy 项 supervisor_alive=dead(陈旧),未清理——U2a 处理,本轮只记录。
|
||||
- 工作区唯一脏文件 `.cursor/rules/agentctl-mandatory.mdc`(删除)已恢复(D6),随 U2a 再删。
|
||||
|
||||
## 远端仓库现状(同步后)
|
||||
|
||||
- Agent-System:dev=1bb48030(=本地),10 分支,无保护(U2b 设置)。
|
||||
- Development-Manager:dev=main=8867e38,本地一致干净。
|
||||
- 4 空仓(Squad/Documents/Guidelines/Releases)原样;Releases-Internal(private)待 U6a 前创建。
|
||||
- governance-drill-2026-09(演练仓,private):已完成 U1.5 全部门禁验证,保留至 U2b 收口。
|
||||
|
||||
## U2b 分支保护状态(2026-09-29 追加)
|
||||
|
||||
PR #1 合并(4d716bcd,agent-bot 作者 / weichen 批准合并)后,Agent-System 启用保护:
|
||||
|
||||
| 分支 | 直推 | 白名单直推 | 合并 | required checks |
|
||||
|---|---|---|---|---|
|
||||
| dev | 禁 | weichen | 仅 weichen 经 PR(需 1 非作者批准+陈旧撤销+最新基线) | CI / go-vet、CI / backup-restore-drill、CI / web (pull_request) |
|
||||
| test | 禁 | weichen | 仅 weichen 经 PR | 无(晋升验证另行补) |
|
||||
| main | 禁 | weichen | 仅 weichen 经 PR | 无 |
|
||||
|
||||
apply_to_admins=true(admin 不旁路)。首个全绿 CI 轮:go-vet ✓、backup-restore-drill ✓、web ✓;
|
||||
短测存量失败按 KTD14 报告不阻断,issue #2 跟踪。
|
||||
|
||||
本地治理钩子(agentctl 强制 with/收据)在主工作树已停用(chmod -x,内容保留);
|
||||
远端准入权威自此为 Gitea 原生保护。本地编辑/提交零 agentctl 依赖。
|
||||
edit A 05:07:06
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "U2 治理交接收口:Gitea 原生门禁取代本地 agentctl 治理"
|
||||
date: 2026-09-29
|
||||
task_id: multi-repo-design-2026-09-28
|
||||
supersedes: agentctl-mandatory 客户端规则与本地租约/收据/候选集成流程
|
||||
---
|
||||
|
||||
# U2 收口记录
|
||||
|
||||
## 交接内容
|
||||
|
||||
| 旧机制(agentctl 本地治理) | 新权威 | 状态 |
|
||||
|---|---|---|
|
||||
| pre-commit/reference-transaction 本地租约与收据 | Gitea 受保护分支(dev/test/main) | 旧钩子文件已删除(原 chmod 停用,现正式退役) |
|
||||
| `agentctl with`/SQLite 写前置 | 无(普通 Git) | 编码流程零 agentctl |
|
||||
| `docs apply` 共享文档 CAS | PR 版本历史 + CI 一致性检查 | CURRENT/MATRIX 转只读快照 |
|
||||
| 候选 `validate`/`queue enqueue`/`integrate` | PR + 必需 CI(go-vet、backup-restore-drill、web)+ 非作者批准 | 旧本地 dev 推进入口退役 |
|
||||
| 需求重叠 catalog 硬门禁 | 降为建议(PR 审核中人工判断) | 不再阻塞写入 |
|
||||
| 收口必跑 cleanup report / docs drift | 移至维护任务/按需 | 不再是会话前置 |
|
||||
|
||||
保留不变的硬边界:凭证/秘密防护(新增 CI secrets 扫描)、共享环境写授权、
|
||||
破坏性操作授权、他人现场保护、证据诚实、产品 IAM/文档职责/DB 事务(产品侧,与本交接无关)。
|
||||
|
||||
## 协作模型(实测验证)
|
||||
|
||||
- 编码 Agent = `agent-bot`(作者):功能分支、提交、建 PR。
|
||||
- 用户 = `weichen`(审核者+合并者):批准、合并;白名单直推保留(不破坏既有习惯)。
|
||||
- Gitea 1.27.3 硬禁止自批准 → 非作者审核由平台强制,非自觉约定。
|
||||
- 完整闭环已跑通:PR #1(U1 文档 + CI 修复)bot 作者 → CI 绿 → weichen 批准 → 合并 4d716bcd。
|
||||
|
||||
## 验收证据
|
||||
|
||||
| 场景 | 结果 |
|
||||
|---|---|
|
||||
| V19 两个独立 clone 同路径并行编辑、各自提交 | 双方均成功,无全局锁牵连(本日实测,分支已清理) |
|
||||
| V20 停 daemon/无本地治理 → 本地开发 | 本会话全部提交零 agentctl 调用;原仓钩子已删除 |
|
||||
| V23 旧入口不能推进远端 | agent-bot 直推 dev 被拒(N6);无批准/陈旧分支/缺 CI 的合并在演练仓全部被拒(N3–N5) |
|
||||
| V26 完整修复→测试→提交→PR→合并零 agentctl | PR #1 全流程即证据;CI 必需检查绿后合并 |
|
||||
| 白名单非破坏 | weichen 直推 dev 放行(P1,4d716bcd..9504b624) |
|
||||
|
||||
## 遗留与后续
|
||||
|
||||
- 短测存量失败(环境依赖用例)→ issue #2,修绿后并入必需门禁。
|
||||
- Development-Manager 中 agentctl 的部署/发布/恢复角色重写(KTD13 裁剪矩阵)→ 后续任务,编码流程已不依赖。
|
||||
- 锁库 `.locks/state.sqlite` 与历史任务记录冻结为只读证据,不再作为权威。
|
||||
- 演练仓 `governance-drill-2026-09` 保留至 U6a 发布链路验证后删除。
|
||||
- 规范文档链(worktree-workflow、policy-registry、validation-contract 等)保留为历史参考;AGENTS.md/PROJECT.md 已按新模型改写并在本 PR 生效。
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title: "U6a 单文件内部 RC 冒烟记录(首个干净环境验证)"
|
||||
date: 2026-09-29
|
||||
task_id: multi-repo-design-2026-09-28
|
||||
binary: agent-system @ 5d961147(33M,CGO_ENABLED=0,内嵌 web/dist)
|
||||
---
|
||||
|
||||
# 结论:U6a 单文件冒烟通过(本地 RC,未发布)
|
||||
|
||||
按 KTD14/V9/V27 部分场景,在**无源码目录**的一次性环境验证:
|
||||
|
||||
| 场景 | 结果 |
|
||||
|---|---|
|
||||
| 构建 | build-single-binary.sh:web 构建(67×2 预压缩资产)+ go build,33M |
|
||||
| `config check`(干净目录) | 通过;DB 目标来自进程 env;秘密仅 `<set>/<unset>` |
|
||||
| `config explain` | instance_id 正确标注 process-env 层 |
|
||||
| 实例引导 | instance-marker.json(rc-smoke,0600)+ instance.lock 创建 |
|
||||
| **内嵌迁移** | 一次性隔离 pgvector 库从零迁至 **schema_version 78**(goose 内嵌链,无源码目录) |
|
||||
| 服务启动 | 监听 :5590,/api/v1/health:database_ready:true、schema_ready:true、task_store_ready:true |
|
||||
| **内嵌 UI** | GET / 返回 SPA index.html(200)——go:embed dist 生效 |
|
||||
| 误重复启动 | 第二进程退出码 1:“instance already running (lock held)” |
|
||||
| 优雅退出 | SIGTERM 后进程退出(drain 逻辑 T20-B7 已在库;本次日志因清理时序未留存,退出行为符合预期) |
|
||||
|
||||
隔离纪律:一次性 PG 容器(5544 端口)用后即删,未触碰真实 dev/test 数据;
|
||||
冒烟目录与二进制已清理。
|
||||
|
||||
## U10 基建汇合状态
|
||||
|
||||
U10a(迁移/提示词/xlsx 内嵌 + config check)、U10b(五层配置栈 + 字段溯源)、
|
||||
U10c(实例身份/单启锁/根校验 + systemd 模板)全部就位并被本冒烟整体消费。
|
||||
优雅退出/readiness 经核实**既有实现已达标**(drain 窗口对齐 AgentTimeout、
|
||||
分量就绪健康端点、先初始化后监听——端口打开即就绪,无需冗余改造)。
|
||||
|
||||
## 未完成 / 后续
|
||||
|
||||
- **RC 未发布**:按用户指令组件仓/发布仓暂不关联 Gitea;二进制仅本地验证。
|
||||
发布需建 Agent-System-Releases-Internal(private)并经授权。
|
||||
- 完整 V9/V27 验收(业务任务、上传/检索、升级/回退、重复发布冲突)待
|
||||
发布阶段在更完整环境执行。
|
||||
- managed-local PG profile、serve/init 子命令分化(KTD16)后续补充。
|
||||
- 版本号方案:本次为无 tag 冒烟构建;正式 RC 走 `vX.Y.Z-rc.N` 命名。
|
||||
@@ -18,6 +18,13 @@
|
||||
|
||||
| owner | task | branch | worktree | mutex | status |
|
||||
|---|---|---|---|---|---|
|
||||
| Cursor | controlled-projection-project-binding-2026-09-28 | fix/controlled-projection-project-binding-2026-09-28 | .worktrees/controlled-projection-project-binding-2026-09-28 | controlled-projection-binding | done |
|
||||
| Cursor | docgov-shared-map-race-2026-09-28-r2 | fix/docgov-shared-map-race-2026-09-28 | .worktrees/docgov-shared-map-race-2026-09-28 | docgov-shared-map-race | done |
|
||||
| Cursor | promote-rc-beta6-closeout-2026-09-28 | feature/promote-rc-beta6-closeout-2026-09-28 | .worktrees/promote-rc-beta6-closeout-2026-09-28 | git:test+env:test | done |
|
||||
| Cursor | quality-gate-heading-deploy-2026-09-28 | fix/quality-gate-heading-mismatch-2026-09-28 | .worktrees/quality-gate-heading-mismatch-2026-09-28 | quality-gate-heading | done |
|
||||
| Cursor | quality-gate-heading-mismatch-2026-09-28-r3 | fix/quality-gate-heading-mismatch-2026-09-28 | .worktrees/quality-gate-heading-mismatch-2026-09-28 | quality-gate-heading | done |
|
||||
| Cursor | nightly-advance-automation-2026-09-28 | feature/nightly-advance-automation-2026-09-28 | .worktrees/nightly-advance-automation-2026-09-28 | nightly-advance | done || Cursor | agent-reply-llm-narrative-2026-09-28 | feature/agent-reply-llm-narrative-2026-09-28 | .worktrees/agent-reply-llm-narrative-2026-09-28 | chat+coordinator | in_progress |
|
||||
| Cursor | test-user-doc-issues-2026-09-28 | fix/test-user-doc-issues-2026-09-28 | .worktrees/test-user-doc-issues-2026-09-28 | intent-write-verb | done |
|
||||
| Cursor | pi-runner-env-mismatch-2026-09-28 | fix/pi-runner-env-mismatch-2026-09-28 | .worktrees/pi-runner-env-mismatch-2026-09-28 | pi-runner-env | done |
|
||||
| Cursor | error-events-pg-roundtrip-2026-09-28 | feature/error-events-pg-roundtrip-2026-09-28 | .worktrees/error-events-pg-roundtrip-2026-09-28 | error-events-pg-evidence | done |
|
||||
| Cursor | error-code-observability-2026-09-28 | feature/error-code-observability-2026-09-28 | .worktrees/error-code-observability-2026-09-28 | error-code-observability | done |
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
lesson_id: LESSON-20260928-docgov-shared-map-race
|
||||
lesson_state: validated
|
||||
owner: Cursor
|
||||
reviewer: Cursor(自审)
|
||||
last_reviewed: 2026-09-28
|
||||
reviewed_revision: b9339cd2
|
||||
source_issues: [ISSUE-20260928-docgov-map-race]
|
||||
keywords: [docgov, memDocs, memMu, per-doc lock, concurrent map, 登录 500]
|
||||
---
|
||||
|
||||
# 拆细粒度锁后,共享 map 仍要单独互斥
|
||||
|
||||
- 触发条件:把全局锁拆成 per-id 锁,但多个 id 仍读写同一张 Go map(缓存、审计、批注、提案)。
|
||||
- 机制与来源:docgov 的 per-doc 锁不保护 `memDocs` 等 map。不同文档并发时 `GetDocument` 与写入撞上,运行时 fatal,进程退出后所有 HTTP API(含登录)500。证据:2026-09-28 `.tmp/launch/server.log` 与 `web.log` 的 `ECONNREFUSED`。修复见 [任务](../short-term/2026-09-28-docgov-shared-map-race.md)。
|
||||
- 开发检查:map 的读写、遍历、append 都走同一把锁。已持有外层锁的路径不能再锁同一把(Go 互斥锁不可重入)。锁序固定,禁止在 map 锁内再拿文档锁或外层锁。不要用「登录 500」去改登录代码。
|
||||
- 验证方式:不同 docID 并发创建与读取,`go test -race` 覆盖 `TestMemMapsNoRaceAcrossDocs`。进程崩溃类问题还要看服务日志里的 fatal,而不是只看单个 API 状态码。
|
||||
- 适用边界:适用于 docgov 这四张内存 map。文档结构体字段的数据竞争、以及持久化失败后的缓存投毒(`ISSUE-20260926-docgov-memdocs-poison`)不在本条范围内。
|
||||
- 防护落实:`memMu` 与 `TestMemMapsNoRaceAcrossDocs`。没有新增静态检查,漏一个裸访问仍要靠 `-race` 和复查。
|
||||
- 修订与失效:若这四张 map 改为分片或移除,本条对那个存储失效,保留历史。
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
lesson_id: LESSON-20260928-material-noun-not-sales-template
|
||||
lesson_state: validated
|
||||
owner: Cursor
|
||||
reviewer: Cursor(自审)
|
||||
last_reviewed: 2026-09-28
|
||||
reviewed_revision: 8f77053f5169502eaf3e4e4135a082e8914f3704
|
||||
source_issues: [ISSUE-20260928-material-template-reply]
|
||||
keywords: [intent, 说明书, 操作视频, 竞品, AMBIGUOUS_FALLBACK, 掂量, fallback_reason]
|
||||
---
|
||||
|
||||
# 资料名词不要掉进阶段推销模板
|
||||
|
||||
- 触发条件:用户用名词短语索取说明书、视频、彩页、手册、上一代或竞品资料,句子里没有「生成/编写/撰写」。
|
||||
- 机制与来源:低置信保底会进模糊咨询;模糊咨询原先对非问句直接输出阶段对照。对话模型建目录或执行失败时若只返回空字符串,模板会原样留下,日志里看不到原因。见 [任务](../short-term/2026-09-28-intent-material-fallback.md)。
|
||||
- 开发检查:资料获取走知识检索,未命中再走对话模型;犹豫征询才保留阶段对照。模型早退必须打日志,并把 `fallback_reason` 写入 `reply_source=template` 的 metadata。写动词不要误判成检索。
|
||||
- 验证方式:原始输入单测不得再是 `AMBIGUOUS_FALLBACK`;无模型时回复不得含「掂量」;真实产品回合的 `reply_source` 为 `pi-agent` 或 `kb`。
|
||||
- 适用边界:适用于「把已有资料找出来」。用户明确要编写新文档时仍走任务触发。阶段推进失败后没有助手消息是另一问题(ISSUE-20260928-advance-stage-silent-fail),本条不覆盖。
|
||||
- 防护落实:manual,另有 intent / coordinator / chat 单测。2026-09-28 在 dev 库、dev runner、工作树二进制上确认 `reply_source=kb`(项目 6024)。:8080 二进制尚未替换。
|
||||
- 修订与失效:若产品改为资料请求一律进小队任务,本条的检索分流不再适用,保留历史。
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
lesson_id: LESSON-20260928-quality-heading-normalize
|
||||
lesson_state: candidate
|
||||
owner: Cursor
|
||||
reviewer: Cursor(自审,待 dev 重跑调研报告后升 validated)
|
||||
last_reviewed: 2026-09-28
|
||||
reviewed_revision: 434d542c3cbdbeac38b9c0e547edf75160acf577
|
||||
source_issues: [ISSUE-20260928-quality-heading-drift]
|
||||
keywords: [quality_gate, 缺少必填章节, State-of-the-Art, H3, AutoRepair]
|
||||
---
|
||||
|
||||
# 章节门禁比对标题,不比对空格和层级
|
||||
|
||||
- 触发条件:交付正文章节都在,质量报告仍写「缺少必填章节」,评分往往停在 85。
|
||||
- 机制与来源:模板 H2 与模型常写的 H3、多一个或少一个空格、全角字母,在精确字符串比较下会被当成整章缺失。缺章会短路受控投影。见 ISSUE-20260928-quality-heading-drift。
|
||||
- 开发检查:标题比对先去空白、折全角、忽略大小写,并允许 H2–H4。只在归一化后确实没有该标题时判缺失。层级不一致记软警告,不扣分。不要把「待确认」或材料不足算进这道门禁。
|
||||
- 验证方式:`TestQualityGate_Validate_HeadingLevelAndSpaceDrift` 同时覆盖漂移通过和真实缺章失败。聊天触发须带 `AutoRepair`。
|
||||
- 适用边界:只覆盖 ATX 标题存在性。近义改名、章节被写进代码块、内容空洞仍不在本条范围内。
|
||||
- 防护落实:`internal/squad/quality_gate.go` 与 `quality_gate_test.go`。
|
||||
- 修订与失效:若产品要求章节层级必须与模板一致才可交付,应把软警告改回硬失败,并同步改回归用例。
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
lesson_id: LESSON-20260928-squad-commit-as-author
|
||||
lesson_state: candidate
|
||||
lesson_state: validated
|
||||
owner: Cursor
|
||||
reviewer: Cursor(自审,待 dev 重跑后升 validated)
|
||||
reviewer: Cursor(自审,dev 原例已复跑)
|
||||
last_reviewed: 2026-09-28
|
||||
reviewed_revision: 774df0599813fb8d7115f95ce255384542d6a0f9
|
||||
reviewed_revision: b1ed8ecfe9fc5849288b6febf2bb4780319c8aca
|
||||
source_issues: [ISSUE-20260928-squad-commit-authority]
|
||||
keywords: [bizdev, docflow, dutyAuthorizer, AutoCommitUserID, OnBehalfOf, uow permission denied, 非编制人]
|
||||
---
|
||||
@@ -14,7 +14,7 @@ keywords: [bizdev, docflow, dutyAuthorizer, AutoCommitUserID, OnBehalfOf, uow pe
|
||||
- 触发条件:受控文档小队任务成功生成正文,但落盘报 `uow: permission denied` 或「版本账本/变更报告落盘失败」。
|
||||
- 机制与来源:`dutyAuthorizer` 对 `ActionCommit` 要求 principal 等于 `document_records.author_id`。触发者常常是其他项目成员。见 ISSUE-20260928-squad-commit-authority。
|
||||
- 开发检查:自动提交的 `AutoCommitUserID` 用文档编制人;实际触发用户写入 `OnBehalfOf`。不要为了小队路径放宽编制人校验。触发者或编制人为 0 时不要伪造代理身份。权限失败文案要写明「非编制人」,同时保留 `uow.ErrPermissionDenied` 哨兵。
|
||||
- 验证方式:非编制人触发应成功且 `OnBehalfOf` 为触发者;编制人触发不设置 `OnBehalfOf`;非编制人直接 `ActionCommit` 仍拒绝。关联 `internal/bizdev/squad_commit_actor_test.go` 与 `TestDutyAuthorizerAllowsDocumentAuthorCommit`。dev 原例重跑完成前本条保持 candidate。
|
||||
- 验证方式:非编制人触发应成功且快照 `actor_user_id` 为触发者,变更事件 `actor_user_id` 为编制人;编制人触发不设置 `OnBehalfOf`;非编制人直接 `ActionCommit` 仍拒绝。dev 原例 `run_1790559088975684237` 已 PASS,见测试报告 `2026-09-28-squad-commit-authority-test-report.md`。
|
||||
- 适用边界:只覆盖小队/agent 自动提交。人工编辑、批准、驳回仍按各自职责人校验。高风险候选仍要编制人确认。
|
||||
- 防护落实:上述单测已落地。原例验收是 manual(dev 非编制人重跑)。
|
||||
- 防护落实:单测在 `internal/bizdev/squad_commit_actor_test.go` 与 `TestDutyAuthorizerAllowsDocumentAuthorCommit`。dev 原例已用真实 pi-agent 重跑,见测试报告。没有新增 CI 门禁。
|
||||
- 修订与失效:若产品改为「非编制人不得触发生成」,本条应退役并改回触发者身份。
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
lesson_id: LESSON-20260928-task-write-verb-visible-refusal
|
||||
lesson_state: candidate
|
||||
owner: Cursor
|
||||
reviewer: Cursor(自审,待含本提交的运行制品复验原句)
|
||||
last_reviewed: 2026-09-28
|
||||
reviewed_revision: db859e185ebcd0e7eccd2241ca07f0e9f4fe4c6b
|
||||
source_issues: [ISSUE-20260928-task-write-verb, ISSUE-20260928-workflow-scope-reply, ISSUE-20260928-quality-failed-success-copy]
|
||||
keywords: [intent, hasTaskWriteVerb, 帮我定, ClarificationQuestion, ErrPrerequisiteNotMet, quality_failed]
|
||||
---
|
||||
|
||||
# 任务写动词要覆盖「定」,拒绝原因要进对话
|
||||
|
||||
- 触发条件:用户说「帮我定/制定/拟定」某交付物,对话却进入「再掂量一下」;或任务被工作流范围、研发前置、质量门禁挡住,气泡仍像成功或只有协调失败。
|
||||
- 机制与来源:强写动词表原先只有生成/做/跑/出/编写/编制/起草。「定」单独出现会误伤「决定」,所以只认「制定」「拟定」以及「帮我/请/给我/帮忙」后面的「定/拟」。跨流澄清和前置失败必须成为用户可见回复,不能再被阶段模板或错误事件盖掉。质量未通过不要写宿主机路径。见 ISSUE-20260928-task-write-verb。
|
||||
- 开发检查:新增交付动词时补强动词或祈使短语,并加一条反例(决定、看看)。`RequiresClarifying` 的问题原文要到达会话。`ErrPrerequisiteNotMet` 的中文原因用拒绝态返回,错误值为空,避免会话层丢掉 `NaturalReply`。
|
||||
- 验证方式:`go test ./internal/intent/ ./internal/coordinator/` 中写动词、跨流澄清、前置可见、质量失败文案四类用例。原句在运行制品上的复验完成前,本条保持 candidate。
|
||||
- 适用边界:只覆盖中文任务触发与同步回复。材料嵌入分块超限、PI runner 数据目录错配是相邻但独立的问题。
|
||||
- 防护落实:`doc_workflow_scope_test.go` 与 `pending_confirmation_reply_test.go`。
|
||||
- 修订与失效:若产品明确「定」永不表示编写,应删祈使短语并退役本条。
|
||||
@@ -4,9 +4,12 @@
|
||||
|
||||
| lesson_id | 触发条件 / 关键词 | 注意事项 | lesson_state | 主记录 / 最近复核 |
|
||||
|---|---|---|---|---|
|
||||
| LESSON-20260928-material-noun-not-sales-template | 说明书、操作视频、竞品、掂量、AMBIGUOUS_FALLBACK、fallback_reason | 资料名词走检索或对话模型;阶段推销只留给犹豫征询;模型早退要留原因 | validated | [资料名词不吃推销模板](2026-09-28-material-noun-not-sales-template.md);2026-09-28 |
|
||||
| LESSON-20260928-docgov-shared-map-race | docgov、memDocs、per-doc 锁、concurrent map、登录 500 | 拆锁后共享 map 仍要单独互斥;锁不可重入;全站 500 先看进程是否退出 | validated | [共享 map 互斥](2026-09-28-docgov-shared-map-race.md);2026-09-28 |
|
||||
| LESSON-20260928-quality-heading-normalize | 缺少必填章节、State-of-the-Art、H3、质量门禁、AutoRepair | 标题比对去空白并跨 H2–H4;层级漂移不扣分;真实缺章仍失败 | candidate | [章节标题归一化](2026-09-28-quality-heading-normalize.md);2026-09-28 |
|
||||
| LESSON-20260928-task-write-verb-visible-refusal | 帮我定、制定、ClarificationQuestion、前置条件、quality_failed | 写动词覆盖制定与帮我定;拒绝和澄清进对话;质量失败不写宿主机路径 | candidate | [写动词与可见拒绝](2026-09-28-task-write-verb-visible-refusal.md);2026-09-28 |
|
||||
| LESSON-20260928-activation-preview-client-gate | activation-preview、NO_GOVERNED_DOCUMENT、clientDutyIssues、400 | 服务端激活门禁要有同等前端预检;live_edit 不套激活门禁 | validated | [激活预检对齐](2026-09-28-activation-preview-client-gate.md);2026-09-28 |
|
||||
| LESSON-20260928-squad-commit-as-author | 小队落盘、uow permission denied、AutoCommitUserID、OnBehalfOf、非编制人 | 自动提交身份用文档编制人,触发者只写入 OnBehalfOf;不要放宽 ActionCommit | candidate | [编制人代理提交](2026-09-28-squad-commit-as-author.md);2026-09-28 |
|
||||
| LESSON-20260928-standard-role-reseed | 注册、role-guest、system_roles、is_system、23503、23505、INTERNAL | 硬编码标准角色须幂等补种并标 is_system;用户名冲突返回领域 400,不要包进 insert user | validated | [标准角色可自愈](2026-09-28-standard-role-reseed.md);2026-09-28 |
|
||||
| LESSON-20260928-squad-commit-as-author | 小队落盘、uow permission denied、AutoCommitUserID、OnBehalfOf、非编制人 | 自动提交身份用文档编制人,触发者只写入 OnBehalfOf;不要放宽 ActionCommit | validated | [编制人代理提交](2026-09-28-squad-commit-as-author.md);2026-09-28 || LESSON-20260928-standard-role-reseed | 注册、role-guest、system_roles、is_system、23503、23505、INTERNAL | 硬编码标准角色须幂等补种并标 is_system;用户名冲突返回领域 400,不要包进 insert user | validated | [标准角色可自愈](2026-09-28-standard-role-reseed.md);2026-09-28 |
|
||||
| LESSON-20260928-ui-dead-control | 死按钮、端点未开通、UsersSection、重置密码、merge-base | 删除「未开通」控件前用当前基线核对路由已存在;注释不能代替 merge-base | validated | [删控件前核对端点](2026-09-28-ui-dead-control.md);2026-09-28 |
|
||||
| LESSON-20260927-server-facts-and-iso-db-recipe | 上传持久化、fileMeta、占位模板、config.Load 默认 DSN、TEST_DATABASE_URL、隔离库、RefuseSharedEnvDSN、goose 顺序 | 服务端事实源优先(本地仅缓存);worktree 测试必须显式钉隔离库并避开 5433/5434/5435 子串端口;隔离库配方=扩展→goose→init SQL,重组件用例先做基线四格对照 | validated | [服务端事实源与隔离库配方](2026-09-27-server-facts-and-iso-db-recipe.md) |
|
||||
| LESSON-20260923-nullable-duty | docgov、SQL、nullable、23503、可选职责、创建/heal | 检查所有写入口的缺席值语义;用真实 PG 验证 NULL/FK | validated | [可选职责持久化](2026-09-23-nullable-duty.md);2026-09-23 |
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: "助手回复智能体叙述优先"
|
||||
task_id: "agent-reply-llm-narrative-2026-09-28"
|
||||
status: in_progress
|
||||
created: 2026-09-28
|
||||
updated: 2026-09-28
|
||||
owner: Cursor
|
||||
task_type: implementation
|
||||
task_tier: multi_stage
|
||||
policy_revision: "agent-governance-2026-09-21-r1 @ 8f77053f5169502eaf3e4e4135a082e8914f3704"
|
||||
branch: feature/agent-reply-llm-narrative-2026-09-28
|
||||
worktree: .worktrees/agent-reply-llm-narrative-2026-09-28
|
||||
base_dev_sha: "8f77053f5169502eaf3e4e4135a082e8914f3704"
|
||||
write_scope: ["internal/coordinator", ".doc"]
|
||||
allowed_environments: ["dev"]
|
||||
head_sha: ""
|
||||
mutex: "chat+coordinator"
|
||||
review_state: pending
|
||||
integration_state: pending
|
||||
deployment: pending
|
||||
cleanup: pending
|
||||
cleanup_owner: "独立维护者"
|
||||
cleanup_trigger: "dev 合入且验收证据落盘后复查 worktree"
|
||||
remote_sync: not_requested
|
||||
---
|
||||
|
||||
# 助手回复智能体叙述优先
|
||||
|
||||
## 用户目标与授权
|
||||
|
||||
- 用户目标:助手回复尽可能由智能体生成,减少模板直答。授权来自本会话「实施助手回复智能化改造方案」。
|
||||
- 交付终点:T1/T2 叙述与 T3 结果通报走 PI Agent,失败回退模板;T4 安全话术保持模板;单测与 dev 真实 Runner 证据落盘后合入 local dev。
|
||||
- 禁止项:不 push、不晋升 test/main、不改确认卡结构与权限拒绝话术。
|
||||
- 例外:注册本任务前,`integrate-attachment-only-turn-2026-09-28` 以 scope `.` 仍为 active,但其分支已是当时 dev 祖先且队列为空。已将其 task state 记为 done 以解除 same_scope 硬门禁。未删除其 worktree。
|
||||
|
||||
## 范围与非目标
|
||||
|
||||
- 允许写入:`internal/coordinator` 叙述管道与意图 handler;本任务记录与测试报告。
|
||||
- 非目标:不改交互卡、挂起期望、澄清追问、权限不足与安全拦截文案的生成路径;不替换 `templates/docs` 交付物模板。
|
||||
|
||||
## 相关问题与经验
|
||||
|
||||
- issue_ids:无对应未关闭问题。
|
||||
- 开工检索 ISSUES / lessons INDEX(模板回复、答非所问、reply_source):无命中可复用条目。
|
||||
- 不新增 lesson:本次是既有「LLM 优先、模板兜底」机制的扩展,尚未出现新的复发机制。
|
||||
|
||||
## 步骤与验收
|
||||
|
||||
1. [x] 统一 `narrateReply` / `applyNarrative`,事实块进入提示词,开关 `AGENT_REPLY_LLM_ENABLED`。
|
||||
2. [x] T1 查询、T2 帮助、T3 已受理/已完成通报接入;确认提问与权限拒绝不接入。
|
||||
3. [x] `go test -short ./...`、`go vet`、`arch-check --mode block`,以及 `RUN_PI_E2E=1` 的真实 Runner 叙述用例。验收见 [测试报告](../../test-reports/2026-09-28-agent-reply-llm-narrative-test-report.md)。
|
||||
|
||||
按 [验证契约](../2026-09-21-validation-contract.md):Go 单测 + 产品 PI 受影响链路。
|
||||
|
||||
## 验证与证据
|
||||
|
||||
| 范围 / 被测 SHA | 命令与环境 | 预期 | 实际 / 判定 | 证据 / 脱敏说明 |
|
||||
|---|---|---|---|---|
|
||||
| coordinator 单测与全量 short | `go test -short ./...`;`arch-check --mode block`;`go vet ./...` | PASS | PASS | 见测试报告 |
|
||||
| 真实 Runner T1/T2/T3 | `RUN_PI_E2E=1 go test -run TestIntentReply_RealPI`,模型 MiniMax-M2.7-highspeed | reply_source=pi-agent | PASS(26.90s) | [测试报告](../../test-reports/2026-09-28-agent-reply-llm-narrative-test-report.md);未记录 token |
|
||||
|
||||
## 收口与交接
|
||||
|
||||
- 剩余工作:真实 Runner 证据、合入 local dev。
|
||||
- 本地提交 / dev 集成 / 部署 / 远端:尚未提交。
|
||||
- cleanup:合入后由独立维护者复查。
|
||||
- 需求目录:`R-agent-reply-llm-narrative`。
|
||||
|
||||
## 相关链接
|
||||
|
||||
- 方案:助手回复智能化改造(LLM 叙述优先、模板兜底)
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
title: "修复调研报告受控投影断链"
|
||||
task_id: controlled-projection-project-binding-2026-09-28
|
||||
status: in_progress
|
||||
created: 2026-09-28
|
||||
updated: 2026-09-28
|
||||
owner: Cursor
|
||||
task_type: fix
|
||||
policy_revision: "AGENTS.md + agent-worktree-workflow @ 2026-09-21"
|
||||
branch: fix/controlled-projection-project-binding-2026-09-28
|
||||
worktree: .worktrees/controlled-projection-project-binding-2026-09-28
|
||||
base_dev_sha: "5d762fb2061113285ee21219471baaf11cb2d770"
|
||||
write_scope:
|
||||
- internal/server
|
||||
- internal/coordinator
|
||||
- internal/bizdev
|
||||
- .doc/ops/CURRENT.md
|
||||
- .doc/ops/short-term/2026-09-28-controlled-projection-project-binding.md
|
||||
- .doc/test-reports/2026-09-28-controlled-projection-project-binding-test-report.md
|
||||
mutex: controlled-projection-binding
|
||||
review_state: pending
|
||||
integration_state: branch_only
|
||||
deployment: not_loaded
|
||||
cleanup: pending
|
||||
remote_sync: not_requested
|
||||
---
|
||||
|
||||
# 修复调研报告受控投影断链
|
||||
|
||||
## 用户目标与授权
|
||||
|
||||
用户在项目会话里让助手编制调研报告。助手回复已生成并保存(含耗时与基准假设),刷新文档页仍是模板骨架。授权范围是修复受控投影断链:回填项目绑定、会话可追溯、无绑定时失败关闭、回复与账本状态一致。不改前端骨架回退,不删除工作区,不晋升环境,不 push。
|
||||
|
||||
## 检索
|
||||
|
||||
- 问题台账未另开新条;本任务记录根因与修复。catalog overlap 探测为 none。登记时未带 mutex/intent,避免与持有全仓范围 `.` 的夜间推进任务发生 same_scope 拒绝。
|
||||
- 经验闭环:本轮采纳「回复口径必须跟受控账本,而不是跟小队成功」。
|
||||
|
||||
## 根因
|
||||
|
||||
聊天入口只 `SetProjectID` 做访问校验,`UserCtx.Project.ID` 一直为空。协调器 `handleTriggerTask` 用后者解析 projectID,得到 0。`canonicalDocID` 因此返回空串,bizdev 整段跳过 docgov/docrevision 投影。`output.md` 只落在隔离工作区,回复仍写「正文已保存」。
|
||||
|
||||
证据工作区:`research_1790576535990100562`(project_id=0、session_id 空、completed)。`doc_proj6024_bd_research` 在 07:06 带绑定重跑前只有模板快照。随后 `research_1790584772615062606`(失败)和 `research_1790585141260333084`(completed)仍是 project_id=0。
|
||||
|
||||
## 修复
|
||||
|
||||
提交 `f8730791`:
|
||||
|
||||
- `bindSessionProjectContext`:`enrichSessionWorkflowStage` 在项目已授权且上下文项目 ID 为空时回填。已有绑定不覆盖。
|
||||
- `handleTriggerTask` / `handleTriggerAdhocTask` 把 `ChatSessionID` 写入 `TaskRunRequest.SessionID`。
|
||||
- 受控目录任务(目录键与 `rd_*`)在 `ProjectID <= 0` 时返回「会话未绑定项目,受控交付物无法入账」,不建孤儿工作区。未知自由任务不在此列。
|
||||
- 完成回复只在 `DocRecord` 有文档 ID 时称已写入受控文档;否则说明交付物在工作区、尚未入账。`CompletionSummary` 对受控键同样区分。
|
||||
|
||||
## 数据
|
||||
|
||||
`doc_proj6024_bd_research` 已由 07:06 带绑定重跑入账,不做数据修复。
|
||||
|
||||
孤儿工作区只登记、不删除:
|
||||
|
||||
| 路径 | cleanup | 原因 |
|
||||
|---|---|---|
|
||||
| `data/dev/workspaces/bizdev/research_1790576535990100562` | pending | project_id=0 的已完成调研,正文未入账 |
|
||||
| `data/dev/workspaces/bizdev/research_1790585141260333084` | pending | 修复前再次以 project_id=0 完成 |
|
||||
|
||||
cleanup_owner:本任务。复查触发:合入 local dev 并确认新会话不再产生 project_id=0 的受控工作区之后,由独立维护者决定是否归档。
|
||||
|
||||
## 验证
|
||||
|
||||
见 [测试报告](../../test-reports/2026-09-28-controlled-projection-project-binding-test-report.md)。
|
||||
|
||||
## 五态
|
||||
|
||||
| 字段 | 值 |
|
||||
|---|---|
|
||||
| review_state | pending |
|
||||
| integration_state | branch_only |
|
||||
| deployment | not_loaded(运行中的 dev 二进制早于本提交) |
|
||||
| cleanup | pending |
|
||||
| remote_sync | not_requested |
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: "docgov 共享 map 并发读写导致进程崩溃"
|
||||
status: done
|
||||
created: 2026-09-28
|
||||
owner: Cursor
|
||||
task_id: docgov-shared-map-race-2026-09-28-r2
|
||||
policy_revision: issue-learning-2026-09-23-r1 @ a3db288a
|
||||
review_state: accepted
|
||||
integration_state: merged_local_dev
|
||||
deployment: loaded_local
|
||||
cleanup: pending
|
||||
remote_sync: not_requested
|
||||
updated: 2026-09-28
|
||||
write_scope: internal/docgov .doc/ops/CURRENT.md .doc/ops/short-term/2026-09-28-docgov-shared-map-race.md .doc/test-reports/2026-09-28-docgov-shared-map-race-test-report.md .doc/audit/ISSUES.md .doc/ops/lessons/2026-09-28-docgov-shared-map-race.md .doc/ops/lessons/INDEX.md
|
||||
mutex: docgov-shared-map-race
|
||||
base_dev_sha: a3db288a7aa055ff5884e3aa4d91511959cff721
|
||||
---
|
||||
|
||||
# docgov 共享 map 并发读写
|
||||
|
||||
## 范围与授权
|
||||
|
||||
用户报告 dev(`frp-mix.com:58880`)登录及多个 API 返回 500,并说明没有改登录代码。授权是查清原因并修复。允许写路径见 front matter。交付终点:共享 map 并发读写有回归测试,合入 local dev 后重建并复验 `/health` 与登录接口不再因进程死亡返回 500。不改登录协议,不晋升 test/main,不 push。
|
||||
|
||||
## 问题登记与经验检索
|
||||
|
||||
- issue_id: `ISSUE-20260928-docgov-map-race`
|
||||
- 检索词:`docgov`、`memDocs`、`concurrent map`、`500`、`登录`
|
||||
- 命中:`ISSUE-20260926-docgov-memdocs-poison`(同模块内存态与库分叉,机制不同,本轮不关)。无「per-doc 锁拆分后共享 map 无互斥」的既有经验。
|
||||
- 采纳:修共用 map 互斥,不用登录特例。
|
||||
|
||||
## 现象
|
||||
|
||||
2026-09-28 06:38 UTC,dev `agent-server` 在 `GetDocument`(`internal/docgov/engine_read.go`)触发 `fatal error: concurrent map read and map write` 后退出。06:38–06:49 Vite 代理 `ECONNREFUSED 127.0.0.1:8080`,登录、`auth/me`、通知、维护状态等全部 500。06:49 进程被重新拉起后接口恢复。登录代码未改。
|
||||
|
||||
## 根因
|
||||
|
||||
`340342fa` 把 docgov 全局锁拆成 per-doc 锁。per-doc 锁只串行同一 `docID`。`memDocs`、`memAudits`、`memComms`、`memProps` 仍被不同文档并发读写。`e.mu` 不是可重入锁,且部分调用方已经持有它,所以新增 `memMu` 专门保护这四张 map。锁序:docLocks → `e.mu` → `memMu`。
|
||||
|
||||
## 修复
|
||||
|
||||
`a90cce73`。回归:`TestMemMapsNoRaceAcrossDocs`(`go test -race`)。
|
||||
|
||||
## 五态
|
||||
|
||||
| 字段 | 值 |
|
||||
|---|---|
|
||||
| review_state | accepted |
|
||||
| integration_state | merged_local_dev(收口记录待第二次快进) |
|
||||
| deployment | loaded_local(2026-09-28 09:10 UTC 重建并重启 `:8080`,health 200) |
|
||||
| cleanup | pending |
|
||||
| remote_sync | not_requested |
|
||||
|
||||
## ISSUE-20260928-docgov-map-race
|
||||
|
||||
- 预期:文档治理并发不杀死 HTTP 进程;登录等无关 API 保持可用。
|
||||
- 实际:2026-09-28 06:38 UTC 进程 fatal,约 11 分钟内全部 API 500。
|
||||
- 状态:resolved。`go test -race` PASS(`a90cce73`)。dev `b9339cd2` 重建 `bin/agent-server` 后,`/health` 200,空登录 POST 401(`req_e28309cc8d615ed4`),Vite `:3100` 的 `public-config` 200。
|
||||
- 同类:所有裸读写上述四张 map 的路径,已收口到 `memMu` 辅助函数。`ISSUE-20260926-docgov-memdocs-poison` 仍是另一机制。
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: "资料类提问不再落入阶段推销模板"
|
||||
status: blocked
|
||||
created: 2026-09-28
|
||||
owner: Cursor
|
||||
task_id: intent-material-fallback-2026-09-28
|
||||
policy_revision: agent-governance-2026-09-23-fav-constraint-restored @ 434d542c3cbdbeac38b9c0e547edf75160acf577
|
||||
review_state: pending
|
||||
integration_state: pending
|
||||
deployment: not_required
|
||||
cleanup: pending
|
||||
remote_sync: not_requested
|
||||
updated: 2026-09-28
|
||||
---
|
||||
|
||||
# 资料类提问不再落入阶段推销模板
|
||||
|
||||
## 范围与授权
|
||||
|
||||
用户要求:test 上 zhangqizhe 在「全自动冲击波治疗仪1」询问说明书、操作视频、竞品资料时,不要再回复「完全理解您想再掂量一下…V0.5」模板;并核对 wanjiahao 发送「1」的失败是否同一类问题。
|
||||
|
||||
授权:只改代码并合入 local dev,dev 真实链路验证后再谈 test 晋升。test 目录属主与 `PI_AGENT_RUNNER_URL` 本轮不改。
|
||||
|
||||
允许写路径:`internal/intent`、`internal/coordinator`、`internal/chat`、`.doc`。工作树已快进到 `dev` `8889011e5b6bd35a1d992ae5aec3f1c1c8bdae31`(原始基线 `8f77053f5169502eaf3e4e4135a082e8914f3704`)。分支 `fix/intent-material-fallback-2026-09-28`。mutex:`intent+coordinator`。
|
||||
|
||||
非目标:不新增第 19 类意图;不改 ADVANCE_STAGE 失败后的静默回合;不晋升 test/main;不 push。
|
||||
|
||||
## 问题登记与经验检索
|
||||
|
||||
检索词:`掂量`、`AMBIGUOUS_FALLBACK`、`reply_source`、`说明书`、`ADVANCE_STAGE`。台账与 `.doc/ops/lessons` 无同机制命中。本任务登记:
|
||||
|
||||
- [ISSUE-20260928-material-template-reply](#issue-20260928-material-template-reply)(本任务修复)
|
||||
- [ISSUE-20260928-advance-stage-silent-fail](#issue-20260928-advance-stage-silent-fail)(只登记)
|
||||
|
||||
经验:[LESSON-20260928-material-noun-not-sales-template](../lessons/2026-09-28-material-noun-not-sales-template.md)。
|
||||
|
||||
## 现象
|
||||
|
||||
test 库 `sess-p8-u33`(项目 8,用户 33)中「上一代产品说明书」「竞品的操作视频」等助手消息 `reply_source=template`,正文为阶段推销,多条内容哈希相同。识别结果为 `CASUAL_CHAT` / `AMBIGUOUS_FALLBACK`,置信度 0.2。
|
||||
|
||||
`sess-p5-u32` 在 2026-09-28 01:29:46Z 的「1」(turn 22)`status=failed`,没有助手消息。约 14 分钟后第二条「1」(turn 23)完成阶段推进。
|
||||
|
||||
## 根因分析
|
||||
|
||||
已证实(问题 A):
|
||||
|
||||
1. `matchKnowledgeQuery` 没有说明书、操作视频、彩页、竞品等资料词,输入掉进 `CASUAL_CHAT` 保底(置信度 0.2),再被收成 `AMBIGUOUS_FALLBACK`。
|
||||
2. `handleAmbiguousConsult` 对非问句直接拼「掂量 / V0.5」模板。问句标记才会改去闲聊。
|
||||
3. 对话模型早退不打日志。test 进程属主 weichen,`data/test/workspaces/dialogue` 属主 root,`MkdirAll` 返回 EACCES 后回复保持模板。test 服务的 `PI_AGENT_RUNNER_URL` 指向 dev runner `18865`(test runner 是 `18866`)。环境问题留到 dev 验证之后;代码侧补上失败原因。
|
||||
|
||||
已证实(问题 B,不修):turn 22 意图 `ADVANCE_STAGE` / `EXPECTATION_HIT`。工作区 `stage_V0.5_feasibility_1790558986793391446` 的 `step_03_regulatory` 失败且输出为空,回合随后没有助手消息。与推销模板不是同一条失败路径。
|
||||
|
||||
## 同类问题与影响范围
|
||||
|
||||
| 关联场景/入口 | 与原例共用的机制 | 检查方法与证据 | 判定及影响 |
|
||||
|---|---|---|---|
|
||||
| 犹豫征询(「你觉得呢」) | 同一模糊分支 | 单测仍要求保留掂量对照 | 保留,征询判断早于问句改道 |
|
||||
| 编写/撰写手册 | 资料词与写动词重叠 | 「请撰写详尽合规手册」不得标 materials | 写动词仍走任务触发 |
|
||||
| 标杆案例 | 同一意图 `QUERY_SIMILAR_CASES` | 无 `query_topic=materials` 时仍走原案例模板 | 资料请求用 slot 分流,避免伪造案例 |
|
||||
| 阶段推进失败无回复 | 不是意图词典 | turn 22 无助手行 | 另立 issue,本轮不改 |
|
||||
|
||||
## 修复
|
||||
|
||||
- 资料词命中 `QUERY_SIMILAR_CASES`,`query_topic=materials`,置信度 0.82。有知识库命中则 `reply_source=kb`;否则对话模型;再失败则中性追问并写 `fallback_reason`。
|
||||
- 模糊分支里,非犹豫、非空输入不再使用阶段推销模板。
|
||||
- `generateDialogueReply` 早退打 `dialogue_reply_fallback`,原因写入 `CoordinationResult.ReplyFallbackReason`;`reply_source=template` 时落入消息 metadata。
|
||||
|
||||
## 五态
|
||||
|
||||
- review_state: pending
|
||||
- integration_state: blocked(2026-09-28 20:45 工作树在 `8889011e`。已关掉工作树或分支已消失、且 scope 为 `.` 的夜间 integrate 空锁。`task register` 仍被拒:`same_scope`,持有者 `quality-gate-heading-deploy-2026-09-28`,overlap `5a90581e-5a70-42e2-9745-8f04ac0ec11f`。该分支还有未进 dev 的文档提交 `5c0a84f7`。`nightly-advance-land-2026-09-28` 占 `.doc` 且状态 in_progress。`controlled-projection-project-binding-2026-09-28` 占 `internal/coordinator`,约 20:35 仍有新提交)
|
||||
- deployment: not_required(:8080 仍是基线二进制 pid 1002830。真实链路打在工作树二进制 `127.0.0.1:18080`,已停止)
|
||||
- cleanup: pending
|
||||
- remote_sync: not_requested
|
||||
|
||||
## ISSUE-20260928-material-template-reply
|
||||
|
||||
- issue_state: verifying
|
||||
- severity: high
|
||||
- owner: Cursor
|
||||
- 验收:单测三条原始输入为 `QUERY_SIMILAR_CASES` / `materials` / 0.82。dev 库真实回合(项目 6024,会话 `sess-intent-material-probe-2`)`reply_source=kb`,不含「掂量」。报告见 [测试报告](../../test-reports/2026-09-28-intent-material-fallback-test-report.md)。:8080 尚未换成这版二进制。
|
||||
|
||||
## ISSUE-20260928-advance-stage-silent-fail
|
||||
|
||||
- issue_state: open
|
||||
- severity: high
|
||||
- owner: Cursor
|
||||
- 下一步:独立授权后再修「小队步骤失败必须落一条助手失败说明」。本记录不把该问题标成已修。
|
||||
@@ -0,0 +1,126 @@
|
||||
---
|
||||
title: "多仓库架构与 fork/PR 协作设计"
|
||||
task_id: multi-repo-design-2026-09-28
|
||||
status: done
|
||||
created: 2026-09-28
|
||||
updated: 2026-09-28
|
||||
owner: Codex
|
||||
task_type: research
|
||||
task_tier: multi_stage
|
||||
policy_revision: "agent-governance-2026-09-23-fav-constraint-restored @ a3db288a7aa055ff5884e3aa4d91511959cff721"
|
||||
branch: feature/multi-repo-design-2026-09-28
|
||||
worktree: .worktrees/multi-repo-design-2026-09-28
|
||||
base_dev_sha: a3db288a7aa055ff5884e3aa4d91511959cff721
|
||||
write_scope:
|
||||
- .doc/ops/short-term/2026-09-28-multi-repo-design.md
|
||||
- .doc/remediation/2026-09-28-multi-repo-architecture-plan.md
|
||||
- .doc/audit/2026-09-28-multi-repo-evidence.md
|
||||
allowed_environments: []
|
||||
mutex: path:multi-repo-design-documents
|
||||
review_state: pending
|
||||
integration_state: not_required
|
||||
deployment: not_required
|
||||
cleanup: retained-with-reason
|
||||
cleanup_owner: "审核后独立维护者"
|
||||
cleanup_trigger: "用户审阅完成且原 owner 退出"
|
||||
remote_sync: not_requested
|
||||
---
|
||||
|
||||
# 多仓库设计任务
|
||||
|
||||
## 用户目标与授权
|
||||
|
||||
来源:2026-09-28 本会话用户要求深入设计主仓 + 子仓 + submodule、fork/PR/上游同步、Gitea 多平台二进制发布,先提交待审方案,未审阅前不生效。允许读取本地代码和 Gitea 仓库元数据,生成本任务文档。仓库描述本轮仅拟稿,实际远端写入留到用户审阅通过后的执行阶段。
|
||||
|
||||
交付终点为可审方案与证据;不迁移代码、不创建或改写远端仓库、不调整权限/描述、不 push、不合入 dev、不部署、不访问业务正文。本地创建任务 worktree 和登记仅承载研究产物。
|
||||
|
||||
## 相关问题与经验
|
||||
|
||||
检索词:submodule、fork、跨仓、迁移、路径、candidate、squad、failclosed。
|
||||
采纳现有经验中的路径/快照一致性、拒绝旁路和未知状态、CURRENT/MATRIX 收据与证据绑定检查。
|
||||
历史多主机协调方案仅作线索,已重新核查本次源码与机器状态,不将方案当上线事实。
|
||||
本任务不修复已有业务问题,不将候选经验升级为 validated。
|
||||
|
||||
## 步骤与验收
|
||||
|
||||
1. 核对本地 dev、实时 Gitea、仓库用途和保护设置。
|
||||
2. 研究 Squad、文档、规范、治理工具、构建发布边界。
|
||||
3. 制定方案、分阶段迁移/回退与验收矩阵。
|
||||
4. 完成文档复核、差异/链接检查及只读收口盘点。
|
||||
|
||||
## 验证与交接
|
||||
|
||||
本地基线 a3db288a7aa055ff5884e3aa4d91511959cff721;Gitea dev 实时为 fabd85a65e15f3417431ba6f004a8bcdf9cb83cb,已验证远端为祖先,本地多 306 提交。本轮不同步。
|
||||
任务登记采用三个独占文件 scope;不修改 CURRENT/MATRIX 共享内容。
|
||||
|
||||
用户补充:Squad 为产品文档生成与复杂任务小队,开发治理工具可单列建议;内部测试版需要私有隔离,公开版公开。两项已写入方案。
|
||||
|
||||
交付:
|
||||
|
||||
- [待审方案](../../remediation/2026-09-28-multi-repo-architecture-plan.md)
|
||||
- [调研证据与审查记录](../../audit/2026-09-28-multi-repo-evidence.md)
|
||||
|
||||
验收:三份文档的格式、链接、要求/单元/验收项覆盖和 git diff --check 已通过。本地六视角审查收敛四项修改;附加外部模型检查未返回并主动终止,不计 PASS。只读 cleanup report 为 19/blocked=19/ready=0;docs drift regressions=0。未运行实施测试或真实产品验证。
|
||||
|
||||
## 收口
|
||||
|
||||
研究任务 done;方案 review_state=pending/policy_state=proposed。用户审阅仍是后续实施入口。
|
||||
本地文档以独立 Conventional Commit 保存;不合入 dev、不 push、不部署、不设置仓库描述/权限。
|
||||
worktree 保留供审阅,cleanup=retained-with-reason;用户审阅结束、owner 退出后由独立维护者处理,不能因为研究完成便删除唯一待审产物。
|
||||
本轮不新增 validated lesson;所有迁移风险仅是方案检查项,尚无修复验收证据。
|
||||
接管者先读本任务、方案与证据,再核对最新 dev/Gitea/在途任务;不得仅凭此任务 done 执行迁移。
|
||||
|
||||
## 修订 2:前端与 Agent-Native(2026-09-28)
|
||||
|
||||
用户追加前端拆仓、自研 Agent 和第三方 Agent 接入方向;原“待审、不生效”授权边界继续适用。
|
||||
沿用逻辑 task_id 与 worktree;旧执行登记已关闭且工具无 reopen,续办执行登记为 multi-repo-design-2026-09-28-r2,不扩大三个文档路径 scope。
|
||||
|
||||
完成源码确认:助手与 Squad 共用 agent.Backend/NewBackend;原生内核位于 agentx,pinative 是产品适配;源码默认仍 pi-agent,部分增强功能仍按该名称装配,运行态未验证。
|
||||
新增 Agent-System-Web/Agent-Native 设计、契约与权限边界、旧名兼容、PI fallback 真正执行身份、Web 单二进制资产装配,以及对应迁移单元与验收。
|
||||
通用规范/模块契约记忆仅辅助定位,当前结论来自新读源码;没有实施或扩大运行权限。
|
||||
不新增 validated lesson;本轮未修复或运行验证。
|
||||
原任务的首轮审查/盘点保留为历史记录,修订 2 的结果单独记于证据。
|
||||
|
||||
## 修订 3:Ubuntu与原生核心替代
|
||||
|
||||
用户确认Ubuntu 26.04 x86_64单平台与PI核心能力对齐方向;新增执行登记multi-repo-design-2026-09-28-r3,逻辑task_id与三个写路径保持。U9独立于拆仓与现版发布,不实施、不改默认后端、不推送或部署。
|
||||
|
||||
## 修订 4:多仓开发治理精简
|
||||
|
||||
来源:用户明确要求重新审视规范、锁、agentctl,可删减无必要或反复阻塞的机制并增加适合新方案的能力;只修订待审方案。
|
||||
沿用multi-repo-design-2026-09-28逻辑任务及仍active的r3执行登记;仅更新原三个文档。
|
||||
交付变化:KTD6改为Gitea原生准入;新增KTD13治理取舍/锁与恢复模型,重写U2,增加R15与V19–V23;补落盘上轮Ubuntu单平台和PI核心能力对齐U9。
|
||||
已核验一次全仓恢复阻塞的实际因果链,并保存为audit中的问题候选;未修复工具,不把历史恢复次数当错误率。
|
||||
|
||||
临时例外:用户批准仅三份待审文档的一次性写入租约豁免,owner=Codex,期限=保存即终止,禁止改锁/工具/现行规范/提交/推送,恢复为正常租约写入。
|
||||
原锁先恢复,方案主体通过agentctl with保存;随后新候选准备操作出现相同全仓恢复阻塞,最终三文档保存使用已批准豁免。没有解锁或修改现行工具/规则;保存校验结束立即终止豁免,按用户明确范围不提交或推送。
|
||||
本轮研究产物完成后仍review_state=pending、policy_state=proposed,部署/实施/远端设置均未执行。
|
||||
|
||||
最终校验:3份文档、6个相对链接、15项要求、9个实施单元、23项验收场景,结构/ID/链接/旧方案残留检查通过;git diff --check exit=0。收口盘点cleanup total=18/blocked=18/ready=0,未清理;docs drift regressions=0/open_capabilities=3。没有实施、编译或真实运行验收。本轮使用的一次性三文档豁免至此结束,不提交/不推送。
|
||||
|
||||
## 修订 5:日常规范按影响适配
|
||||
|
||||
用户要求按最新方案适当完善过严规范;继续只更新三份待审文档,不提交/推送/实施。
|
||||
逻辑task_id不变;机器执行登记multi-repo-design-2026-09-28-r5承接本次,旧登记不重开。
|
||||
增加KTD14:四类工作影响、精简默认、按阶段验证与适用证据复用、硬边界/默认/建议区分;U2负责同步规范、模板、客户端与CI。
|
||||
保留原规范已合理的纯文档轻验证、按机制回归和真实证据要求;不把放宽流程误写为跳过必要验证或扩大外部授权。
|
||||
|
||||
修订5验证:三文档结构、6个相对链接、R1–R16/U1–U9/V1–V25唯一性与待审状态检查通过,git diff --check通过。只读cleanup为18项blocked、0项ready,docs drift无回退;未清理工作区。正常agentctl with写入,无需也未沿用上轮临时豁免;未提交、推送、修改生效规范或执行产品测试。
|
||||
|
||||
## Revision 6: Remove Coding Workflow Dependence
|
||||
|
||||
用户明确反馈Agent主要精力被agentctl任务领取、提交、合并消耗。本轮收敛KTD13/14的职责,新增KTD15:普通编码流程零agentctl依赖,任务/PR单一状态来源;治理恢复归维护者,发布部署独立。
|
||||
这不是已经测得时间占比;依据为用户观察与本会话两次跨工作区锁阻塞、登记续办及blocked CLI不匹配的实际记录。
|
||||
修改U2优先顺序,先完成本地减负与Gitea交接,再做多仓适配;不把新的task/reopen/claim平台作为减负前提。新增V26端到端日常流程验收。
|
||||
本轮仅修订原三份待审文档,不启用新规则,不提交/推送;现行登记仍按当前工具执行,逻辑task_id未变。
|
||||
|
||||
## 修订 7:单文件应用与可配置多实例
|
||||
|
||||
用户要求统一单文件应用制品、通过启动配置隔离test/prod及其他实例,并评估存储依赖的启动方式。
|
||||
新增KTD16–18、实例隔离与数据目录、U10;替换旧外置assets部署包合同,扩充R17–R19及V27–V34。
|
||||
推荐应用入口内置、自带应用静态资产,PG原生服务,KB/文档逻辑在应用内;部署设置使用独立config_root的本地持久层解决DB启动循环。
|
||||
实例名不再绑定Git分支;同版本制品只构建一次,各实例固定版本与自己的配置/数据。
|
||||
原任务worktree/逻辑task_id不变,执行登记multi-repo-design-2026-09-28-r7。主目录当前test,未切换/部署;对本轮涉及路径复核最新dev差异。
|
||||
仍只修改三文档、不提交或推送;新配置/初始化/迁移与服务生命周期尚未实施,不会因方案示例创建目录或数据库。
|
||||
|
||||
修订7验证:3文档结构、6个相对链接、R1–R19/U1–U10/V1–V34唯一性与待审标记检查通过,git diff --check通过;只读cleanup为20项blocked/0项ready,docs drift无回退,未清理。源码/配置/生命周期定向复核完成;未编译、建库、运行实例或部署。文档通过正常agentctl with保存,本次未使用临时豁免,仍不提交/不推送。
|
||||
@@ -0,0 +1,49 @@
|
||||
# 夜间自动推进任务(nightly-advance-automation-2026-09-28)
|
||||
|
||||
- **status**: in_progress
|
||||
- **owner**: Cursor
|
||||
- **mutex**: nightly-advance
|
||||
- **base_dev_sha**: 650703c5c28d3f4c9fb51ba9022499524a91fe01
|
||||
- **write_scope**: scripts/nightly/、scripts/nightly-advance.sh、scripts/tests/test_nightly_advance.py、.doc/ops/short-term/2026-09-28-nightly-advance-automation.md
|
||||
- **authorization_ref**: 用户 2026-09-28 会话("每天晚上 3 点 10 分,推进项目.worktree 目录下的所有分支任务……"及后续清理语义修正);Agents Window 不可用后用户同意本地 cron 替代方案
|
||||
|
||||
## 用户目标
|
||||
|
||||
每日 03:10 自动推进 `.worktrees/` 下所有活跃分支任务至彻底完成,合入 dev,验证无内容丢失后删除对应任务目录,更新 dev 服务至最新,产出每日夜间自动化报告。
|
||||
|
||||
## 用户裁决(AskQuestion 记录)
|
||||
|
||||
1. **推进范围**:所有活跃任务(不分 owner;Cursor/pi/Codex/Cline)。
|
||||
2. **清理语义**(用户后续修正):任务彻底完成、合入 dev、删除目录不丢内容且不影响系统 → 删除目录与分支;删除被拒时保留 cleanup=pending 并报告原因,禁止 `--force`/`-D`。
|
||||
|
||||
## 方案
|
||||
|
||||
- `scripts/nightly/plan.py`:读 cleanup report + CURRENT + registry,按依赖排序(integrate 候选 → 活跃任务 → 可清理项)。
|
||||
- `scripts/nightly/advance.py`:编排器。预检(daemon 健康、互斥锁、冻结 dev tip)→ 计划 → cursor-agent 无头推进(写走 `agentctl with`)→ daemon ff-only 合入 → dev tip 变化时 `env:dev` 租约 rebuild + `/health` `/ready` → 清理验证+普通删除 → 报告草稿落 run 目录。
|
||||
- `scripts/nightly-advance.sh`:cron 入口(PATH 注入,日志 `.tmp/nightly/entry-*.log`)。
|
||||
- cron:`10 3 * * *`(用户级 crontab,与现有 jev-service watchdog 同模式)。
|
||||
- run 目录:`.tmp/nightly/<stamp>/`(gitignored;`data/` root 属主不可写)。
|
||||
- 每日报告:夜间最后一棒 cursor-agent 日报任务把 run 目录草稿正式化并归档 `.doc/test-reports/`(agentctl 租约提交),按 test-reports-standard 命名 `YYYY-MM-DD-nightly-advance-test-report.md`。
|
||||
|
||||
## 硬边界
|
||||
|
||||
- 写操作一律 `agentctl with`;裸 git commit 被 pre-commit hook 拒绝。
|
||||
- 合入只走 `validate` + `queue enqueue`(daemon `--ff-only`)。
|
||||
- dev 服务更新走 `with --resource env:dev` + `env-service.sh dev rebuild`;不碰 all/test/main。
|
||||
- 清理只用 `agent-worktree.sh remove --delete-branch`(普通删除);拒绝即保留 pending。
|
||||
- 不 push(用户未要求);报告真实验证,无 stub 冒充。
|
||||
|
||||
## 验证
|
||||
|
||||
- 单元:`python3 -B -m unittest discover -s scripts/tests -p 'test_nightly_advance.py'`(9 用例:CURRENT 解析、排序、phase 分类、registry 回退、CLI cap、自排除)。
|
||||
- 真实链路:dry 模式(--skip-agent)端到端:预检锁、plan 13 项、清理拦截未合入分支、报告草稿落盘(run 20260928T063326Z)。
|
||||
- 无头执行器冒烟:cursor-agent --print ask 模式回 `NIGHTLY-OK`;写探测 `PROBE-WRITE-OK`。
|
||||
- 本报告任务自身从 plan 排除(防递归)。
|
||||
|
||||
## 五态(收口时更新)
|
||||
|
||||
- review_state: pending
|
||||
- integration_state: not_started
|
||||
- deployment: not_required(工程工具变更,无需重建产品容器)
|
||||
- cleanup: pending(本任务自身 worktree 由独立维护者复查后清理)
|
||||
- remote_sync: not_requested
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "晋升 rc-v1.1.0-beta.5(当前 dev→test)并重启 test"
|
||||
task_id: promote-rc-beta5-2026-09-28
|
||||
status: done
|
||||
created: 2026-09-28
|
||||
updated: 2026-09-28
|
||||
owner: Cursor
|
||||
task_type: ops
|
||||
task_tier: small
|
||||
policy_revision: "AGENTS+worktree-ops @ 193a752f"
|
||||
branch: feature/promote-rc-beta5-2026-09-28
|
||||
worktree: .worktrees/promote-rc-beta5-2026-09-28
|
||||
base_dev_sha: "193a752f1a540fb22e9b54feb56d8aa8e2f30cc1"
|
||||
head_candidate: "73836962a0932d7be868f593804b09a321e0902c"
|
||||
write_scope: ["."]
|
||||
allowed_environments: ["test"]
|
||||
capabilities: [git:test, env:test, docs:publish, docs:admin]
|
||||
mutex: git:test+env:test
|
||||
review_state: accepted
|
||||
integration_state: merged_local_test
|
||||
deployment: test_ok
|
||||
cleanup: pending
|
||||
remote_sync: not_requested
|
||||
---
|
||||
|
||||
# 晋升 rc-v1.1.0-beta.5
|
||||
|
||||
## 用户目标与授权
|
||||
|
||||
用户指令(2026-09-28):不要继续跑长门禁,把当前运行中的 dev 合入 test,并重启最新 test 服务;工作区与数据库连接不得串到 dev。授权来源:本会话明确指令。未授权 push。
|
||||
|
||||
已中止本会话启动的 nightly(`run-nightly-real-pi.sh` @ `8f77053f`,FullNoneMixedChain 首轮 MISSING_TARGET 后仍在跑 TestManager)。不把未完成的 nightly 写成 PASS 收据。
|
||||
|
||||
## 结果
|
||||
|
||||
- 冻结并晋升的 dev:`193a752f`(含 PI runner 数据目录校验)。test 从 `2fe1e055`(rc-v1.1.0-beta.4)前进到 `73836962`。
|
||||
- 候选:commit-tree,P1=`2fe1e055`,P2=`193a752f`,树与 dev 相同。validate run `21f631ce`(拓扑 + `git diff --quiet`)。
|
||||
- `git:test` 租约内 `update-ref refs/heads/test` + tag `rc-v1.1.0-beta.5`。
|
||||
- 制品:主仓 `bin/agent-server-test`,由 `193a752f` 工作树构建(demo seeds off)。`launch-single.sh test` 在清空调用方 `DATA_DIR` / `PI_AGENT_RUNNER_URL` 后启动,避免根 `.env` 的 dev 值生效。
|
||||
- 租约因 nohup 进程组未退出记 `recovery_required`(run `eaf7e2e4`)。服务改由独立会话拉起后,`recover --reason verified:` 已收口。
|
||||
|
||||
## 连接核对(重启后)
|
||||
|
||||
| 项 | 实际 |
|
||||
|---|---|
|
||||
| health/ready `:8081` | `env=test`,`schema_version=78`,`database_ready/kb_ready=true`,SPA 200 |
|
||||
| 进程 | `bin/agent-server-test`,`SEED_DEMO_USERS=false` |
|
||||
| 系统库 | `127.0.0.1:5434/agent_system_test`(连接已建立;projects=11,users=11,与重启前相同) |
|
||||
| 共享 KB | 宿主将 `shuimu-kb-shared` 规范为 `127.0.0.1:5432/regulations` |
|
||||
| 工作区 | `DATA_DIR` 解析为 `data/test`;sidecar `:18866` 的 `data_dir=data/test/workspaces` |
|
||||
| dev 未串 | test 进程无到 `:5433` 的连接;dev `:8080` 仍在,projects=25;sidecar `:18865` 仍是 `data/dev/workspaces` |
|
||||
|
||||
## 清理
|
||||
|
||||
已合入 dev 且任务 done 的 worktree,在去掉可再生忽略文件后 `agent-worktree.sh remove --delete-branch`。未动:进行中的 agent-reply / test-user-doc 现场、squad evidence 未提交文档、u9u12(用户 shell 占用)、intent-material-fallback(有未提交跟踪修改)、pi-runner 任务目录(本地 `configs/.env.test` 与主仓不一致)。
|
||||
|
||||
`feature/full-system-browser-e2e-2026-09-26` 分支已 `-d`(祖先为 `a9d79b99`)。目录残留 root 拥有的 `playwright-output-main/`,普通删除被拒绝,保留待授权清理。
|
||||
|
||||
## 收口
|
||||
|
||||
- deployment=test_ok;remote_sync=not_requested。
|
||||
- 本记录提交在晋升分支上。当时另有 integrate 候选未合入 dev,故未把本文件 enqueue 进 dev,避免挤占对方基线。
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: "晋升 rc-v1.1.0-beta.6(dev→test)与已结束 worktree 清理"
|
||||
task_id: promote-rc-beta6-closeout-2026-09-28
|
||||
status: done
|
||||
created: 2026-09-28
|
||||
updated: 2026-09-28
|
||||
owner: Cursor
|
||||
task_type: ops
|
||||
task_tier: small
|
||||
policy_revision: "AGENTS+worktree-ops @ 714732df"
|
||||
branch: feature/promote-rc-beta6-closeout-2026-09-28
|
||||
worktree: .worktrees/promote-rc-beta6-closeout-2026-09-28
|
||||
base_dev_sha: "714732df5a378eae43df0f73ad6a059a4840c3e1"
|
||||
head_candidate: "0664abe57d85f7776874728e92476c5360388b2a"
|
||||
write_scope: [".doc"]
|
||||
allowed_environments: ["test"]
|
||||
capabilities: [docs:publish, docs:admin]
|
||||
mutex: git:test+env:test
|
||||
review_state: accepted
|
||||
integration_state: merged_local_test
|
||||
deployment: test_ok
|
||||
cleanup: pending
|
||||
remote_sync: not_requested
|
||||
---
|
||||
|
||||
# 晋升 rc-v1.1.0-beta.6
|
||||
|
||||
## 用户目标与授权
|
||||
|
||||
用户指令:继续把 dev 合入 test,并清理已结束现场。不跑 nightly,不 push。
|
||||
|
||||
## 结果
|
||||
|
||||
- beta.5 记录先合入 dev:`714732df`(`merge: candidate land-promote-rc-beta5-record-2026-09-28 into dev`)。首次入队因主仓未跟踪的中止 nightly 证据被 `DIRTY_TREE` 拒绝;证据移到 `/tmp/agent-nightly-aborted-8f77053f/` 后,r2 候选合入。
|
||||
- test 从 `73836962`(rc-v1.1.0-beta.5)前进到 `0664abe5`,标签 `rc-v1.1.0-beta.6`。第一父是上一轮 test,第二父是 `714732df`,树与该 dev 相同。validate run `f3fed9d6`。
|
||||
- 制品:主仓 `bin/agent-server-test`,由 `714732df` 构建。启动前清掉调用方 `DATA_DIR` 与 `PI_AGENT_RUNNER_URL`。
|
||||
- 租约 run `c1eb671a` 因后台进程组未退出进入 `recovery_required`。服务改由独立会话拉起后已 `recover`。
|
||||
|
||||
## 连接核对
|
||||
|
||||
| 项 | 实际 |
|
||||
|---|---|
|
||||
| health `:8081` | `env=test`,schema 78,数据库与共享知识库就绪;首页 200 |
|
||||
| 系统库 | `127.0.0.1:5434/agent_system_test`;projects=11,users=11,与重启前相同 |
|
||||
| 工作区 | `DATA_DIR` 解析为 `data/test`;sidecar `:18866` 数据目录为 `data/test/workspaces` |
|
||||
| dev 未串 | test 进程无到 `:5433` 的连接;dev `:8080` 仍在;`:18865` 仍是 `data/dev/workspaces` |
|
||||
|
||||
构建把主仓已跟踪的 `web/dist/.gitkeep` 从工作区删掉,已 `git checkout` 恢复,未形成新提交。
|
||||
|
||||
## 清理
|
||||
|
||||
已删除:`fix/test-user-doc-issues-2026-09-28`(只剩 `.codegraph/`);`feature/integrate-land-promote-rc-beta5-record-2026-09-28-r2`(已是 dev)。
|
||||
|
||||
留下:`feature/integrate-land-promote-rc-beta5-record-2026-09-28`(首个候选不是 dev 祖先,`cleanup_pending`);进行中的 agent-reply、quality-gate、refresh-task-state、subagent-process-view、e2e-wipe 及其 integrate 场;intent-material-fallback 与 squad evidence 的未提交修改;pi-runner 本地 `configs/.env.test` 与主仓不一致;u9u12 仍被 PID 3386059 占用;root 拥有的 e2e `playwright-output-main/`。
|
||||
|
||||
## 收口
|
||||
|
||||
deployment=test_ok;remote_sync=not_requested。本记录随后合入 dev。test 这一刀不含本文件。
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
title: "质量门禁章节标题漂移不再误判缺章"
|
||||
task_id: quality-gate-heading-mismatch-2026-09-28-r3
|
||||
status: done
|
||||
created: 2026-09-28
|
||||
updated: 2026-09-28
|
||||
owner: Cursor
|
||||
task_type: fix
|
||||
policy_revision: "AGENTS.md + agent-worktree-workflow @ 2026-09-21"
|
||||
branch: fix/quality-gate-heading-mismatch-2026-09-28
|
||||
worktree: .worktrees/quality-gate-heading-mismatch-2026-09-28
|
||||
base_dev_sha: "434d542c3cbdbeac38b9c0e547edf75160acf577"
|
||||
write_scope:
|
||||
- internal/squad
|
||||
- internal/coordinator
|
||||
- .doc
|
||||
mutex: quality-gate-heading
|
||||
review_state: accepted
|
||||
integration_state: merged_local_dev
|
||||
deployment: loaded_host_dev
|
||||
cleanup: pending
|
||||
remote_sync: not_requested
|
||||
---
|
||||
|
||||
# 质量门禁章节标题漂移不再误判缺章
|
||||
|
||||
## 用户目标与授权
|
||||
|
||||
dev「吞咽电刺激」调研报告已写完,助手称已生成,界面却是「质量未通过」,受控文档没有更新。用户要求先确认失败原因;若只是项目信息仍在迭代,不得因此判质量不通过,并给出可落地修复。用户确认:标题做归一化并跨 H2–H4 匹配;聊天触发任务默认打开质量自动修复;门禁修复合入后重新发起调研报告。
|
||||
|
||||
## 检索
|
||||
|
||||
- 检索词:`quality_report`、`缺少必填章节`、`extractHeadings`、`质量未通过`、`AutoRepair`。
|
||||
- 命中 [ISSUE-20260928-quality-failed-success-copy](../../audit/ISSUES.md):同步回复在质量失败时不得写宿主机路径、不得声称已完成。当前 `formatTriggerTaskReply` 已把非完成态交给 `CompletionSummary`,本任务不改这句文案,只补聊天链路的 `AutoRepair`。
|
||||
- catalog overlap 语义层无命中。注册时若绑定 intent,会与仍为 active 的 `agent-reply-llm-narrative-2026-09-28`(scope `internal`+`.doc`)以及若干 scope `.` 的 integrate 任务发生 same_scope。该叙述任务改过 `engine_task.go` 的返回包装,与本次 `TaskRunRequest` 字段不在同一行。本次因此不绑 intent/mutex 到目录库,mutex 只写在 CURRENT 认领行。首个 task id 因 scope 不含 `.doc/ops` 无法认领,已 cancelled,改用 `quality-gate-heading-mismatch-2026-09-28-r2`。
|
||||
|
||||
## 根因
|
||||
|
||||
`research_1790566833205199368/manifest.json` 的 `quality_report.passed=false`,唯一违规是缺少「技术路线与 State-of-the-Art」。终稿该节在 `output.md` 第 279 行,标题是 `### 技术路线与State-of-the-Art`:层级降成 H3,且「与」后少了空格。门禁原先只抽 H2,清洗序号后做字符串精确相等,因此把格式漂移当成整章缺失。评分 85 仍高于 80,但缺章直接 `Passed=false`。
|
||||
|
||||
质量失败后 `finalizeSquadDeliverable` 按设计不投影受控文档,所以正文留在隔离工作区、项目文档不更新。这次 `workspace.json` 的 `project_id` 为 0,docID 为空,聊天触发又没设 `AutoRepair`,修复轮为 0。
|
||||
|
||||
门禁不检查「待确认」数量。信息还在迭代补齐,不是这次失败的原因。
|
||||
|
||||
## 修复
|
||||
|
||||
- 模板 H2 与正文 H2–H4 按归一化标题比对:去掉全部空白(含全角空格)、全角 ASCII 折半角、英文字母小写。只在 H3/H4 命中时记「章节层级与模板不一致」,不扣分、不阻断。归一化后仍没有的章节继续判缺失。
|
||||
- `handleTriggerTask` 设置 `AutoRepair: true`,修复轮上限仍是 `DefaultMaxRepairRounds`。
|
||||
- `module-boundaries.yaml` 为 coordinator 补上已有的 `projectworkflow` 依赖。该 import 在基线 `434d542c` 的 `engine_task.go` 里就存在,本 diff 没有新增 import;block 模式基线计数为 0,不补允许项则门禁对当前 dev 也是红的。
|
||||
|
||||
## 同类
|
||||
|
||||
dev 历史 `passed: false` 工作区里,`test_manager_1790215680339783789` 的「签署/说明」是「十一、」「十二、」序号,现有序号清洗已能对上。`intake_1789329815528283509` 当前正文已有「阻塞信息」「非阻塞信息」H2,不能用现状复现当时的缺章。`intake_1789269755280857331` 模板要「正文」,成稿没有这个标题,缺章判定仍然正确。
|
||||
|
||||
## 验收
|
||||
|
||||
单测覆盖:空格/全角/大小写漂移通过且有层级软警告;真实缺「正文」仍失败;聊天触发 `AutoRepair=true`;质量失败回复不含「已完成生成」。报告见 [质量门禁标题漂移测试报告](../../test-reports/2026-09-28-quality-gate-heading-mismatch-test-report.md)。
|
||||
|
||||
代码已合入 local dev `650703c5`。当时 dev 实际进程是宿主 `bin/agent-server`(`data/dev/pg` 占用 5433),compose 重建绑不上该端口。已按 `launch.sh --no-docker` 重建宿主二进制并换到新会话;二进制含「章节层级与模板不一致」。
|
||||
|
||||
## 重跑
|
||||
|
||||
摸底文档 `doc_proj6024_bd_intake` 已是 approved,阶段就绪 `can_advance=true`。已把工作流实例 12 从 `V0.1_triage` 推进到 `V0.5_feasibility`,再以项目 6024 发起 `bd_research`。
|
||||
|
||||
- 运行 `run_1790578236273253115`,工作区 `bd_research_1790578240073269711`,`workspace.json` 的 `project_id` 为 6024,状态 completed。耗时约 16 分钟。
|
||||
- `manifest.json` 的 `quality_report.passed=true`,score 100,title_check true。终稿该节是 `## 五、技术路线与 State-of-the-Art`,序号清洗后与模板对齐,没有再报缺章。
|
||||
- 受控文档 `doc_proj6024_bd_research` 从无 head、无 work_dir,更新为 `head_snapshot_id=62`、`content_sha256=a1a7f41b17ded92f659a32e35b9652435c36a61f22049152df795df7dc4a5b73`、`work_dir=data/dev/workspaces/bizdev/bd_research_1790578240073269711`。状态仍是 drafting。
|
||||
|
||||
## ISSUE-20260928-quality-heading-drift
|
||||
|
||||
- 预期:模板要求的章节只要正文里有同名内容,就不因空格或降为 H3/H4 被判整章缺失。
|
||||
- 实际:调研报告终稿被判缺少「技术路线与 State-of-the-Art」,评分 85,文档不投影。
|
||||
- 状态:resolved。单测覆盖 H3 与空格漂移。dev 重跑 `bd_research_1790578240073269711` 的 `project_id=6024` 且 `quality_report.passed=true`,受控文档 head 与 work_dir 已更新。这次成稿标题本身是 H2,活体没有再踩到层级漂移。
|
||||
|
||||
## ISSUE-20260928-data-dir-doubled
|
||||
|
||||
- 观察:存在 `data/dev/data/dev/workspaces/bizdev/intake_1790565513115586191/output.md`,该目录没有 `workspace.json`。正确工作区在 `data/dev/workspaces/bizdev/intake_1790565513115586191/`。
|
||||
- 假设:某处把已含 `data/dev` 的 DataDir 又拼了一次。未沿调用链证实,本任务不修。
|
||||
- 状态:open。复查触发:下次出现交付路径含 `data/dev/data/dev` 时沿写入点追查。
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: "刷新后恢复任务进度并纠正进度问询"
|
||||
task_id: refresh-task-state-recovery-2026-09-28
|
||||
status: in_progress
|
||||
created: 2026-09-28
|
||||
updated: 2026-09-28
|
||||
owner: Cursor
|
||||
task_type: fix
|
||||
policy_revision: "AGENTS.md + agent-worktree-workflow @ 2026-09-21"
|
||||
branch: fix/refresh-task-state-recovery-2026-09-28
|
||||
worktree: .worktrees/refresh-task-state-recovery-2026-09-28
|
||||
base_dev_sha: "4c42d1bf74e262930bcf127e517ab6b76a0dda71"
|
||||
write_scope:
|
||||
- internal
|
||||
- web
|
||||
- .doc
|
||||
mutex: refresh-task-state-recovery
|
||||
review_state: pending
|
||||
integration_state: branch_only
|
||||
deployment: not_loaded
|
||||
cleanup: pending
|
||||
remote_sync: not_requested
|
||||
---
|
||||
|
||||
# 刷新后恢复任务进度并纠正进度问询
|
||||
|
||||
## 用户目标与授权
|
||||
|
||||
用户发起调研报告后,聊天下方的任务进度在刷新页面时消失。随后两次询问「调研报告编写进度怎么样了」,没有看到调研报告生成。用户要求浏览器和服务器同时记下进行中的任务:刷新或重连后先显示本地缓存,再按会话和任务标识向服务器收敛。授权范围同时包含进度问句被当成新任务,以及 11:27 / 11:38 两次步骤被判空输出失败。
|
||||
|
||||
## 检索
|
||||
|
||||
- 问题台账命中 ISSUE-20260928-task-run-replay-502(历史失败气泡重放 502)。本轮给重放补了退避,并保持终态 `replay_only`。
|
||||
- catalog overlap 探测无硬重叠。mutex 为 `refresh-task-state-recovery`,不占用 `chat+coordinator` 与 `bizdev+docflow`。
|
||||
|
||||
## 根因
|
||||
|
||||
1. `run_id` 与阶段快照只在任务终态写回助手消息。窗口缓存只保存服务端窗口,刷新后没有锚点,进度卡片无法重放。
|
||||
2. 「调研报告编写进度怎么样了」含写动词「编写」和任务名词「调研报告」。`matchTriggerTask` 排在进度查询之前,于是又启动了 research。进度回复只读模板,不读真实 run。
|
||||
3. 步骤判定把 runner 的 `Success=false` 当成失败,即使盘上已经有正文。连接错误留下的文件仍可能是截断的,这条不能放宽。
|
||||
|
||||
## 修复
|
||||
|
||||
- run 启动和绑定助手消息时写入 `{run_id, task_status: running}`。
|
||||
- `GET /api/v1/tasks/runs?session_id=&active=1` 只返回当前用户、当前会话里仍为 pending/running 的 run。
|
||||
- 浏览器 `smdf_agent_workbench_v6_active_tasks` 在窗口就绪后先画出进行中卡片,再用活跃列表收敛;终态删除缓存。
|
||||
- 有 run 锚点但还没有 `task_status` 和阶段快照时,消息保持 `streaming_status`。终态重放对 502 退避,401/404 不再试。
|
||||
- 疑问进度句让行 `QUERY_PROGRESS`。回复读取该会话最近一次 run 的真实状态。
|
||||
- 盘上已有非空正文,且失败原因不是传输/可重试错误时,步骤接受该文件。空 patch 不覆盖已有 `output.md`。
|
||||
|
||||
## 验证
|
||||
|
||||
见 [测试报告](../../test-reports/2026-09-28-refresh-task-state-recovery-test-report.md)。
|
||||
|
||||
## 五态
|
||||
|
||||
合入与 dev 重载完成后回写。当前仍是任务分支上的实现。
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "修复非编制人触发小队账本提交被拒"
|
||||
task_id: squad-commit-authority-2026-09-28
|
||||
status: in_progress
|
||||
status: done
|
||||
created: 2026-09-28
|
||||
updated: 2026-09-28
|
||||
owner: Cursor
|
||||
@@ -14,9 +14,9 @@ write_scope:
|
||||
- internal
|
||||
- .doc
|
||||
mutex: bizdev+docflow
|
||||
review_state: pending
|
||||
integration_state: pending
|
||||
deployment: pending
|
||||
review_state: accepted
|
||||
integration_state: merged_local_dev
|
||||
deployment: dev_rebuilt
|
||||
cleanup: pending
|
||||
remote_sync: not_requested
|
||||
---
|
||||
@@ -37,7 +37,7 @@ remote_sync: not_requested
|
||||
## ISSUE-20260928-squad-commit-authority
|
||||
|
||||
issue_id: ISSUE-20260928-squad-commit-authority
|
||||
issue_state: fixing
|
||||
issue_state: resolved
|
||||
severity: high
|
||||
owner: Cursor
|
||||
task_ids: [squad-commit-authority-2026-09-28]
|
||||
@@ -67,8 +67,8 @@ dev 库只读核实(2026-09-28,`agent_system_dev`):`document_records.doc
|
||||
| 日期 | 动作 | 结果 | 下一步 |
|
||||
|---|---|---|---|
|
||||
| 2026-09-28 | dev 库核对 author 与 task_runs.user_id | 4 ≠ 74,两次失败均为 permission denied | 按编制人代理提交实现 |
|
||||
| 2026-09-28 | `squadCommitActor` + 回归 | 内存单测通过;dev 真实重跑未做 | 合入 dev 后用非编制人重跑 |
|
||||
| 2026-09-28 | dev 以 user 74 重跑 `bd_intake` | `run_1790559088975684237` completed;commit actor 4,snapshot actor 74 | 问题可关闭 |
|
||||
|
||||
### 验收与关闭
|
||||
|
||||
尚未关闭。原例待 dev 重跑;变体与正常路径见 `TestExecuteTask_NonAuthorTriggerMaterializesAsAuthor`、`TestExecuteTask_AuthorTriggerDoesNotSetOnBehalfOf`、`TestExecuteTask_NonAuthorPassesDutyAuthorizer`、`TestDutyAuthorizerAllowsDocumentAuthorCommit`。
|
||||
2026-09-28 关闭。原例 PASS,见 [测试报告](../../test-reports/2026-09-28-squad-commit-authority-test-report.md)。编制人路径与非编制人直接提交拒绝由单测覆盖。dev 宿主二进制已重建;未晋升 test/main。
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# 任务:小队过程流式展示与 subagent 子页面设计
|
||||
|
||||
## 状态
|
||||
|
||||
- status: done(2026-09-28)
|
||||
- owner: Cursor
|
||||
- type: 方案研究(设计文档,无代码实施)
|
||||
- created: 2026-09-28
|
||||
- base_dev_sha: 4c42d1bf74e262930bcf127e517ab6b76a0dda71
|
||||
- branch: feature/subagent-process-view-design-2026-09-28
|
||||
- worktree: .worktrees/subagent-process-view-design-2026-09-28
|
||||
- write_scope: `.doc/system-design/`、`.doc/ops/short-term/`(本文件)
|
||||
- mutex: subagent-process-view-design
|
||||
|
||||
## 用户目标与授权
|
||||
|
||||
> 是否能让助手的工作过程,像豆包、kimi、codex 等工具一样,输出思考、工具调用、过程内容输出等信息;使用流式输出,持续更新助手的工作过程。多智能体小队先设计如何使用 subagent 子页面来展示上面那样的信息,但可以先不实现。
|
||||
|
||||
(来源:2026-09-28 Cursor 会话,用户已确认计划)
|
||||
|
||||
- 授权范围:撰写系统设计文档;**不实施任何代码变更**(用户明示"可以先不实现")
|
||||
- 交付终点:设计文档评审通过(可审产物验收)
|
||||
- 禁止项:修改 Go/TS 代码、数据库迁移、环境操作、推送远端
|
||||
|
||||
## 需求重叠探测
|
||||
|
||||
- 2026-09-28 `catalog search`(关键词:流式输出/思考/工具调用/subagent 页面、streaming agent thought tool call、小队过程可视化流式)均无命中
|
||||
- 既有关联文档为归档/演进方向(2026-09-08 三层信息架构、小队编排 §18.5),无进行中任务重叠
|
||||
|
||||
## 研究输入
|
||||
|
||||
- 三路并行只读探索(后端流式链路 / squad+taskrun 架构 / 前端聊天架构)+ 主线直接调研,交叉验证
|
||||
- 关键现状结论:协议/持久化/前端骨架已就绪,七处断点导致过程数据(思考链、工具调用、增量输出)无法到达前端;前端无 subagent 页面载体
|
||||
|
||||
## 交付物
|
||||
|
||||
- `.doc/system-design/2026-09-28-agent-process-streaming-and-subagent-view-design.md`(460 行,A–H 八章:目标参照/协议扩展/七断点打通/subagent 子页面/分期路线/风险边界/断点对照表/参考文件)
|
||||
- 已登记 `.doc/system-design/README.md` 报告清单(0c 条目)
|
||||
|
||||
## 验证结果(2026-09-28 实测)
|
||||
|
||||
- 行号核对:设计文档引用的 14 处 文件:行 全部与真实代码一致(sed 逐处核对:stream/types.go:68、pinative.go:102、manager.go:364、loop.go:178-183、backend.go:38、runner.mjs:600/24、sse.go:476、workflow.go:543、useTaskRunAttempts.ts:474、taskRuns.ts:113、session.go:624、phase_snapshot.go:225 等)
|
||||
- 内链核对:文档内 21 个代码文件相对链接、6 个 .doc 文档引用全部存在
|
||||
- 术语修正:stripThinkBlocks 剥离对象修正为 `<think>…</think>`(原文误写为特殊符号)
|
||||
- 无硬编码模型名(grep MiniMax/M2.7 零命中于方案正文,仅协议描述提及 provider 家族作为 reasoning_content 语义来源)
|
||||
- 产品 PI 测试 NOT REQUIRED(依据:验证契约「范围判定」——纯设计文档,无运行模板/提示词/业务变更;本任务不产生产品代码)
|
||||
|
||||
## 五态收口
|
||||
|
||||
- review_state: done(设计文档自检完成;待用户评审确认)
|
||||
- integration_state: not_applicable(不合入代码;文档随本任务分支提交)
|
||||
- deployment: not_applicable
|
||||
- cleanup: pending(worktree 待独立维护者复查后清理)
|
||||
- remote_sync: not_applicable(未授权 push)
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "修复第三方 test 用户文档三问题"
|
||||
task_id: test-user-doc-issues-2026-09-28
|
||||
status: done
|
||||
created: 2026-09-28
|
||||
updated: 2026-09-28
|
||||
owner: Cursor
|
||||
task_type: fix
|
||||
policy_revision: "AGENTS.md + agent-worktree-workflow @ 2026-09-21"
|
||||
branch: fix/test-user-doc-issues-2026-09-28
|
||||
worktree: .worktrees/test-user-doc-issues-2026-09-28
|
||||
base_dev_sha: "8f77053f5169502eaf3e4e4135a082e8914f3704"
|
||||
write_scope:
|
||||
- internal
|
||||
- .doc
|
||||
mutex: intent-write-verb
|
||||
review_state: accepted
|
||||
integration_state: merged_local_dev
|
||||
deployment: not_redeployed
|
||||
cleanup: pending
|
||||
remote_sync: not_requested
|
||||
---
|
||||
|
||||
# 修复第三方 test 用户文档三问题
|
||||
|
||||
## 用户目标与授权
|
||||
|
||||
用户提供《AI 测试问题.docx》(放在仓库 `.tmp/`)。授权:阅读图文、定位并修复第三方用户在 test 环境遇到的问题。
|
||||
|
||||
## 检索与重叠
|
||||
|
||||
- 检索词:`帮我定设计开发计划任务书`、`hasTaskWriteVerb`、`质量检查未通过`、`PI runner data_dir`。
|
||||
- P1(只上传附件被空消息拒绝)已由 ISSUE-20260928-attachment-only-turn 在 dev 修复。
|
||||
- catalog overlap(无 scope 绑定)hard/soft 为空。带 `--intent` 注册被 `same_scope` 拒绝,持有者 `agent-reply-llm-narrative-2026-09-28`(scopes `internal`+`.doc`)。本任务不改其文件(`dialogue_llm.go` / `intent_reply_llm.go` 等),只改意图动词与任务回复文案。
|
||||
- P3/P4 的 runner 数据目录错配由并行任务 `pi-runner-env-mismatch-2026-09-28`(`4f581306`)修复。开工时其 integrate 为 DIRTY_TREE;收口前已确认该提交是 local dev `193a752f` 的祖先。本任务不代合入、不改其文件。
|
||||
|
||||
## 事实
|
||||
|
||||
test 库 `agent_system_test`(127.0.0.1:5434)会话 `sess-p8-u33`(2026-09-28):
|
||||
|
||||
- 「帮我定设计开发计划任务书」落到 `handleAmbiguousConsult` 模板「完全理解您想再掂量一下」,未触发 `rd_design_plan`。`hasTaskWriteVerb` 不含「制定 / 帮我定」。
|
||||
- 「帮我生成报价单」回复声称正文已保存到宿主机 `cdo_quote_*/output.md`。该工作区 `quality_report.passed=false`(缺报价范围/模型/说明/约束)。质量失败仍走成功文案。
|
||||
- `bd_intake` run `run_1790558895559080556` 为 `quality_failed`,writer 修复步 `MISSING_TARGET`。sidecar 把 test 工作区交给了数据目录不一致的 runner(见并行任务提交说明)。
|
||||
|
||||
## 修复
|
||||
|
||||
- 写动词增加「制定」「拟定」,以及祈使「帮我定 / 请定」;「决定」不计。
|
||||
- 质量未通过或未完成时,同步回复改用 `CompletionSummary`,不展示宿主机路径、不声称正文已保存。
|
||||
- `ErrPrerequisiteNotMet` 以中文前置条件回复结束回合,不再把哨兵错误当成协调失败丢掉回复。
|
||||
- 模糊咨询若已带澄清问题,直接透传,不再覆盖成阶段对照模板。
|
||||
|
||||
## 验证
|
||||
|
||||
单测与 test 进程核对见 [测试报告](../../test-reports/2026-09-28-test-user-doc-issues-test-report.md)。报价单未在本轮重跑。test 进程在核对时已指向 18866 与 `data/test`。回复文案随本任务进入 local dev 后,test 二进制尚未按新 dev 重放。
|
||||
|
||||
## 收口
|
||||
|
||||
- review_state: accepted
|
||||
- integration_state: merged_local_dev(候选 `6e1d5830`,随后本收口提交再合一次)
|
||||
- deployment: not_redeployed(未持有 env:test,未重启 test)
|
||||
- cleanup: pending
|
||||
- remote_sync: not_requested
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,211 @@
|
||||
---
|
||||
title: "多仓库架构实施方案:U1–U10 执行计划"
|
||||
date: 2026-09-29
|
||||
type: implementation-plan
|
||||
task_id: multi-repo-design-2026-09-28
|
||||
basis:
|
||||
- .doc/remediation/2026-09-28-multi-repo-architecture-plan.md (rev 7)
|
||||
- .doc/audit/2026-09-28-multi-repo-evidence.md
|
||||
- 2026-09-29 只读基线核实(Gitea API + 本地仓库)
|
||||
policy_state: proposed
|
||||
execution_authority: "design_only; implementation requires user approval"
|
||||
---
|
||||
|
||||
# 多仓库架构实施方案(待审,不生效)
|
||||
|
||||
本文件把 2026-09-28 架构方案(rev 7)的 U1–U10 落成可执行的分阶段计划:
|
||||
每个阶段给出前置、具体步骤(含 Gitea API/命令级细节)、验收、回退和暂停准则。
|
||||
所有写操作(推送、建仓、改保护、停旧入口)都需用户按阶段批准后才执行;本文不构成授权。
|
||||
|
||||
---
|
||||
|
||||
## 一、2026-09-29 基线核实结果(只读)
|
||||
|
||||
### 与证据文档一致的事实
|
||||
|
||||
- Gitea 1.27.3,读取身份 `weichen`(实例 admin)。
|
||||
- 六仓库:Agent-System 与 Development-Manager 非空,其余四个(Squad / Development-Documents / Development-Guidelines / Releases)仍为空、public、default=main。
|
||||
- Agent-System 远端 dev/test/main 未动:`fabd85a` / `0e4b111` / `210fe4a`;Development-Manager 远端 dev=`8867e38` 与本地一致、干净。
|
||||
- 两仓 branch_protections 均为 `[]`,releases 为空。
|
||||
|
||||
### 相对证据文档的关键更新
|
||||
|
||||
| # | 更新事实 | 对方案的影响 |
|
||||
|---|---|---|
|
||||
| N1 | **实例级 runner 存在且在线**:`home-runner`(id=1,online,labels:`ubuntu-latest`/`docker`/`nightly`) | 架构方案"实施前须核实 runner"的前置已满足;U1/U2 的 Gitea 原生 CI 门禁可直接落地,但仍需用一个真实 workflow 验证标签匹配与执行语义 |
|
||||
| N2 | 本地 dev 领先 origin/dev 从 306 → **337** 提交,HEAD `1bb48030`(含方案三文档、rc-v1.1.0-beta.5/6 晋升、多个产品修复);无 behind,纯 fast-forward | U1 的"差异同步"对象扩大;同步本身仍是 U1 第一决策 |
|
||||
| N3 | 远端分支从 3 个 → **29 个**(大量 2026-09-22~23 的陈旧 feature/* 分支) | U1 基线盘点需含远端分支清理清单 |
|
||||
| N4 | 实例**单用户**(weichen,admin)、无组织 | KTD4 的"每人一个 fork"退化场景成立:首期不需要 fork,直接 repo 内 feature 分支 + PR(见决策 D2) |
|
||||
| N5 | 工作区有一个未提交删除:`.cursor/rules/agentctl-mandatory.mdc` | 属于 U2a 内容,不应混入 U1 基线同步;先恢复,在 U2a 作为首个提交处理 |
|
||||
| N6 | workflows 仍仅在 `.github/workflows/`(ci.yml、nightly-real-pi.yml),无 `.gitea/workflows/` | U2b 切换 CI 时需确认实例 ACTIONS 路径配置,避免双跑(沿用架构方案 KTD CI 节) |
|
||||
|
||||
凭证说明:`~/.secrets/gitea` 为 YAML(`url_prefix` + `access_token` 两键),token 对 API 有效(token/Bearer 均可)。本文件不含凭证值。
|
||||
|
||||
---
|
||||
|
||||
## 二、总体执行序列
|
||||
|
||||
```
|
||||
Phase 0 U1 基线与治理试点 ──────────────出口:治理替代清单可审、远端同步完成
|
||||
Phase 1 U2a 本地减负 ─→ U2b 远端交接 ───出口:编码流程零 agentctl 依赖(V19–V26)
|
||||
Phase 2a U7a Agent-Native 契约(最小模块)─出口:contract SHA 可供 U4 消费
|
||||
Phase 2b U4 Squad 提取 ∥ U7b Native 内核提取
|
||||
U3 文档仓拆分 ∥ U8 Web 拆分 ∥ U10 单文件/多实例(可在现仓并行早启)
|
||||
Phase 3 U5 外层仓收口 ─→ U6b 多仓组合发布
|
||||
早交付线 U10 → U6a 单仓单文件内部 RC(不等待拆仓,Phase 0 后即可并行)
|
||||
独立线 U9 PI parity(依赖 U7a 契约;能力盘点 U9a 可随时提前)
|
||||
```
|
||||
|
||||
架构方案定的依赖顺序不变:U1→U2→U7a→U4/U7b;U3/U8 穿插;U5 收口;U9 独立验收。
|
||||
每个 Phase 出口都是一个可暂停点:保留当时已验证的组合,不半开新门禁。
|
||||
|
||||
---
|
||||
|
||||
## 三、需用户确认的决策点(开工前批准)
|
||||
|
||||
| ID | 决策 | 建议 | 理由 |
|
||||
|---|---|---|---|
|
||||
| D1 | 337 提交如何同步到远端 dev | **整体 fast-forward push dev**(不 push 全部分支);test/main 暂不动 | 无 behind、无分叉,机械安全的路径;远端 dev 自 2026-09-24 起未变,阻塞了 U1 之后一切远端治理试点。push 前先 `git push --dry-run` + 备份 `git bundle` |
|
||||
| D2 | 单用户下的协作模型 | ~~不建机器人账号~~ → **D2'(09-29 演练修正):已创建机器账号 `agent-bot`**。演练证实 Gitea 1.27.3 硬禁止自批准(author approve own pull → "approve your own pull is not allowed",admin 亦不可),单账号下 required_approvals 永远无法满足。最终模型:编码 Agent 以 agent-bot 提交/建 PR,用户以 weichen 批准+合并。不建 fork | 见[演练记录](../audit/2026-09-29-governance-drill-record.md);agent-bot 凭据在 `~/.secrets/agent-bot`(token scope: write:repository, read:user) |
|
||||
| D3 | 新仓库创建时机 | 只新建 3 个:`Agent-Native`(U7a 时)、`Agent-System-Web`(U8 时)、`Agent-System-Core`(U5 时);`Agent-System-Releases-Internal`(U6a 前,**private**)。其余 4 个复用空仓 | 仓库跟着实施单元走,避免空壳仓库先行;Releases-Internal 必须 private 并做匿名拒绝验证(V5) |
|
||||
| D4 | runner 信任边界 | 实例 runner 只有一台;U2b 起 PR 触发的 workflow 一律**无秘密、无共享环境写权限**(`nightly` 标签任务除外,它持部署凭证但只由 protected 分支/维护者触发) | 单 runner 同时服务不可信 PR 代码与可信 nightly,必须按 Gitea Actions token 权限文档隔离;架构方案 CI Trust 节的落地 |
|
||||
| D5 | 29 个远端陈旧分支 | U1 盘点后按清单**删除已合入 dev 的**;保留 dev/test/main 与仍活跃的(如 feature/squad-missing-target-retry 系列) | 分支堆积干扰 U1 基线与后续 PR 流;删除可逆(reflog + 对象仍在) |
|
||||
| D6 | 工作区未提交的 `.cursor/rules/agentctl-mandatory.mdc` 删除 | U1 先 `git restore` 恢复;到 U2a 随"解除强制 hook"提交一起删除 | 保持 U1 基线同步为纯 fast-forward,不夹带治理变更 |
|
||||
| D7 | (09-29 用户指令)**不破坏现有 Agent-System 系统**:所有拆分/迁移工作放入 `~/projects/` 下**新目录**(单一目录+submodule 结构,即未来外层仓工作区);对现有仓的一切治理变更走 feature 分支 + PR,不直接改 dev 运行状态 | 新目录承载 Agent-Native/Web/Squad/Documents/Guidelines 各组件仓 clone 与外层结构;现有 Agent-System 保持在原位运行,直到 U5 收口以普通提交完成外层化 | 用户 09-29 指令;与 R9 可迁移可停止一致 |
|
||||
|
||||
---
|
||||
|
||||
## 四、Phase 0 — U1 基线与治理试点(预计 1–2 日)
|
||||
|
||||
目标:远端与本地一致、治理演练仓验证 Gitea 原生门禁真实行为、产出 U2 所需的完整替代清单。
|
||||
|
||||
### 步骤
|
||||
|
||||
1. **本地备份**:`git bundle create ../agent-system-dev-$(date +%F).bundle --all` 并验证 `git bundle verify`;记录恢复步骤。
|
||||
2. **处理脏工作区**(D6):`git restore .cursor/rules/agentctl-mandatory.mdc`。
|
||||
3. **盘点(只读,产出清单文件)**:
|
||||
- 本地/远端分支映射表(29 远端 + 本地 feature/* 与 docs/*),逐条标注 keep/delete/merge-pending;
|
||||
- 活跃 worktrees(当前 3 个:u9u12-keys-rollout 注册在册,另 2 个目录核查归属);
|
||||
- 运行制品身份:rc-v1.1.0-beta.* tags、主仓树内 *.test 二进制清单(后续移除对象,历史不重写);
|
||||
- `.locks/state.sqlite` 快照(busy/recovery_required 计数)与活跃 agentctl 任务。
|
||||
4. **同步主干**(D1 批准后执行):`git push --dry-run origin dev` 核对 fast-forward → `git push origin dev`。不 push 其他分支;test/main 不动。
|
||||
5. **建演练仓**(一次性、可删):创建 `weichen/governance-drill-2026-09`(临时 private),在其中验证以下 Gitea 原生行为,逐项记录证据(对应架构方案 V6/V23 的前置):
|
||||
- 分支保护 API:`POST /repos/weichen/governance-drill-2026-09/branch_protections`,字段组合:`enable_push=false`、`required_approvals=1`、`block_on_official_review_requests=true`、`dismiss_stale_approvals=true`、`status_check_contexts=["ci/verify"]`;
|
||||
- 验证旁路负例:API/Web/HTTP 直推被拒;无 approve 的合并被拒;head 更新后旧 approve 被撤销;required check 缺失时合并被拒;
|
||||
- 合并语义:merge/rebase/squash 各触发一次,记录 `merge_commit_id`/`head_commit_id` 行为,确认"合入后 tree 可核验"路径;
|
||||
- runner 真实执行:push 一个最小 workflow(`on: pull_request`,runs-on 匹配 `ubuntu-latest` 或 `docker`),确认实例 runner 认领并跑通,验证 job token 权限边界(尝试跨仓读应失败);
|
||||
- `.github/workflows/` 与 `.gitea/workflows/` 双路径是否双跑(决定 U2b 迁移路径)。
|
||||
6. **产出**:`.doc/ops/` 下新增《U1 基线清单》+《Gitea 门禁演练记录》;按 KTD13 更新 retain/replace/remove 矩阵为最终可审版。
|
||||
|
||||
### 出口 / 回退
|
||||
|
||||
- 出口:远端 dev=本地 dev;演练仓全部负例有证据;D5 清单获批;U2 范围冻结。
|
||||
- 回退:主干 push 不改变历史(纯 fast-forward),无需回退;演练仓直接删除。
|
||||
- 暂停准则:演练中任一门禁旁路成功(负例失败)→ 停止 U2 计划,先解决实例配置。
|
||||
|
||||
---
|
||||
|
||||
## 五、Phase 1 — U2 治理切换(预计 3–5 日,U1 后重估)
|
||||
|
||||
### U2a 本地减负(先做,只依赖 U1 的基线核对)
|
||||
|
||||
1. Development-Manager 侧修改 agentctl 职责(KTD13/KTD15):
|
||||
- 删除普通编辑/提交的全局 `with`/SQLite 前置;锁域改为 `workspace_id+path` 局部身份;
|
||||
- `overlaps` 不再比较 `path:.` 类全局资源;候选冲突只隔离该候选工作区;
|
||||
- 失败分类落地:preflight_rejected / failed_known / outcome_unknown(V21);
|
||||
- task 子系统退出编码流程:不再要求 register/claim/lease/close(历史只读保留)。
|
||||
2. 主仓侧配套(一次提交,含 N5 的 hook 删除):
|
||||
- 删除 `.cursor/rules/agentctl-mandatory.mdc` 与对应 hook 转发;
|
||||
- `AGENTS.md`/`PROJECT.md` 按三层规范(Guidelines → 各仓短文件 → CI/执行器规则)改写;
|
||||
- 保留纯 Git 主干误提交保护、秘密防护、单工作区 owner。
|
||||
3. 新增测试(Development-Manager):`test_workspace_isolation.py`、`test_operation_outcomes.py`、`test_task_lifecycle.py`、`test_cleanup_allowlist.py`;主仓保留与新合同相符的 `test_agentctl*.py` 子集。
|
||||
4. 验收(V19/V20/V22/V26 前半):两个 worktree 改同路径互不阻塞;停 daemon/损坏缓存仍可本地开发提交;完整"修复→测试→提交→PR"流程零 agentctl 调用。
|
||||
|
||||
### U2b 远端交接(依赖演练仓全部负例通过)
|
||||
|
||||
1. Agent-System 正式设置分支保护:dev/test/main 按演练确认的字段组合(D4 信任边界);首期 `required_approvals=1` 由用户人工 approve(D2)。
|
||||
2. 迁移 CI 到确定路径(避免双跑,N6);PR workflow 无秘密;nightly 保持维护者触发 + `nightly` 标签。
|
||||
3. 停用旧本地 dev 推进入口(`git-push-dev.sh` 直推路径、agentctl integrate 入口):先排空在途操作(`agentctl status` 无 running/busy),记录旧新权限清单,同一提交内生效。
|
||||
4. 切换验证(V23):旧 token/hook/本地 integrate 不能推进远端;无 approve/CI 的 PR 不能合并;陈旧 head 不能合并。
|
||||
5. 四类日常演练(V24):文档微调 / 单组件 bug / 跨仓 API 变更 / 共享 DB 迁移,确认低风险不再被旧模板强制全流程。
|
||||
|
||||
### 出口 / 回退
|
||||
|
||||
- 出口:远端唯一写权威=Gitea 保护分支+PR;本地编码零 agentctl;治理命令调用数显著下降(记录基线对比)。
|
||||
- 回退:若保护/CI 行为异常,恢复旧入口前必须先关闭新写权威(不同时双开);保留旧管理库只读。
|
||||
|
||||
---
|
||||
|
||||
## 六、Phase 2 — 组件拆分(可部分并行)
|
||||
|
||||
### U7a Agent-Native 最小契约(预计 1–2 日,Phase 1 后立即)
|
||||
|
||||
1. 现 chat-agent-system 仓内冻结助手/Squad 执行行为与副作用语义(不改默认后端);
|
||||
2. 定义 contract 包(Execute 输入输出/事件/错误/能力/取消),不依赖 config/workspace;
|
||||
3. 新建 `weichen/Agent-Native` 仓(D3),首提交只含 contract + go.mod + conformance fake 测试;
|
||||
4. 推送、打 tag(如 `contract/v0.1.0`),产出 U4 可固定的 SHA。
|
||||
|
||||
### U4 Squad 提取(预计 5–8 日)∥ U7b Native 内核(预计 4–7 日)
|
||||
|
||||
- U4 按 KTD1:先现仓依赖注入/材料解析去耦(保持行为)→ filter-repo 抽取至 `Agent-System-Squad`(复用空仓)→ 主仓留临时 facade 适配 29 个调用文件 → 迁移算法测试 + 新增双侧 contract_test。主仓以相邻 replace(`../squad`、`../agent-native`)联调,`GOWORK=off` 正式构建。
|
||||
- U7b 迁 agentx/runtime、工具/模型/执行会话、PI transport 适配器至 Agent-Native;`pi-native` 旧名 alias、AGENTX_* 配置、transcript 根兼容期保留(V16)。
|
||||
- 共同出口:两库均无 `chat-agent-system/internal/*` import、可独立 `go build`/`go test`;真实 PI 与 Native 分别验证,fallback 不冒充 native PASS(V13)。
|
||||
|
||||
### U3 文档拆分(预计 2–3 日)
|
||||
|
||||
- 通用规范 → `Development-Guidelines`(复用空仓);静态设计/审计 → `Agent-System-Development-Documents`(复用空仓);
|
||||
- 机器契约 `module-boundaries.yaml` 留代码仓 `configs/architecture/`;CURRENT/MATRIX 活动文档**暂留**原路径(KTD2 首期不整体替换);
|
||||
- 主仓建 `development/` gitlinks;文档仓内容经 PR 合入后由主仓 gitlink 显式采纳;
|
||||
- 历史导入前先扫描将公开内容(架构方案 Risks 节的阻断出口)。
|
||||
|
||||
### U8 Web 拆分(预计 3–5 日)
|
||||
|
||||
- 迁 web 源码/npm 锁/前端测试至 `Agent-System-Web`(D3 新建);Go embed 入口留应用侧 `internal/webassets`;
|
||||
- 构建链:固定 Web SHA → `npm ci && npm run build` → dist 物化到隔离构建上下文 → 摘要校验后编译;
|
||||
- static_assets 多路径猜测在发布模式禁用(V15);新增 `test_web_asset_assembly.py` 与 manifest 校验。
|
||||
|
||||
### U10 单文件/多实例(可与 Phase 2 并行,基于 U1 最低基线)
|
||||
|
||||
- U10a:迁移 SQL/prompts/xlsx 等内嵌 fs.FS;同一程序 serve/init/migrate/config 子命令;
|
||||
- U10b:ConfigSnapshot 统一(默认→.env→进程 env→CLI→instance-settings),字段唯一 owner,saved/applied 分离(V28/V32);
|
||||
- U10c:实例隔离(instance_id/目录重叠/cookie 独立/进程归属,V29–V31/V33/V34);managed-local PG 为可选后续 profile,首期 CLI 明确 unsupported。
|
||||
|
||||
---
|
||||
|
||||
## 七、Phase 3 — 收口与发布
|
||||
|
||||
- **U5 外层仓**(2–4 日):剩余产品代码 → `Agent-System-Core`;主仓变纯组合入口(components.yaml + gitlinks + 构建配方);验证全新 recursive clone / 源码 bundle / 无原路径构建(V9)。
|
||||
- **U6b 多仓发布**(2–3 日):同一配方切换固定组件组合,重跑 U6a 全部验收;私有 RC → 用户审批 → 公开晋升,附件摘要一致(V5/V10/V11)。
|
||||
|
||||
## 八、早交付线 — U10 → U6a 单仓单文件内部 RC
|
||||
|
||||
Phase 0 后即可并行:在现单仓完成 U10a/U10b 最小集 → `scripts/build-single-binary.sh` 完善 → 干净 Ubuntu 26.04 amd64 验收(V9/V27 部分)→ 发布至 `Agent-System-Releases-Internal`(新建 private,D3)→ 匿名/无权限下载拒绝验证(V5)。不等待任何拆仓。
|
||||
|
||||
## 九、独立线 — U9 PI parity
|
||||
|
||||
U9a 盘点(1–2 日)可立即做:固定上游 0.85.1 三层基线,产出 parity matrix(available/partial/missing/not_required + 证据)。U9b–e 依赖 U7a 契约与 U7b 内核;全程禁用 fallback 验收 native(V17/V18),分批计划在 U9a 后单独报批。
|
||||
|
||||
---
|
||||
|
||||
## 十、风险与暂停准则(汇总)
|
||||
|
||||
| 风险 | 触发信号 | 动作 |
|
||||
|---|---|---|
|
||||
| Gitea 保护旁路 | 演练负例未全部拒绝 | 停 U2b;不上线新权威 |
|
||||
| 单 runner 被不可信代码滥用 | PR workflow 越权访问跨仓/秘密 | 收紧 token/标签路由;必要时暂停 PR CI |
|
||||
| 拆仓等价性破坏 | Squad/Native contract 回归失败 | 保留旧组合;该组件暂停,其他组件不受牵连 |
|
||||
| 单文件验收失败 | 干净机启动缺资产/误连 dev DB | U6a 不发布;修 U10 对应项 |
|
||||
| 历史含敏感内容 | 公开导入扫描命中 | 阻断对应仓库公开化;仅走内部包 |
|
||||
| 在途治理操作未排空 | 切换时存在 running/recovery_required | 延后 U2b 切换;不清空管理库 |
|
||||
|
||||
三条独立回退路径保持不变:产品代码回退(git)、部署制品回退(Release 附件)、DB/用户文件恢复(备份)。
|
||||
|
||||
## 十一、验收映射
|
||||
|
||||
V1–V5/V10/V11 → Phase 0/U6;V6/V23/V24 → Phase 1;V13/V16/V19–V22/V25/V26 → Phase 1–2;V7/V8 → U4/U3;V9/V15/V27–V34 → U10/U6a/U6b;V12 → U5/U6b;V14 → U7 后新增 adapter 时;V17/V18 → U9。全部证据落 `.doc/audit/` 或 Release 元数据,绑定最终 SHA。
|
||||
|
||||
---
|
||||
|
||||
## Definition of Done(本方案自身)
|
||||
|
||||
用户批准决策点 D1–D6 并批准首个 Phase 后,本文件升级为生效执行计划;各单元完成以架构方案 Definition of Done(U1–U8、U10 出口 + V 证据)为准。未获批准前,一切远端写操作不执行。
|
||||
@@ -0,0 +1,460 @@
|
||||
# 智能体工作过程流式展示与 subagent 子页面设计
|
||||
|
||||
> 版本:v1.0(设计稿,未实施)
|
||||
> 日期:2026-09-28
|
||||
> 任务:subagent-process-view-design-2026-09-28
|
||||
> 作者:Cursor(owner),三路并行代码探索 + 主线调研交叉验证
|
||||
> 交付物性质:系统设计文档,**不含任何代码实施**(用户明示"可以先不实现")
|
||||
|
||||
---
|
||||
|
||||
## A. 目标与参照
|
||||
|
||||
### A.1 用户需求
|
||||
|
||||
1. 让助手的工作过程像豆包、kimi、codex 等工具一样对外输出:**思考(thinking)、工具调用(tool call)、过程内容输出**三类信息;
|
||||
2. 使用**流式输出**持续更新助手的工作过程,而非终态一次性呈现;
|
||||
3. 多智能体小队设计 **subagent 子页面**承载上述展示(先设计,不实现)。
|
||||
|
||||
### A.2 参照产品的展示形态拆解
|
||||
|
||||
| 产品 | 思考展示 | 工具调用展示 | 增量输出 | 交互模式 |
|
||||
|---|---|---|---|---|
|
||||
| 豆包 | "深度思考"折叠块:思考中呼吸态滚动,完成后折叠为一行摘要,可点击展开全文 | 工具卡片:图标+工具名+入参摘要+结果摘要+耗时 | 正文逐 token 流式 | 主流内嵌折叠,不打断阅读 |
|
||||
| kimi | 推理过程折叠区,默认收起,进行中有进度感 | 搜索/工具步骤条 | 逐段流式 | 同上 |
|
||||
| codex(CLI/Web) | 思考行内灰字流式 | 命令执行块(命令+输出+退出码) | 流式 | 工程向,密度更高 |
|
||||
|
||||
共同设计语言:**默认折叠(低认知负荷)→ 状态可感知(进行中动效)→ 按需展开(全文/详情)→ 归档后可回放**。
|
||||
|
||||
### A.3 本仓已有基座与约束
|
||||
|
||||
本设计不是从零发明,而是延伸既有三层信息架构(2026-09-08 streaming-events 设计,已归档但语义仍有效):
|
||||
|
||||
- **L1 phase/status**("现在在干嘛"):已实现并持久化(`task_run_events`);
|
||||
- **L2 summary/delta/briefing**("结论是什么"):已实现(delta 为整段伪流式);
|
||||
- **L3 detail**("推演依据"):**协议已定义(`DetailPayload.category: agent_thought/tool_call/step_output/log`),但无生产者、双重丢弃**——本设计的主战场。
|
||||
|
||||
既有 UX 约束必须继承:
|
||||
|
||||
1. **不打扰原则**:detail 默认折叠,不默认展开思考链/工具日志(2026-09-09 chat-wait-error-retry 设计 P1 明确);
|
||||
2. **透明可追溯**:详细思考与草稿不丢失,按需可查(工作区文件留存兜底);
|
||||
3. **治理红线**:过程展示只是可视化,**不改变验收证据边界**——PI 叙事、模型输出不能冒充 PASS/测试证据(AGENTS.md 安全与证据)。
|
||||
|
||||
### A.4 概念区分(重要)
|
||||
|
||||
本设计的 "subagent" 指**产品内小队成员**(`internal/squad` 的 AgentSpec:organizer/regulatory/writer/leader 等 8 标准专员 + ad_hoc 派生专员),**不是**本仓开发流程的工程工具 PI 子任务派发(`scripts/pi_subagents.py` + `.doc/ops/2026-09-24-pi-subagent-executor.md`)。两者名字相近但完全不同域,实施时不得混用数据源。
|
||||
|
||||
---
|
||||
|
||||
## B. 过程事件协议扩展
|
||||
|
||||
### B.1 现状协议回顾
|
||||
|
||||
`internal/stream/types.go` 的 `StreamEvent` 已定义 12 种事件类型;其中 `DetailPayload`:
|
||||
|
||||
```go
|
||||
// internal/stream/types.go:68
|
||||
type DetailPayload struct {
|
||||
Category string `json:"category"` // agent_thought, step_output, tool_call, log
|
||||
StepID string `json:"step_id,omitempty"` // 步骤标识 (如 tasks/01_regulatory)
|
||||
Role string `json:"role,omitempty"` // 对应角色
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"` // 完整推演正文或技术草稿
|
||||
Collapsible bool `json:"collapsible"`
|
||||
}
|
||||
```
|
||||
|
||||
现状问题:
|
||||
|
||||
1. 只有 `Content` 全文单帧,无增量模式(思考链逐 token 需要数百帧,全文帧会放大 N 倍流量);
|
||||
2. 无 `RunID` 冗余(事件重放时按 run 已隔离,可省;但 live 合并需要前端从通道上下文取);
|
||||
3. 并行步骤下 `Role`/`StepID` 已有,但前端折叠键没有用它们(见 B.4)。
|
||||
|
||||
### B.2 DetailPayload 扩展(增量模式)
|
||||
|
||||
```go
|
||||
// 设计草案(不实施)
|
||||
type DetailPayload struct {
|
||||
Category string `json:"category"` // agent_thought | tool_call | step_output | log(沿用四类)
|
||||
StepID string `json:"step_id,omitempty"` // 步骤标识(如 tasks/03_regulatory)
|
||||
Role string `json:"role,omitempty"` // 成员角色 key(如 regulatory)
|
||||
RunID string `json:"run_id,omitempty"` // 冗余 run 标识(重放通道按 run 已隔离,可选)
|
||||
Seq int64 `json:"seq,omitempty"` // 同一 (category, step_id, role, item_id) 流内的序号
|
||||
ItemID string `json:"item_id,omitempty"` // 聚合项 ID:一个思考块/一次工具调用/一份产出
|
||||
// 增量模式三选一:
|
||||
Delta string `json:"delta,omitempty"` // 增量帧:追加到 ItemID 项
|
||||
Content string `json:"content,omitempty"` // 终帧/快照帧:ItemID 项的完整内容(或无增量能力的发送方)
|
||||
Done bool `json:"done,omitempty"` // 终帧标记(ItemID 项结束)
|
||||
Title string `json:"title,omitempty"`
|
||||
Collapsible bool `json:"collapsible"`
|
||||
// tool_call 特化字段(Category == "tool_call" 时):
|
||||
Tool *ToolCallInfo `json:"tool,omitempty"`
|
||||
}
|
||||
|
||||
type ToolCallInfo struct {
|
||||
Name string `json:"name"` // write_file / read_file / grep ...
|
||||
Status string `json:"status"` // calling | succeeded | failed
|
||||
Input string `json:"input,omitempty"` // 入参摘要(JSON,可能分帧)
|
||||
Output string `json:"output,omitempty"` // 结果摘要(可能分帧)
|
||||
DurationMs int64 `json:"duration_ms,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
**增量语义**(关键设计决策):
|
||||
|
||||
- 发送方优先逐帧 `Delta`;`Content` 用于两类场景:(a) 无流能力的发送方(如既有 `step_output` 整段产出),(b) 周期性**快照对账帧**(防止增量丢帧后内容漂移);
|
||||
- 客户端按 `(StepID, Role, Category, ItemID)` 聚合:`Delta` 追加、`Content` 覆盖、`Done` 定稿;
|
||||
- 节流(发送方义务,见 F.1):逐 token 事件合并为 ~100–300ms 批帧,单帧上限(如 8KB)超限分帧。
|
||||
|
||||
### B.3 llm 层事件扩展(reasoning 链)
|
||||
|
||||
`internal/agentx/llm` 现有事件只有 `text_delta/tool_call_delta/message_end/error`。OpenAI 兼容国产模型(MiniMax/DeepSeek 系)在 delta 中返回 `reasoning_content` 字段(思考链)。扩展:
|
||||
|
||||
```go
|
||||
// internal/agentx/llm/types.go 设计草案
|
||||
const (
|
||||
KindTextDelta EventKind = "text_delta"
|
||||
KindReasoningDelta EventKind = "reasoning_delta" // 新增
|
||||
KindToolCallDelta EventKind = "tool_call_delta"
|
||||
...
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Kind EventKind
|
||||
Text string
|
||||
ReasoningDelta string // 新增:reasoning_content 增量
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
`ParseSSE`(internal/agentx/llm/sse.go)补充解析 `delta.reasoning_content` → `KindReasoningDelta`。
|
||||
|
||||
**容器侧对应**:`deploy/pi-agent/runner.mjs:24` 的 `stripThinkBlocks` 目前把 `<think>…</think>` 块从产物中剥离(丢弃)。设计上不改变"正文不含 think"的既有约定,而是在**剥离前**把 think 块作为 reasoning 事件分流转发(见 C.5)。
|
||||
|
||||
### B.4 并行步骤归属路由(修复既有缺陷)
|
||||
|
||||
现状 `PhasePayload` 折叠键为 `phase_id#step_index`([taskrun/phase_snapshot.go:225](../../../internal/taskrun/phase_snapshot.go) 与 [web/src/engine/phaseEventMapper.ts:65](../../../web/src/engine/phaseEventMapper.ts)),**并行组成员**(多个 role 同时 `step_executing`)若无 step_index 会互相覆盖/误折叠。
|
||||
|
||||
设计:折叠键升级为 `phase_id#step_index@role`(role 存在时必选参与键)。前端 `applyPhaseEvent` 与后端 `FoldPhaseEvents` 同步修改,语义保持对齐(两处折叠逻辑本就要求一致,见 phase_snapshot.go 注释)。detail 事件天然带 `role/step_id`,聚合不受影响。
|
||||
|
||||
### B.5 通道选择:沿用 stream/StreamEvent,不新增域事件
|
||||
|
||||
理由:
|
||||
|
||||
1. `events.StaticContracts`(internal/events/registry.go)是静态 allowlist(13 种业务事件 + YAML parity 测试),加 agent 过程事件需改 Go+YAML+测试三处,且域事件总线面向**业务投影**(通知/阶段推进),不面向用户实时流;
|
||||
2. `task_run_events`(JSONB payload)+ `after_seq` 重放已为过程事件准备好持久化与断线恢复;
|
||||
3. chat SSE 通道(链路 A)无持久化,但 B 轨参谋对话的过程展示可接受"live-only + 终态落库"(chat_turns 记录终态),与现状一致。
|
||||
|
||||
---
|
||||
|
||||
## C. 后端链路打通设计(七断点逐条)
|
||||
|
||||
### C.0 总览
|
||||
|
||||
```
|
||||
【链路 B:长任务(本设计主通道,先做)】
|
||||
worker goroutine (taskrun.Manager.runWorker)
|
||||
└ bizdev.ExecuteTask → squad.WorkflowDriver.runOneStep
|
||||
└ agent.Backend.Execute(OnEvent 回调) ← 断点 4
|
||||
├ pi-native: agentx.Run(OnEvent) ← 断点 2/3(直通)
|
||||
│ └ llm.Stream: reasoning/text/tool delta ← 断点 1
|
||||
└ pi-agent: POST runner /v1/run/stream ← 断点 5(NDJSON 流)
|
||||
└ Streamer.EmitDetail(category, stepID, role, ...) ← 断点 6 之前:detail 已能出 Streamer
|
||||
└ Manager.appendEvent → task_run_events (seq) ← 持久化(已就绪)
|
||||
└ SSE: GET /tasks/runs/{id}/events ← 透传(已就绪)
|
||||
└ 前端 detail 渲染分支 ← 断点 7
|
||||
|
||||
【链路 A:聊天 SSE(P4 再做)】
|
||||
chat.Session → coordinator.generateDialogueReply
|
||||
└ dialogueBackend.Execute(OnEvent) → token 级 EmitDelta(替换一次性 EmitDelta 伪流式)
|
||||
```
|
||||
|
||||
### C.1 断点 1:reasoning_content 解析
|
||||
|
||||
- 文件:[internal/agentx/llm/sse.go](../../../internal/agentx/llm/sse.go)(`sseChoice.Delta` 增 `ReasoningContent string \`json:"reasoning_content"\``)、[internal/agentx/llm/types.go](../../../internal/agentx/llm/types.go)(`Event` 增 `ReasoningDelta` + `KindReasoningDelta`)
|
||||
- 语义:reasoning 与 text 分开累计;`message_end` 的 `Message.Content` 仍只含 text(与 runner `stripThinkBlocks` 语义对齐:正文无 think)。
|
||||
- 兼容性:无 reasoning 的模型零行为变化(字段空即无事件)。
|
||||
|
||||
### C.2 断点 2:agentx 循环转发 tool_call_delta
|
||||
|
||||
- 文件:[internal/agentx/loop.go:178-183](../../../internal/agentx/loop.go) `consume()`:`case llm.KindToolCallDelta` 增加转发(emit 一个 `tool_call_delta` Event,带 ToolName/ArgumentsDelta);`KindReasoningDelta` 同理转发 `reasoning_delta`。
|
||||
- `agentx.Event`(loop.go:19)增字段:`Kind string`(llm 层事件细分)或复用 `Type` 前缀约定(`text_delta`/`reasoning_delta`/`tool_call_delta`/`tool_execution_start`/`tool_execution_end`…已够用,推荐复用 Type)。
|
||||
|
||||
### C.3 断点 3:Backend 接口过程回调
|
||||
|
||||
**设计取舍**:不改 `Backend.Execute` 签名(改签名会波及 stub/fallback/limiter 全部实现与调用方),改为 **Request 增可选回调**:
|
||||
|
||||
```go
|
||||
// internal/agent/backend.go 设计草案
|
||||
type Request struct {
|
||||
...
|
||||
// OnEvent 过程事件回调(nil = 不需要过程,行为与现状完全一致)
|
||||
// 实现方必须保证:回调在 Execute 返回前完成(同步或等待收尾),无泄漏 goroutine。
|
||||
OnEvent func(ev ProcessEvent)
|
||||
}
|
||||
|
||||
// ProcessEvent 对 agentx.Event 与 runner NDJSON 事件的统一投影
|
||||
type ProcessEvent struct {
|
||||
Type string // text_delta | reasoning_delta | tool_call_delta | tool_execution_start | tool_execution_end | message_end | ...
|
||||
Text string // delta 文本(Type 为 *delta 时)
|
||||
Tool string // 工具名
|
||||
Detail string // 工具参数/结果摘要(tool_execution_* 时)
|
||||
}
|
||||
```
|
||||
|
||||
- pi-native([internal/agent/pinative.go](../../../internal/agent/pinative.go)):`agentx.Run` 的 `OnEvent` 双写——既拼 `execution.log`(现状保留),又投影到 `req.OnEvent`;
|
||||
- `WrapWithConcurrencyLimit` 透传 Request 即透传回调,无需改;
|
||||
- fallback/stub:fallback 转发 primary 的回调事件即可;stub 无事件。
|
||||
|
||||
### C.4 断点 4:squad 层桥接 Streamer
|
||||
|
||||
[internal/squad/workflow.go](../../../internal/squad/workflow.go) `runOneStep`(构造 `agent.Request` 处,约 :543):
|
||||
|
||||
```go
|
||||
// 设计草案
|
||||
if req.OnEvent == nil {
|
||||
req.OnEvent = func(ev agent.ProcessEvent) { /* 仅拼日志(现状) */ }
|
||||
}
|
||||
// 注入流式桥(streamer 可为 nil,安全)
|
||||
req.OnEvent = chainEvents(req.OnEvent, squadDetailBridge(streamer, step))
|
||||
```
|
||||
|
||||
`squadDetailBridge`(新函数,~40 行)把 `ProcessEvent` 翻译为 `EmitDetail`:
|
||||
|
||||
| agentx/runner 事件 | EmitDetail 映射 |
|
||||
|---|---|
|
||||
| `reasoning_delta` | category=agent_thought, delta 帧, ItemID=`step.ID#thought<N>` |
|
||||
| `text_delta` | category=agent_thought 的草稿子项(或 P2 可选:不改,任务正文走终态 step_output,避免与最终 deliverable 重复)→ **推荐 P0 不发 text_delta,仅 reasoning + tool** |
|
||||
| `tool_call_delta` | category=tool_call, delta 帧(入参增量), ItemID=`step.ID#tool<N>` |
|
||||
| `tool_execution_start` | tool_call 项 status=calling 帧 |
|
||||
| `tool_execution_end` | tool_call 项 status=succeeded/failed + Output 摘要 + duration 帧 |
|
||||
| `message_end` | 对应 ItemID 的 done 帧 |
|
||||
|
||||
节流在 bridge 内做(100–300ms 合并、8KB 分帧、工具输出截断 N KB——工具结果可能巨大,只发首尾摘要)。
|
||||
|
||||
**B 轨参谋(链路 A,P4)**:[internal/coordinator/dialogue_llm.go](../../../internal/coordinator/dialogue_llm.go) `generateDialogueReply` 同样注入 `OnEvent`,把 `text_delta` 逐帧转 `streamer.EmitDelta`(替换 [internal/chat/session.go:624](../../../internal/chat/session.go) 一次性 `EmitDelta(NaturalReply)`),`reasoning_delta` 转 `EmitDetail(agent_thought)`。注意 chat SSE 无持久化——断线后靠 `chat_turns` 终态查询(现状机制),过程帧不可恢复(可接受,与豆包/kimi 行为一致)。
|
||||
|
||||
### C.5 断点 5:远程 runner 流式端点
|
||||
|
||||
[deploy/pi-agent/runner.mjs](../../../deploy/pi-agent/runner.mjs) 增加端点(不动 `/v1/run` 既有契约——`ResultV1` typed 校验、wire 预算 8MB 等全部保留):
|
||||
|
||||
```
|
||||
POST /v1/run/stream Content-Type: application/x-ndjson(chunked)
|
||||
请求体 = /v1/run 同构 + stream: true
|
||||
响应 = 逐行 NDJSON:
|
||||
{"type":"proc","ev":{...pi RPC stdout 事件行...}} // 过程事件(原样转发已解析的行)
|
||||
...(执行中持续 flush)
|
||||
{"type":"result","body":{...与 /v1/run 完全相同的 typed 结果...}} // 终帧
|
||||
```
|
||||
|
||||
- runner 侧改动集中在 `handleRun`:stdout `on("data")` 里既有的逐行 JSON 解析循环(runner.mjs:365-388)旁路一个转发器;`stripThinkBlocks` 之前先把 think 块作为 `{"type":"reasoning_delta"}` proc 帧发出;
|
||||
- Go 侧 [internal/agent/piagent.go](../../../internal/agent/piagent.go) `executeRemote`:`req.OnEvent != nil` 时改走流式端点——`http.Client` 无总超时的流式变体(`Timeout` 由 ctx 控制),bufio 逐行读:`proc` 帧 → `req.OnEvent` 投影;`result` 帧 → 走既有 `DecodeRemoteRun` 校验路径(**终态校验逻辑零改动**,保证治理边界不放松);
|
||||
- 降级:流式端点 404(旧 runner)→ 回落 `/v1/run` 一次性调用 + 无过程事件(`capabilities` 探测亦可,但 404 回落更简单)。
|
||||
|
||||
### C.6 断点 6:includeDetails 参数化
|
||||
|
||||
两处硬编码/丢弃:
|
||||
|
||||
1. [internal/taskrun/manager.go:364](../../../internal/taskrun/manager.go) `stream.WithListener(ctx, false, listener)`——worker 侧 Streamer 是否发 detail 由**创建时**决定。设计:`CreateAndStart`/`StartClaimedRun` 透传 `IncludeDetails`(`RunRecord` 或 `CreateOptions` 增字段;默认 false 保持现状)。**注意**:detail 是否持久化与是否推送解耦——建议 **detail 一律持久化**(`task_run_events` JSONB 足够),`includeDetails` 只控制 live 推送与 chat 通道;重放端点用 query `include_details=0` 过滤,历史 subagent 页面仍可全量回放。取舍理由:持久化膨胀可控(F.1 节流后估算),而"刷新后过程消失"违背透明可追溯原则;
|
||||
2. [internal/server/sse.go:476](../../../internal/server/sse.go) `_ = includeDetails`——`StreamChatRequest.IncludeDetails` 已存在(chat 通道)但 task 通道读取后丢弃;接线到 worker 创建。
|
||||
|
||||
### C.7 断点 7:前端协议与分支
|
||||
|
||||
- [web/src/api/chatStream.ts](../../../web/src/api/chatStream.ts) `StreamEvent` 增 `detail?: { category?: string; step_id?: string; role?: string; item_id?: string; seq?: number; delta?: string; content?: string; done?: boolean; title?: string; collapsible?: boolean; tool?: { name?: string; status?: string; input?: string; output?: string; duration_ms?: number } }`;
|
||||
- [web/src/hooks/useTaskRunAttempts.ts](../../../web/src/hooks/useTaskRunAttempts.ts) `onEvent` switch 增 `case 'detail'`:聚合进消息的 `processItems`(见 D.4),不进正文。
|
||||
|
||||
---
|
||||
|
||||
## D. subagent 子页面设计(重点)
|
||||
|
||||
### D.1 入口与路由
|
||||
|
||||
**决策:扩展 hash 路由,不引入 react-router。**
|
||||
|
||||
理由:
|
||||
|
||||
| 方案 | 优点 | 代价 |
|
||||
|---|---|---|
|
||||
| A. hash 扩展(推荐) | 复用 [web/src/engine/anchorNavigator.ts](../../../web/src/engine/anchorNavigator.ts) 既有解析/监听先例(`#/s/{sessionId}/m/{messageId}`);零新依赖;与"工作台为唯一应用视图"的产品形态匹配 | 顶层 view 状态机要加一态;深链参数解析手写 |
|
||||
| B. react-router | 标准 | 新依赖 + 全仓路由心智迁移;App.tsx 1758 行重构风险;本需求只有一个子页面,收益不成比例 |
|
||||
|
||||
路由形态:
|
||||
|
||||
```
|
||||
#/s/{sessionId}/run/{runId}/agents → 子页面(默认选中当前 running/最近完成成员)
|
||||
#/s/{sessionId}/run/{runId}/agents/{roleId} → 子页面(选中指定成员)
|
||||
```
|
||||
|
||||
实现骨架(设计草案):
|
||||
|
||||
- App.tsx 顶层 view 状态机扩展:`'workbench' | 'admin' | 'subagents'`;`parseAnchorHash` 扩展识别 `/run/{runId}/agents` 段;
|
||||
- `handleSubagentsView` 由 TaskRunCard 的"查看小队过程"入口触发(写 hash 触发 hashchange,与消息深链同一机制);
|
||||
- 子页面经 `OnDemand`([web/src/components/lazy/OnDemand.tsx](../../../web/src/components/lazy/OnDemand.tsx))懒加载,**props 最小化**:仅 `runId`、`sessionId`、`onExit`(返回 workbench)——规避 App.tsx props 钻透:子页面自持 SSE 订阅与状态,不依赖父容器消息流。
|
||||
|
||||
### D.2 页面信息架构
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 小队过程 · {任务标题} · run:{runId 截断} [刷新] [返回对话] │
|
||||
├──────────────┬───────────────────────────────────────────────┤
|
||||
│ 成员卡列表 │ 选中成员(role: regulatory 法规专员) │
|
||||
│ │ ┌ 状态条: ⚙️ 正在执行 · 已用 2m13s · 第2/5步 ┐ │
|
||||
│ 👤 organizer │ └──────────────────────────────────────────┘ │
|
||||
│ ✅ 完成 1m2s│ ┌ ThinkingBlock(默认折叠)──────────────┐ │
|
||||
│ 👤 searcher │ │ 💭 思考中…(流式增量,呼吸态) │ │
|
||||
│ ✅ 完成 3m4s│ └───────────────────────────────────────┘ │
|
||||
│ 👤 regulatory│ ┌ ToolCallCard ─────────────────────────┐ │
|
||||
│ ⚙️ 执行中 │ │ 🔧 read_file tasks/01.../output.md │ │
|
||||
│ 👤 writer │ │ ✅ 213ms · 12KB │ │
|
||||
│ ⏳ 等待 │ └──────────────────────────────────────┘ │
|
||||
│ 👤 leader │ ┌ ThinkingBlock(第二段)──────────────┐ │
|
||||
│ ⏳ 等待 │ ... │ │
|
||||
│ │ ┌ 步骤产出 output.md(终态 step_output)┐ │
|
||||
│ [成员详情] │ │ 📄 阶段性产出(可展开全文) │ │
|
||||
│ │ └──────────────────────────────────────┘ │
|
||||
├──────────────┴───────────────────────────────────────────────┤
|
||||
│ 事件时间线(跨成员 phase 事件折叠,复用 applyPhaseEvent 语义) │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **左栏数据源**:`squad.json`(成员编制:role/Name/Responsibilities——工作区文件,经新只读端点或既有 workspace API 投影)+ phase/detail 事件聚合出状态与耗时(`step_executing`/`step_completed`/`step_failed` by role);
|
||||
- **右栏数据源**:detail 事件流按 `role == selected` 过滤,聚合为 `processItems`(见 D.4);
|
||||
- **底部时间线**:全量 phase 事件(复用 `TaskRunCard` 折叠逻辑与 `PhaseEvent` 类型)。
|
||||
|
||||
### D.3 三类过程组件
|
||||
|
||||
| 组件 | 状态 | 内容来源 | 交互 |
|
||||
|---|---|---|---|
|
||||
| `ThinkingBlock` | thinking(呼吸态+增量滚动)/ done(折叠为一行摘要:前 40 字 + 字数)/ error | `agent_thought` 增量帧聚合 | 默认折叠;点击展开全文(Markdown 渲染);进行中可"实时展开" |
|
||||
| `ToolCallCard` | calling(转轮)/ succeeded(绿勾+耗时)/ failed(红叹+错误摘要) | `tool_call` 帧聚合(name/input/output/status/duration) | 入参/结果默认摘要(N KB 截断),点击展开 JSON/代码(**需补块级渲染**,见 D.6) |
|
||||
| `ProcessStepOutput` | done | 既有 `step_output` 整段 content | 默认折叠,展开渲染 Markdown |
|
||||
|
||||
组件均为纯展示组件(props 驱动),放置 `web/src/components/subagents/`,配 `node:test` 静态断言测试(与仓内组件测试形态一致)。
|
||||
|
||||
### D.4 前端数据流与状态
|
||||
|
||||
```typescript
|
||||
// 设计草案:web/src/hooks/useRunProcessStream.ts
|
||||
type ProcessItem = {
|
||||
key: string; // `${stepId}/${itemId}`
|
||||
category: 'agent_thought' | 'tool_call' | 'step_output' | 'log';
|
||||
role: string;
|
||||
stepId: string;
|
||||
title?: string;
|
||||
text: string; // delta 累计或 content 快照
|
||||
status: 'streaming' | 'done' | 'error';
|
||||
tool?: { name: string; status: string; input?: string; output?: string; durationMs?: number };
|
||||
updatedAt: number;
|
||||
};
|
||||
|
||||
function applyDetailEvent(items: Record<string, ProcessItem>, ev: DetailEvent): Record<string, ProcessItem>
|
||||
// 纯函数(与 applyPhaseEvent 同风格,node:test 可测):
|
||||
// delta → 追加 text、status=streaming
|
||||
// content → 覆盖 text(快照对账)
|
||||
// done → status=done
|
||||
// tool 帧 → 合并 tool 字段
|
||||
```
|
||||
|
||||
订阅策略(复用既有骨架):
|
||||
|
||||
- **live**:`subscribeTaskRunEvents(runId, { onEvent, afterSeq })`([web/src/api/taskRuns.ts:113](../../../web/src/api/taskRuns.ts)),事件分发:`phase` → 左栏状态 + 底部时间线;`detail` → `applyDetailEvent`;
|
||||
- **刷新/进入已结束 run**:先 `readTaskRunSSEOnce(runId, 0, …, { replayOnly: true })` 全量重放(事件已持久化,见 C.6),再决定是否续订 live;
|
||||
- **超量回放保护**:`after_seq` 分页重放(既有能力),detail 大 run 可先只重放 phase + 按需拉成员 detail(`GET /runs/{id}/events?role=xxx&include_details=1` 过滤参数为可选增强,P2.5)。
|
||||
|
||||
### D.5 与主对话气泡的联动
|
||||
|
||||
- 气泡内 `TaskRunCard` 保持现状(L1 时间线),新增两处:
|
||||
1. 运行中:卡片底部一行"查看小队过程 →"(写 hash 深链进子页面);
|
||||
2. 完成后:L3 摘要条(如"5 名专员 · 12 次工具调用 · 3 段推演"),点击同样进子页面;
|
||||
- **不把全量 detail 渲染进气泡**——认知负荷原则:气泡只承载 L1/L2,L3 全文在子页面。`MessageBubble` 现有 `message.phaseEvents` 通道不动。
|
||||
|
||||
### D.6 前端基础设施缺口补齐(实施前置)
|
||||
|
||||
1. **块级 Markdown + 代码高亮**:现状 `renderInlineMarkdown` 仅行内语法([web/src/engine/renderMarkdown.ts](../../../web/src/engine/renderMarkdown.ts))。工具调用入参/结果需要 fenced code block + JSON 高亮。方案:`renderMarkdown` 增块级分支(heading/fence/list——解析器自研或引入 `marked`/`react-markdown`;倾向自研轻量扩展,避免新依赖与 XSS 面扩大,沿用现有 escape 纪律);
|
||||
2. **高频增量渲染性能**:processItems 更新频率受后端节流(100–300ms 批帧)保护;子页面右栏**不用虚拟列表**(成员过程项数量级 ~10²),但聚合函数必须纯函数 + `React.memo` 行项;
|
||||
3. **通用折叠组件**:自研 `CollapsibleBlock`(受控 `open` + `Summary` 插槽),三组件复用。
|
||||
|
||||
### D.7 后端配套只读端点(成员编制)
|
||||
|
||||
子页面左栏需要 `squad.json`。设计(P2 内最小):
|
||||
|
||||
```
|
||||
GET /api/v1/tasks/runs/{run_id}/squad
|
||||
→ { members: [{ role, name, responsibilities, source }], task_key, title }
|
||||
```
|
||||
|
||||
实现:taskrun Manager 读 `record.WorkspaceID` → 工作区 `squad.json` 反序列化(只读投影,权限沿用 run 事件的 `RequireProjectAccess`)。
|
||||
|
||||
---
|
||||
|
||||
## E. 分期实施路线
|
||||
|
||||
| 期 | 内容 | 验证形态(按验证契约) | 依赖 |
|
||||
|---|---|---|---|
|
||||
| **P0 协议+直通** | B.2/B.3 协议扩展;C.1–C.4(pi-native 全链路);C.6 includeDetails 参数化 + detail 持久化;前端 chatStream 类型 + detail 分支(仅聚合不渲染) | Go 单测(llm sse_test 增 reasoning case、squad bridge 节流测试);stub/pi-native E2E;真实 PI Runner(dev 环境)冒烟 | 无 |
|
||||
| **P1 气泡过程块** | D.5 气泡内 L3 摘要 + 折叠 ThinkingBlock(最小渲染,块级 markdown 只做 fence+JSON) | 前端 node:test + 真实浏览器验收(交互变更) | P0 |
|
||||
| **P2 subagent 子页面** | D.1–D.7 全量(hash 路由、成员卡、过程流、squad 端点) | 前端单测 + Playwright spec(新增 e2e/subagents-view.spec.ts)+ 真实 PI 全链路验收 | P1 |
|
||||
| **P2.5(可选)** | 事件过滤参数(role/include_details query);大 run 分页回放 | 接口测试 | P2 |
|
||||
| **P3 runner 流式** | C.5 `/v1/run/stream` + piagent 流式消费 + 404 回落 | runner 单测(mjs 侧 settle-policy 风格)+ 真实容器验收(dev) | P0(可并行) |
|
||||
| **P4 B 轨参谋** | C.4 尾段:dialogue_llm OnEvent → token 级 EmitDelta + reasoning detail | 真实 PI 对话链路验收(chat SSE) | P0 |
|
||||
| **P5(演进)** | 历史回放端点(transcript.jsonl/execution.log 投影 API);per-member 持久化表(如 `task_run_step_events`,仅当 JSONB 容量实测超限) | 视实测 | P2/P3 |
|
||||
|
||||
每期独立合入(Conventional Commits:`feat(stream)/feat(web)` 前后端分原子提交),P0 即可演示 pi-native 路径的"思考流+工具卡"。
|
||||
|
||||
---
|
||||
|
||||
## F. 风险与边界
|
||||
|
||||
### F.1 事件量与持久化膨胀
|
||||
|
||||
- **节流义务在 bridge**(C.4):reasoning/text delta 合并 100–300ms 批帧;单帧 ≤8KB;工具输出截断(如 4KB 首部 + 省略标记);`step_output` 保持现状单帧(终态产物已有 `deliverable` 通道与文档存储,不重复全文持久化时可评估只存摘要——P0 先全文,P2.5 实测再收紧);
|
||||
- **容量估算**:单 run 5 成员 × (1 思考块 2–8KB + 6 工具调用 × 1KB 摘要) ≈ 50KB/run 量级(节流后),`task_run_events` JSONB 单行无硬限,可控;
|
||||
- **兜底**:`execution.log`/`transcript.jsonl` 仍全量落盘工作区(现状不变),P5 投影 API 作历史回放。
|
||||
|
||||
### F.2 认知负荷与安全
|
||||
|
||||
- detail 默认折叠贯穿三处(气泡摘要、子页面 ThinkingBlock、ToolCallCard 入参/结果摘要)——继承 2026-09-09 设计 P1 约束;
|
||||
- **脱敏**:bridge 层过滤——不透传 prompt/AGENTS.md 全文(这些是任务输入不是过程);工具入参只发 workspace 相对路径相关字段;配置/密钥字段(provider/key)永不进事件(`ProcessEvent` 设计上就不含这些来源);
|
||||
- **治理红线**:过程事件不写 `events` 域总线、不参与验收判定;测试/审查结论仍以报告路径与 receipt 为准(pi-subagents 规则),本设计纯可视化。
|
||||
|
||||
### F.3 兼容与降级
|
||||
|
||||
- 旧 runner(无 `/v1/run/stream`)→ 404 回落一次性调用,功能退化为 L1 时间线(现状);
|
||||
- `OnEvent == nil` → 所有后端行为与现状完全一致(回调是纯增量);
|
||||
- 前端不识别 detail(旧前端 + 新后端)→ JSON 字段忽略,无破坏;
|
||||
- `includeDetails` 默认 false → chat 通道现状不变。
|
||||
|
||||
### F.4 并行正确性
|
||||
|
||||
- 并行组多成员同时发 detail:`Streamer.Emit` 已有 RWMutex;bridge 无共享状态(每 step 独立闭包);
|
||||
- 前端聚合键含 role/stepID(B.4),并行不串扰;
|
||||
- `step_output` 终态帧在并行完成回调内发出(现状 workflow.go:746 位置不变)。
|
||||
|
||||
### F.5 明确不做
|
||||
|
||||
- 不引入 WebSocket(SSE + after_seq 重放已覆盖需求);
|
||||
- 不做过程事件的域总线登记(B.5 理由);
|
||||
- 不做 B 轨 chat 过程帧的持久化与断线重放(P4 live-only,终态落库现状);
|
||||
- 本期不实现任何代码(用户授权边界)。
|
||||
|
||||
---
|
||||
|
||||
## G. 附录:断点-文件-方案对照表
|
||||
|
||||
| # | 断点 | 文件:行 | 方案章节 |
|
||||
|---|---|---|---|
|
||||
| 1 | reasoning_content 未解析 | internal/agentx/llm/sse.go、types.go | C.1 / B.3 |
|
||||
| 2 | agentx 丢弃 tool_call_delta | internal/agentx/loop.go:178-183 | C.2 |
|
||||
| 3 | Backend 黑盒无回调 | internal/agent/backend.go:38、pinative.go:102 | C.3 |
|
||||
| 4 | squad 层无成员过程流 | internal/squad/workflow.go:543(runOneStep) | C.4 |
|
||||
| 5 | runner /v1/run 一次性 | deploy/pi-agent/runner.mjs:600 | C.5 |
|
||||
| 6 | includeDetails 硬编码 false / 丢弃 | internal/taskrun/manager.go:364、server/sse.go:476 | C.6 |
|
||||
| 7 | 前端无 detail 协议与分支 | web/src/api/chatStream.ts、hooks/useTaskRunAttempts.ts:474 | C.7 |
|
||||
|
||||
## H. 参考文件
|
||||
|
||||
- 三层信息架构基座:`.doc/archive/design/superpowers/specs/2026-09-08-streaming-events-and-digestible-ux-design.md`
|
||||
- 认知负荷约束:`.doc/archive/design/superpowers/specs/2026-09-09-chat-wait-error-retry-ux-design.md`
|
||||
- 小队编排(含 §18.5 演进方向):`.doc/system-design/agent-squad/02-智能体小队编排-设计-思路-架构-实现-难点-解决方案-亮点与优势.md`
|
||||
- 长任务 resume:`.doc/archive/design/superpowers/specs/2026-09-09-longtask-phase-live-resume-ux-design.md`
|
||||
- 任务记录:`.doc/ops/short-term/2026-09-28-subagent-process-view-design.md`
|
||||
@@ -7,6 +7,14 @@
|
||||
|
||||
## 📋 报告清单
|
||||
|
||||
### 0c. 小队过程流式展示与 subagent 子页面(2026-09-28)
|
||||
|
||||
| 项 | 链接 |
|
||||
|-----|------|
|
||||
| 主设计 | [2026-09-28-agent-process-streaming-and-subagent-view-design.md](2026-09-28-agent-process-streaming-and-subagent-view-design.md) |
|
||||
| 任务 | [../ops/short-term/2026-09-28-subagent-process-view-design.md](../ops/short-term/2026-09-28-subagent-process-view-design.md) |
|
||||
| 一句话总结 | 思考/工具调用/过程内容七断点打通(reasoning 解析→Backend 回调→runner 流式→detail 持久化)+ subagent 子页面(hash 路由 + 成员卡 + 过程流组件);设计稿未实施 |
|
||||
|
||||
### 0b. 自由协作与认领提醒(2026-09-24)
|
||||
|
||||
| 项 | 链接 |
|
||||
|
||||
@@ -25,9 +25,12 @@ imports:
|
||||
- config
|
||||
authz:
|
||||
- docduty
|
||||
bootstrap:
|
||||
- config
|
||||
bizdev:
|
||||
- authz
|
||||
- agent
|
||||
- app/uow
|
||||
- authz
|
||||
- config
|
||||
- conflictresolve
|
||||
- contracts/vocabulary
|
||||
@@ -49,6 +52,8 @@ imports:
|
||||
- squad
|
||||
- storage
|
||||
- stream
|
||||
- template
|
||||
- textextract
|
||||
- workflowdef
|
||||
- workspace
|
||||
chat:
|
||||
@@ -77,6 +82,7 @@ imports:
|
||||
- kb
|
||||
- maturity
|
||||
- projectinstance
|
||||
- projectworkflow
|
||||
- squad
|
||||
- stream
|
||||
- workflowscope
|
||||
@@ -212,6 +218,7 @@ imports:
|
||||
- kb
|
||||
- mineru
|
||||
- squad
|
||||
- textextract
|
||||
maturity: []
|
||||
mineru: []
|
||||
notification: []
|
||||
@@ -241,6 +248,7 @@ imports:
|
||||
- auth
|
||||
- authz
|
||||
- bizdev
|
||||
- bootstrap
|
||||
- chat
|
||||
- config
|
||||
- contracts/vocabulary
|
||||
@@ -270,22 +278,21 @@ imports:
|
||||
- storage
|
||||
- stream
|
||||
- stream/transport
|
||||
- supplementimpact
|
||||
- swboot
|
||||
- sysadmin
|
||||
- taskrun
|
||||
- textextract
|
||||
- traceimpact
|
||||
- upload
|
||||
- workflowdef
|
||||
- workflowscope
|
||||
- workspace
|
||||
- supplementimpact
|
||||
squad:
|
||||
- agent
|
||||
- config
|
||||
- quote
|
||||
- stream
|
||||
- template
|
||||
- workspace
|
||||
- textextract
|
||||
- workflowdef
|
||||
storage:
|
||||
- config
|
||||
@@ -304,6 +311,7 @@ imports:
|
||||
- projectworkflow
|
||||
- stream
|
||||
- workspace
|
||||
textextract: []
|
||||
template:
|
||||
- workflowdef
|
||||
upload: []
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# 助手回复智能体叙述 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
项目助手大量查询、帮助和操作回执仍由 Go 硬编码模板直接返回。本轮把这些路径改为:系统先生成确定性事实底稿,再由产品 PI Agent Runner 组织自然语言;Runner 失败、开关关闭或无 backend 时回退原模板。安全拦截、权限不足和确认提问不进入叙述。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- T1 权责查询、T2 能力帮助、T3 暂缓回执由真实 Runner 生成,`reply_source=pi-agent`,且保留底稿中的人名、文档名或项目名。
|
||||
- Runner 失败时回退模板,并记录 `agent_reply_fallback`。
|
||||
- 开关关闭时不调用 backend。
|
||||
- 安全拦截话术不被叙述改写。
|
||||
- 存量 `go test -short ./...` 与 `arch-check --mode block` 通过。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- OS:Linux。Go 模块测试在任务 worktree `feature/agent-reply-llm-narrative-2026-09-28`,基线 dev `8f77053f5169502eaf3e4e4135a082e8914f3704`。
|
||||
- 真实 Runner:宿主机 `http://127.0.0.1:18865/health` 返回 200。模型按本机批准配置 `PI_AGENT_PROVIDER=minimax`、`PI_AGENT_MODEL=MiniMax-M2.7-highspeed`。凭证只从本机 `.env` 读取,不写入报告。
|
||||
- 层级:Go 单测(scripted backend)+ 受影响产品 PI 链路(`RUN_PI_E2E=1`)。未跑 nightly 五项全量,因为本变更不改工作流生成链。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
- 有 backend 且开关开启:`ReplySource=pi-agent`,提示词含「系统确定性底稿」。
|
||||
- backend 返回失败:`ReplySource=template`,正文仍含模板标题「我能帮您做什么」。
|
||||
- `AGENT_REPLY_LLM` 测试开关关闭:不调用 backend。
|
||||
- `SECURITY_FORBIDDEN`:正文不含脚本注入的智能体改写句。
|
||||
- 真实 Runner 三轮均 `source=pi-agent`,且分别保留「张工/用户需求表」「查询/补充」「肌骨超声项目」。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
### 用例 1:真实 Runner T1/T2/T3
|
||||
|
||||
- **测试输入 (Input)**:`RUN_PI_E2E=1 go test -count=1 -timeout 8m -v ./internal/coordinator/ -run TestIntentReply_RealPI_NarratesQueryHelpAndDecline`
|
||||
- **实际输出 (Actual Output)**:
|
||||
|
||||
```text
|
||||
intent=QUERY_USER_RIGHTS source=pi-agent duration=8.919s
|
||||
intent=HELP_CAPABILITIES source=pi-agent duration=11.655s
|
||||
intent=DECLINE_EXPECTATION source=pi-agent duration=6.329s
|
||||
--- PASS: TestIntentReply_RealPI_NarratesQueryHelpAndDecline (26.90s)
|
||||
```
|
||||
|
||||
权责回复含「张工」与「用户需求表」;帮助回复含「查询项目进展与风险」「补充项目事实」,并保留「请前往项目设置界面」;暂缓回复含「肌骨超声项目暂不推进」。路由日志里的 `channel=TEMPLATE` 是 R0 意图通道名,不是回复来源;回复来源字段为 `pi-agent`。
|
||||
|
||||
- **评价判定**:PASS
|
||||
- **性能与耗时**:三轮合计 26.90s(8.919s / 11.655s / 6.329s)
|
||||
|
||||
### 用例 2:backend 失败回退模板
|
||||
|
||||
- **测试输入**:`go test -short -v -run TestIntentReply_BackendFailureFallsBackToTemplate`
|
||||
- **实际输出**:
|
||||
|
||||
```text
|
||||
INFO agent_reply_fallback handler=handleHelpCapabilities intent=HELP_CAPABILITIES reason="forced failure"
|
||||
--- PASS: TestIntentReply_BackendFailureFallsBackToTemplate (0.00s)
|
||||
```
|
||||
|
||||
- **评价判定**:PASS
|
||||
- **性能与耗时**:<1s(scripted backend,非真实 Runner)
|
||||
|
||||
### 用例 3:开关关闭与安全话术
|
||||
|
||||
- **测试输入**:`TestIntentReply_FlagOffKeepsTemplate`、`TestIntentReply_SecurityForbiddenStaysTemplate`
|
||||
- **实际输出**:两者 PASS。安全用例未调用 dialogue backend。
|
||||
- **评价判定**:PASS
|
||||
- **性能与耗时**:<1s
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
| 检查 | 结果 |
|
||||
|---|---|
|
||||
| `./scripts/arch-check.sh --mode block` | PASS(production gate passed) |
|
||||
| `go vet ./...` | PASS(exit 0) |
|
||||
| `go test -short -count=1 -timeout 8m ./...` | PASS(含 `internal/coordinator` 0.042s、`internal/server` 20.498s) |
|
||||
| `RUN_PI_E2E=1` 叙述三轮 | PASS(26.90s) |
|
||||
|
||||
未运行 nightly 全量五项。不把未运行记为产品全链路 PASS。
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
授权范围内的叙述改造达到本轮验收:真实 Runner 能在保留确定性事实的前提下改写查询、帮助和暂缓回执;失败与安全边界仍走模板。
|
||||
|
||||
后续可观察线上 `agent_reply_fallback` 比例,再决定是否把仍保持模板的确认提问也改为叙述。确认卡选项与权限拒绝本轮故意不改。
|
||||
@@ -0,0 +1,103 @@
|
||||
# 调研报告受控投影项目绑定 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
项目会话里触发调研报告后,助手称正文已保存,刷新文档页仍显示模板骨架。根因是会话授权项目 ID 没有回填到协调器读取的 `UserCtx.Project.ID`,受控投影被跳过。任务记录见 [短文](../ops/short-term/2026-09-28-controlled-projection-project-binding.md)。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- 已授权会话在派发前把项目 ID 写入协调器上下文,且不覆盖已有绑定。
|
||||
- 协调器触发任务时 `TaskRunRequest` 带上项目 ID 与 `SessionID`。
|
||||
- 受控任务键在 `ProjectID <= 0` 时失败关闭,不建工作区。
|
||||
- 完成回复在没有受控账本记录时不称「正文已保存」。
|
||||
- 既有阶段编排、异步 run 与流式任务在带上项目 ID 后仍能完成。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- OS:Linux。工作树:`.worktrees/controlled-projection-project-binding-2026-09-28`。
|
||||
- 基线 `5d762fb2061113285ee21219471baaf11cb2d770`。被测提交 `f8730791b9a0a9d4ac67586f622acb6afaf43f10`。
|
||||
- 层级:Go 接口短测、`go vet ./...`、架构边界 `arch-check --mode block`。
|
||||
- 产品 PI 浏览器链路依赖已加载本提交的 dev 进程。本报告记录短测时,运行中的 `bin/agent-server` 仍是合入前的主目录制品。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
- `bindSessionProjectContext` 把空项目 ID 填成会话项目;已有值保持不变;未授权项目不回填。
|
||||
- 同步触发 research 时请求的 `ProjectID` 与 `SessionID` 与上下文一致。
|
||||
- `intake` / `research` 在项目 ID 为 0 时返回 `ErrUnboundControlledProject`,`DocRecord` 为空。
|
||||
- 无 `DocRecord` 的完成回复包含「尚未写入受控文档账本」,且不含「正文已保存」。
|
||||
- `go vet ./...` 无输出;三包 `go test -short` 通过;架构边界通过。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
### 用例 1:项目绑定回填
|
||||
|
||||
- 输入:`go test -count=1 -short -timeout 180s ./internal/server/ -run TestBindSessionProjectContext`(含在用例 4 的整包短测中)。
|
||||
- 实际输出:`internal/server` 整包 `ok`,20.400s。用例覆盖回填 6024、保留已有 `"9"`、`SetProjectID(0)` 时不写入。
|
||||
- 判定:PASS
|
||||
- 耗时:见整包短测
|
||||
|
||||
### 用例 2:触发请求携带项目与会话
|
||||
|
||||
- 输入:`go test -count=1 -short ./internal/coordinator/ -run 'TestSyncTriggerTask|TestFormatTriggerTaskReply'`
|
||||
- 实际输出:`internal/coordinator` 整包 `ok`,0.015s。`TestSyncTriggerTask_EnablesAutoRepair` 断言 `ProjectID==9` 且 `SessionID==sess-research`。
|
||||
- 判定:PASS
|
||||
|
||||
### 用例 3:无项目绑定失败关闭,回复不声称入账
|
||||
|
||||
- 输入:`go test -count=1 -short ./internal/bizdev/ -run 'TestExecuteTask|TestTaskRunResult_'`
|
||||
- 实际输出:`internal/bizdev` 整包 `ok`,0.180s。受控键无项目时 `errors.Is` 为 `ErrUnboundControlledProject`。无账本的完成摘要包含「尚未写入受控文档账本」。未绑定项目的同步触发回复为该错误原文,状态 `StatusRejected`。
|
||||
- 判定:PASS
|
||||
|
||||
### 用例 4:三包短测、vet、架构边界
|
||||
|
||||
- 输入:
|
||||
|
||||
```text
|
||||
gofmt -w internal/server/task_run_observer_test.go
|
||||
go test -count=1 -short -timeout 180s ./internal/bizdev/ ./internal/coordinator/ ./internal/server/
|
||||
go vet ./...
|
||||
./scripts/arch-check.sh --mode block
|
||||
```
|
||||
|
||||
- 实际输出:
|
||||
|
||||
```text
|
||||
ok chat-agent-system/internal/bizdev 0.180s
|
||||
ok chat-agent-system/internal/coordinator 0.015s
|
||||
ok chat-agent-system/internal/server 20.400s
|
||||
VET:0
|
||||
arch-check mode=block
|
||||
ok chat-agent-system/internal/archtest 0.194s
|
||||
ok chat-agent-system/internal/archtest 0.191s
|
||||
arch-check block: production gate passed
|
||||
ARCH:0
|
||||
```
|
||||
|
||||
- 判定:PASS
|
||||
- 耗时:三包短测约 23s;vet 与架构检查约 2s。工作目录为任务 worktree。
|
||||
|
||||
说明:`TestTaskRun_DisconnectDoesNotCancelWorker` 曾因 worker 用 `RunRecord.ProjectID` 覆盖请求而失败。测试记录改为 `ProjectID: 1` 后通过。这与线上异步 run 以记录中的项目 ID 为准一致。
|
||||
|
||||
### 用例 5:dev 真实 PI 浏览器链路
|
||||
|
||||
- 输入:在已加载 `f8730791` 的项目会话中对话触发调研报告,核对 `workspace.json` 的 `project_id`、`document_versions` 的 agent_squad 提交,以及刷新后的文档正文。
|
||||
- 实际输出:未运行。当前 dev 的 `bin/agent-server` 早于本提交,主目录 `dev` 尚未合入该提交。
|
||||
- 判定:未运行
|
||||
- 风险:短测证明绑定与失败关闭,不能代替刷新后文档页看到正文。
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
| 检查 | 结果 |
|
||||
|---|---|
|
||||
| `go test -short` `internal/bizdev` `internal/coordinator` `internal/server` | PASS |
|
||||
| `go vet ./...` | PASS |
|
||||
| `./scripts/arch-check.sh --mode block` | PASS |
|
||||
| 产品 PI 浏览器 | 未运行 |
|
||||
|
||||
未跑全仓 `go test -short ./...`。本改动的包与架构门禁已覆盖计划中的回归范围。
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
授权内的代码与短测达到任务分支可审终点。合入 local dev 并重载 `agent-server` 之后,仍须用真实 PI 在项目会话触发调研报告,确认工作区 `project_id` 非 0、账本出现 agent_squad 版本,刷新文档页不再只剩模板骨架。
|
||||
|
||||
孤儿工作区 `research_1790576535990100562`、`research_1790585141260333084` 保持 cleanup=pending,不删除。`doc_proj6024_bd_research` 已有带绑定重跑入账,无额外数据修复。
|
||||
@@ -0,0 +1,55 @@
|
||||
# docgov 共享 map 并发读写 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
2026-09-28 06:38 UTC,dev 后端因 `internal/docgov` 共享 map 并发读写 fatal 退出。Vite 代理在后端缺席期间对登录等全部 API 返回 500。本报告记录修复后的工程回归。产品 PI 全链路不适用:变更是治理引擎内存锁,不改对话或文档业务规则。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- 不同 docID 并发创建、读取、列审计/批注、写批注时,`-race` 不报告 data race,进程不 fatal。
|
||||
- 并发结束后每个 docID 仍能读回。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- OS:Linux;`go version go1.27.1 linux/amd64`
|
||||
- 工作区:`.worktrees/docgov-shared-map-race-2026-09-28`,提交 `a90cce73`
|
||||
- 层级:模块单元测试 + race detector。无数据库。产品 PI:NOT REQUIRED(非产品工作流变更;依据验证契约对纯工程锁修复的范围,以及本报告不把单测写成产品 PASS)。
|
||||
- 用例:`TestMemMapsNoRaceAcrossDocs`
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
`go test -race` 该用例 PASS,且 48 个文档均可 `GetDocument`。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
- 测试输入 (Input):`go test -race -count=1 -timeout 180s -v ./internal/docgov/ -run TestMemMapsNoRaceAcrossDocs`
|
||||
- 实际输出 (Actual Output):
|
||||
|
||||
```
|
||||
=== RUN TestMemMapsNoRaceAcrossDocs
|
||||
--- PASS: TestMemMapsNoRaceAcrossDocs (0.00s)
|
||||
PASS
|
||||
ok chat-agent-system/internal/docgov 1.012s
|
||||
```
|
||||
|
||||
- 评价判定 (Evaluation / Verdict):PASS
|
||||
- 性能与耗时 (Duration & Latency):包测试约 1.012s;用例本身 0.00s(race 插桩耗时在包级)
|
||||
|
||||
同一次会话里不带 `-run` 的 `go test -race -count=1 -timeout 180s ./internal/docgov/` 亦为 `ok`(1.012s)。
|
||||
|
||||
合入 dev `b9339cd2` 后 `./scripts/launch.sh --no-docker` 重建并重启(2026-09-28 09:10 UTC,PID 2447734):
|
||||
|
||||
- `GET /health` → HTTP 200,`status=ok`,`schema_version=78`
|
||||
- `POST /api/v1/auth/login` 空 JSON → HTTP 401,`error=用户名或密码不能为空`,`request_id=req_e28309cc8d615ed4`
|
||||
- `GET http://127.0.0.1:3100/api/v1/auth/public-config` → HTTP 200
|
||||
|
||||
评价:PASS。这确认进程在听并且登录校验生效,不是产品登录业务的新用例。
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
- `go test -race ./internal/docgov/`:PASS(含 `TestMemMapsNoRaceAcrossDocs`)
|
||||
- 未跑产品 PI、未跑全仓 `go test`
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
工程回归与 dev 重启复验都已完成。`ISSUE-20260926-docgov-memdocs-poison` 仍开放。
|
||||
@@ -0,0 +1,71 @@
|
||||
# 资料类提问意图与回复 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
test 会话里「上一代产品说明书」「竞品的操作视频」被收成模糊咨询,助手回复阶段推销模板(`reply_source=template`)。本轮在 `fix/intent-material-fallback-2026-09-28`(基线 `8f77053f`)把资料名词改到知识检索,未命中再走对话模型,并让模型早退留下 `fallback_reason`。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- 三条原始资料输入不再是 `AMBIGUOUS_FALLBACK`,slot 为 `query_topic=materials`,置信度 ≥ 0.75。
|
||||
- 无对话模型时,非犹豫输入不得出现「掂量」模板;犹豫输入仍保留对照。
|
||||
- `reply_source=template` 时 metadata 带 `fallback_reason`。
|
||||
- dev 数据库与 dev runner 上的真实对话,`reply_source` 为 `kb` 或 `pi-agent`,正文不含阶段推销。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- OS:Linux。Go 模块 `chat-agent-system`。工作树 `.worktrees/intent-material-fallback-2026-09-28`。
|
||||
- 单测:`go test ./internal/intent/ ./internal/coordinator/ ./internal/chat/ -count=1`。
|
||||
- 真实链路:用工作树源码构建的进程,环境变量取自正在运行的 dev `agent-server`(`ENV=dev`,`DATA_DIR=./data/dev`,`PI_AGENT_RUNNER_URL=http://127.0.0.1:18865`,知识库在线)。监听 `127.0.0.1:18080`,避免替换尚未合入的 `:8080` 二进制。账号为 dev 管理员,项目 6024「吞咽电刺激」(dev 库没有 test 上的项目 8)。
|
||||
- 预存失败对照:同一语义记忆用例在未改动的主目录 `dev` `8f77053f` 上同样失败。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
- 资料名词:`QUERY_SIMILAR_CASES`,`materials`,置信度 0.82。
|
||||
- 有知识库命中:`reply_source=kb`,正文引用材料,不含「掂量」。
|
||||
- 撰写类输入不标成资料检索。
|
||||
- 犹豫输入在模型不可用时仍含阶段对照,并带 fallback 原因。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
### 单测
|
||||
|
||||
- **输入**:`go test ./internal/intent/ ./internal/coordinator/ ./internal/chat/ -count=1 -timeout 240s`
|
||||
- **实际输出**:intent `ok`(0.072s),coordinator `ok`(0.014s)。chat 仅 `TestChatSession_SemanticMemoryRecallAndAsyncTurnRecord` 失败(2.01s,`session_test.go:556`)。同一命令在主目录未改代码上同样失败(2.02s)。本改动的资料、模板和 `fallback_reason` 用例通过。
|
||||
- **评价**:本任务用例 PASS。语义记忆索引用例为基线既有失败,不作为本改动回归。
|
||||
- **耗时**:约 2.5s。
|
||||
|
||||
### 真实对话(项目 6024,新会话 `sess-intent-material-probe-2`)
|
||||
|
||||
- **输入**:`POST /api/v1/chat`,消息「上一代产品说明书」。
|
||||
- **实际输出**:HTTP 200,8.46s。`top_intent=QUERY_SIMILAR_CASES`,置信度 0.82,`filled_slots.query_topic=materials`。`reply_source=kb`。正文以「关于「吞咽电刺激」里您要的「上一代产品说明书」,检索到这些材料」开头,不含「掂量」。`chat_messages.metadata.reply_source=kb`。
|
||||
- **评价**:PASS。
|
||||
- **耗时**:8.46s。证据 [probe-clean.json](assets/2026-09-28-intent-material-fallback/probe-clean.json)。
|
||||
|
||||
### 同一进程上的另外两条(会话 `sess-intent-material-probe`)
|
||||
|
||||
- **输入**:「类似的全自动产品的说明书」(11.19s)、「竞品的操作视频」(9.72s)。该会话在更早一次失败请求里记下了项目名「全自动冲击波治疗仪1」,所以引用抬头沿用了这个名字;意图与来源不受影响。
|
||||
- **实际输出**:两条均为 HTTP 200,`reply_source=kb`,`has_sales_template=false`。库中助手行 `reply_source=kb`。
|
||||
- **评价**:PASS(路由与来源)。项目名抬头是探测会话复用,不是资料分流缺陷。
|
||||
- **耗时**:见 [probe.json](assets/2026-09-28-intent-material-fallback/probe.json)。
|
||||
|
||||
### 官方 :8080
|
||||
|
||||
- **输入**:同一「上一代产品说明书」打到正在运行的 `bin/agent-server`(pid 1002830,基线二进制)。
|
||||
- **实际输出**:项目 8 在 dev 库不存在,请求 500。项目 6024 的既有日志仍把低置信输入记成 `AMBIGUOUS_FALLBACK`(03:26:06Z)。本二进制尚未替换 :8080。
|
||||
- **评价**:未在 :8080 上复验新代码。合入并重启后再记。
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
| 套件 | 结果 |
|
||||
|---|---|
|
||||
| `go test ./internal/intent/` | PASS |
|
||||
| `go test ./internal/coordinator/` | PASS |
|
||||
| `go test ./internal/chat/` | 1 个基线失败(语义记忆异步索引),其余通过;本任务新增用例通过 |
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
资料分流在 dev 库、dev runner、工作树二进制上达到「命中知识库则 `reply_source=kb`,不再输出阶段推销模板」。对话模型早退的日志与 `fallback_reason` 有单测;这次真实回合走了知识库,没有触发早退。
|
||||
|
||||
尚未合入 local dev:`task register` 被硬重叠拒绝,持有者 `agent-reply-llm-narrative-2026-09-28`(`same_scope`,scopes `.doc` 与 `internal`)。:8080 仍是旧二进制。test 目录属主与 runner 端口不在本轮。
|
||||
|
||||
阶段推进失败后没有助手消息另记 ISSUE-20260928-advance-stage-silent-fail,本报告不把它记成已修。
|
||||
@@ -0,0 +1,95 @@
|
||||
# 成员文档可见性与对话上下文注入 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
测试项目中,测试员工能作为项目成员打开项目,但流程受控文档被前端全局角色过滤成 0 份。对话触发的调研任务工作区只留下「开关关闭或无可共享对话」占位,是因为会话只写入数字项目 ID,协调器读取的字符串项目 ID 仍为空,对话包直接不采集。可行性评估在调研仍是模板时,回复只列信息缺口,不提示可先调研。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- 会话绑定后,协调器上下文的项目 ID 与数字用户 ID 与会话一致。
|
||||
- 未注入对话时,说明能区分开关关闭、项目未绑定、本轮没有对话包;有统计时 README 写明保留与滤除数量。
|
||||
- 已打开项目中,项目成员能看见本项目流程受控文档;指定人、角色白名单和私密文档规则不变。
|
||||
- 用户要写可行性评估且调研未完成时,回复建议先调研;阶段拒绝时附上推进说明。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- 任务分支 `feature/member-visibility-dialogue-ctx-2026-09-28`,合入候选 `7eb9477d9edffd3f39aa7026a874169c6efb190b`,第一父 `8889011e5b6bd35a1d992ae5aec3f1c1c8bdae31`。
|
||||
- 合入后本地 `dev` 指向同一候选。
|
||||
- 工程验收在合入候选 worktree 上由 `agentctl validate` 重跑。
|
||||
- 产品 PI 与测试员工浏览器全链路:未运行。原因见第七节。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
- `arch-check`、`go vet ./...`、`go test -count=1 -short ./...` 通过。
|
||||
- `web` 下 `npm test` 与 `npm run build` 通过。
|
||||
- 成员可见性用例:非成员访客看不到流程受控文档;`projectMember: true` 时可见;`designated_only` 仍然拒绝。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
### 1. 架构门禁
|
||||
|
||||
- 输入:`./scripts/arch-check.sh --mode block`
|
||||
- 首次输出:`NEW import chat internal/chat/workflow_guidance.go workflowdef`
|
||||
- 处理:调研键只在 chat 内识别 `research` 与 `bd_research`,不再引用 `workflowdef`。
|
||||
- 复跑:`arch-check block: production gate passed`
|
||||
- 判定:PASS
|
||||
|
||||
### 2. Go short
|
||||
|
||||
- 输入:`go vet ./... && go test -count=1 -short ./...`
|
||||
- 实际:候选上 validate 退出码 0;`internal/archtest`、`internal/chat`、`internal/squad`、`internal/server` 等均为 ok。
|
||||
- 判定:PASS
|
||||
- 耗时:候选上整段 validate(含前端)约 48 秒。
|
||||
|
||||
### 3. 前端
|
||||
|
||||
- 输入:`npm test`、`npm run build`
|
||||
- 实际:node:test 543 通过、0 失败;vitest 3 文件 34 通过;`tsc && vite build` 成功。
|
||||
- 判定:PASS
|
||||
|
||||
### 4. 身份回填与阶段建议
|
||||
|
||||
- 输入:`go test -count=1 -short ./internal/chat/`
|
||||
- 覆盖:`TestSetProjectIDSyncsCoordinatorProject`、`TestBindCoordinatorIdentityUsesNumericUser`、`TestWorkflowPrerequisiteHintSuggestsResearch`、`TestAugmentWorkflowGuidanceSetsExpectationAndStageNote`
|
||||
- 判定:PASS
|
||||
|
||||
### 5. 对话占位说明
|
||||
|
||||
- 输入:`go test -count=1 -short ./internal/squad/`
|
||||
- 覆盖:未绑定项目的说明含「任务未绑定项目」且不写 `turns.md`;已绑定但无对话包的说明含「没有对话包」;开关关闭含「开关已关闭」;有统计时 README 含「采集统计:保留 2 轮」。
|
||||
- 判定:PASS
|
||||
|
||||
### 6. 运行中的 dev 制品
|
||||
|
||||
- 输入:`GET /api/v1/health`;`ss` 查看 `:8080`。
|
||||
- 实际:health 为 `status=ok`、`env=dev`。监听进程 `agent-server` pid 2657636,启动时间早于本次合入(二进制时间 2026-09-28 10:14 UTC)。`guard env --target dev` 返回 `LEASE_REQUIRED`。
|
||||
- 判定:未运行。合入后的后端尚未加载。
|
||||
|
||||
### 7. 浏览器中的测试员工流程受控
|
||||
|
||||
- 输入:本机 `curl http://127.0.0.1:3100/src/engine/rbac.ts`;IDE 浏览器打开 `http://127.0.0.1:3100/`。
|
||||
- 实际:Vite 提供的源码含 `projectMember`。IDE 浏览器停在 `chrome-error://chromewebdata/`,没有进入登录后的项目页。
|
||||
- 判定:未运行。不能把源码含该字段记成测试员工看到 7 份文档。
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
本次实际运行并在合入候选上复跑通过:
|
||||
|
||||
- `./scripts/arch-check.sh --mode block`
|
||||
- `go vet ./...`
|
||||
- `go test -count=1 -short ./...`
|
||||
- `web`:`npm test`、`npm run build`
|
||||
|
||||
未运行:产品 PI Agent Runner、测试员工登录后的流程受控点击、合入后的新后端进程。
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
工程门禁达到合入条件,候选已由队列合入本地 `dev`(`7eb9477d9edffd3f39aa7026a874169c6efb190b`)。这不是产品链路验收通过。
|
||||
|
||||
未完成项:
|
||||
|
||||
- `.doc` 由进行中的 `quality-gate-heading-deploy-2026-09-28` 与 `nightly-advance-land-2026-09-28` 占用,本报告未能随 `docs(test)` 提交。
|
||||
- `internal/coordinator` 与 `internal/bizdev` 由进行中的 `controlled-projection-project-binding-2026-09-28` 占用。本任务没有改 `engine_task.go`。会话已把 `ChatSessionID` 和数字用户 ID 放进上下文;该任务的分支会把 `ChatSessionID` 抄进 `SessionID`。`StageKey` 保持空:非空会改写文档阶段解析,并把其他阶段的可共享对话滤成不可见。`ClientTurnID` 仍未进入任务请求。`dialogue_inject.go` 尚未把 `TurnCount` 等统计抄进小队对话包,因此成功注入时 README 先不写「滤除 0」。
|
||||
- 重载 `agent-server` 需要 `env:dev` 租约,本任务没有该能力。当前 `:8080` 仍是合入前的进程。
|
||||
|
||||
下次在占用解除并拿到 dev 环境租约后:重编译并重载 dev 后端,用测试员工打开测试项目确认流程受控为 7 份,再发起一次调研,核对工作区 `context/dialogue` 不再是项目未绑定占位。
|
||||
@@ -0,0 +1,114 @@
|
||||
# 夜间推进自动化 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
每日 03:10 的设计目标是无人值守推进 `.worktrees/` 下的分支任务、经治理门禁合入 `dev`、在 tip 变化时更新 dev 服务,并在确认无内容丢失后清理已收口目录。本报告归档 run `20260928T064350Z` 的真实执行结果。该次运行不是满队列夜间清扫:编排进程为 `python3 -B scripts/nightly/advance.py --repo /home/weichen/projects/Agent-System --max-tasks 1 --task-timeout 300`(与 run 目录时间戳同时启动)。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- 计划构建能列出当夜待处理条目(`plan.json`)。
|
||||
- 在 `--max-tasks 1` 上限内,对第一条非清理任务执行无头推进,并留下 result、耗时与日志。
|
||||
- `dev` tip 未变化时不重建服务(`env.json`)。
|
||||
- 清理阶段对计划中的 `cleanup_verify` 条目做祖先与干净检查;不满足则保留目录。
|
||||
- 报告引用 run 目录数据,不把未执行项记为通过。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- 操作系统:Linux。
|
||||
- 主仓:`/home/weichen/projects/Agent-System`(分支 `dev`)。
|
||||
- 报告工作区:`/home/weichen/projects/Agent-System/.worktrees/nightly-advance-automation-2026-09-28`。
|
||||
- 设计触发:cron `10 3 * * *` → `scripts/nightly-advance.sh`(任务文档约定)。本次 run 的实际入口是上面的 `advance.py --max-tasks 1 --task-timeout 300`。
|
||||
- 执行器:cursor-agent 无头模式;agentctl daemon。`state.json` 记录 `daemon=ok`。
|
||||
- run 目录:`/home/weichen/projects/Agent-System/.tmp/nightly/20260928T064350Z`。
|
||||
- 证据(相对主仓):
|
||||
- `.tmp/nightly/20260928T064350Z/state.json`
|
||||
- `.tmp/nightly/20260928T064350Z/plan.json`
|
||||
- `.tmp/nightly/20260928T064350Z/advance.json`
|
||||
- `.tmp/nightly/20260928T064350Z/env.json`
|
||||
- `.tmp/nightly/20260928T064350Z/cleanup.json`
|
||||
- `.tmp/nightly/20260928T064350Z/agent-00-integrate-agent-reply-llm-narrative-2026-09-28.log`
|
||||
- 层级:工程工具夜间编排的一次有界实跑。产品 PI 全链路不适用;本文件不把该次运行记成产品测试 PASS。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
- 活跃或待合入任务按计划推进、合入,或带原因 `blocked` / `failed`。
|
||||
- `dev` tip 变化时重建服务并通过 `/health` 与 `/ready`;tip 不变则服务阶段为 `not_required`。
|
||||
- 已完成且内容已在 `dev` 上、工作树干净的目录,经普通删除后移除;祖先检查失败则保留。
|
||||
- 每个实际执行的任务在本报告中有 result、耗时和证据日志相对路径。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
- 开始:`2026-09-28T06:43:50+00:00`;`dev` 起点 `e5c2519feaeedb689f0c4055e03cbfe65507f719`(`state.json` 的 `dev_tip_start`)。
|
||||
- 草稿落盘时刻:`2026-09-28T06:45:07+00:00`;`dev` 终点与起点相同(`dev_tip_end` / `dev_tip_final` 均为 `e5c2519feaeedb689f0c4055e03cbfe65507f719`)。
|
||||
- `dev` 变更:否。
|
||||
- 计划生成:`plan.json` `generated_at=2026-09-28T06:43:54`,`count=11`。
|
||||
|
||||
### 用例 1:integrate-agent-reply-llm-narrative-2026-09-28
|
||||
|
||||
- **测试输入 (Input)**:`plan.json` 第 1 条。`phase=integrate_closeout`,worktree `/home/weichen/projects/Agent-System/.worktrees/integrate-agent-reply-llm-narrative-2026-09-28`,`base_feature=agent-reply-llm-narrative`,blockers 为 `not_ancestor_of_dev`、`dirty_or_ignored_files`。编排上限 `--max-tasks 1`,因此只把这一条交给无头执行器。
|
||||
- **实际输出 (Actual Output)**:`advance.json` 记录 `rc=0`,`duration_s=71.6`,`phase=integrate_closeout`,`branch=null`,`owner=null`。日志末行:
|
||||
|
||||
```text
|
||||
{"task_id":"integrate-agent-reply-llm-narrative-2026-09-28","result":"merged","summary":"功能尖 dfd9f6ef 已由 4c42d1bf 合入 dev,当前候选第一父落后于 dev,未重建","new_dev_tip":"e5c2519feaeedb689f0c4055e03cbfe65507f719"}
|
||||
```
|
||||
|
||||
日志正文记录:工作树干净;冻结 HEAD `cb0ccf33fe58b2aa8aa84a1fc5fc3aebbcaf996f`,第一父 `8f77053f5169502eaf3e4e4135a082e8914f3704`;功能尖 `dfd9f6efb374c12bec7dd90b214c40f44cc0fee6` 已是当前 `dev` 的祖先,由 `4c42d1bf74e262930bcf127e517ab6b76a0dda71` 带入。未重新入队,未重建候选。
|
||||
|
||||
- **评价判定 (Evaluation / Verdict)**:PASS(`result=merged`)。
|
||||
- **性能与耗时 (Duration & Latency)**:71.6 秒。
|
||||
- **证据日志**:`.tmp/nightly/20260928T064350Z/agent-00-integrate-agent-reply-llm-narrative-2026-09-28.log`
|
||||
|
||||
### 用例 2:计划内未交给执行器的条目
|
||||
|
||||
`advance.json` 只有 1 条。下列 8 条在 `plan.json` 中、且阶段不是 `cleanup_verify`,本次没有对应 agent 日志。判定为未运行,不记 PASS。
|
||||
|
||||
| task | phase | 判定 | 耗时 | 证据 |
|
||||
|---|---|---|---|---|
|
||||
| integrate-agent-reply-llm-narrative-2026-09-28-r2 | integrate_closeout | 未运行 | — | 无 agent 日志;见 `.tmp/nightly/20260928T064350Z/plan.json` |
|
||||
| integrate-error-code-observability-2026-09-28 | integrate_closeout | 未运行 | — | 同上 |
|
||||
| integrate-land-promote-rc-beta5-record-2026-09-28 | integrate_closeout | 未运行 | — | 同上 |
|
||||
| integrate-pi-runner-env-mismatch-2026-09-28 | integrate_closeout | 未运行 | — | 同上 |
|
||||
| integrate-quality-gate-heading-mismatch-2026-09-28-r3 | integrate_closeout | 未运行 | — | 同上 |
|
||||
| integrate-test-user-doc-issues-2026-09-28 | integrate_closeout | 未运行 | — | 同上 |
|
||||
| full-system-browser-e2e-wipe-2026-09-26 | feature_advance | 未运行 | — | 同上 |
|
||||
| quality-gate-heading-mismatch-2026-09-28 | feature_advance | 未运行 | — | 同上 |
|
||||
|
||||
### 用例 3:dev 服务
|
||||
|
||||
- **测试输入 (Input)**:`dev_tip_start` 与 `dev_tip_end` 同为 `e5c2519feaeedb689f0c4055e03cbfe65507f719`。
|
||||
- **实际输出 (Actual Output)**:`env.json` 为 `{"dev_tip":"e5c2519feaeedb689f0c4055e03cbfe65507f719","status":"not_required","reason":"dev tip unchanged tonight"}`。无 `/health`、`/ready` 采样。
|
||||
- **评价判定 (Evaluation / Verdict)**:PASS(阶段按设计跳过;服务未重建)。
|
||||
- **性能与耗时 (Duration & Latency)**:`env.json` 未记录单独耗时。
|
||||
- **证据**:`.tmp/nightly/20260928T064350Z/env.json`
|
||||
|
||||
### 用例 4:清理
|
||||
|
||||
两条均来自 `cleanup.json`。`registry_state=done`,`ancestor_of_dev=false`,`clean=true`,`dirty_sample=[]`,`verdict=skip_not_on_dev`。未调用删除。
|
||||
|
||||
| worktree | 判定 | 耗时 | 证据 |
|
||||
|---|---|---|---|
|
||||
| integrate-quality-gate-heading-mismatch-2026-09-28-r2 | skip_not_on_dev | 未单独记录 | `.tmp/nightly/20260928T064350Z/cleanup.json` |
|
||||
| integrate-quality-gate-heading-mismatch-2026-09-28-r4 | skip_not_on_dev | 未单独记录 | `.tmp/nightly/20260928T064350Z/cleanup.json` |
|
||||
|
||||
- **评价判定 (Evaluation / Verdict)**:PASS(门禁按「不是 `dev` 祖先则不删」执行;目录保留)。
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
本次夜间 run 没有另跑 `unittest` / `go test`。套件即编排阶段本身:
|
||||
|
||||
| 检查 | 实际 | 判定 |
|
||||
|---|---|---|
|
||||
| 预检 daemon | `state.json` `daemon=ok` | PASS |
|
||||
| plan 构造 | `plan.json` `count=11` | PASS |
|
||||
| 任务推进 | `advance.json` 1 项;`failed`/`blocked` 0 项;另外 8 项因 `--max-tasks 1` 未运行 | 已执行项 PASS |
|
||||
| dev 服务 | `status=not_required` | PASS |
|
||||
| 清理 | 2 项 `skip_not_on_dev`,0 项删除 | PASS |
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
本次**已执行范围**判定为 PASS:唯一推进项 `result=merged`(71.6 秒),`dev` tip 未变,服务阶段 `not_required`,清理两项因不是 `dev` 祖先而保留。`advance.json` 中没有 `blocked` 或 `failed` 任务,因此没有该类人工裁决点。
|
||||
|
||||
后续(观察项,不是失败裁决):
|
||||
|
||||
1. 满队列尚未覆盖。`--max-tasks 1` 使另外 8 条 `integrate_closeout` / `feature_advance` 未运行。下一次去掉该上限(或显式提高上限)后再归档对应日志。
|
||||
2. 两个 `cleanup_verify` 目录仍在:`integrate-quality-gate-heading-mismatch-2026-09-28-r2` 与 `integrate-quality-gate-heading-mismatch-2026-09-28-r4`。登记状态为 `done`,工作树干净,但分支不是当前 `dev` 的祖先,自动化按门禁跳过删除。若要删除,需人工确认功能内容已经在 `dev` 上,再走普通 `remove`;本运行未使用 `--force`。
|
||||
@@ -0,0 +1,77 @@
|
||||
# 质量门禁章节标题漂移 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
dev 项目「吞咽电刺激」调研报告 `research_1790566833205199368` 已生成正文,但 `manifest.json` 的 `quality_report.passed=false`,唯一违规是缺少「技术路线与 State-of-the-Art」。终稿该节实际在 H3,且标题少一个空格。质量失败短路了受控文档投影。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- 空格、全角、大小写和 H3/H4 漂移不再被判成缺章,只记层级软警告。
|
||||
- 正文确实没有的模板 H2 仍然失败。
|
||||
- 聊天触发任务带上 `AutoRepair`。
|
||||
- 质量失败的同步回复不声称「已完成生成」。
|
||||
- `arch-check --mode block`、`go vet ./...`、`go test -short ./...` 通过。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- 环境:Linux,worktree `.worktrees/quality-gate-heading-mismatch-2026-09-28`,基线 `434d542c`,修复提交 `aea19f70`、边界补记 `87888213`。
|
||||
- 层级:工程工具 / Go 单测。产品 PI 全链路重跑调研报告不在本报告的工程门禁内,合入并重载 dev 后另验。
|
||||
- 未改前端,不跑 `npm test`。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
- `TestQualityGate_Validate_HeadingLevelAndSpaceDrift`:漂移稿通过且评分 100,并含层级软警告;缺「正文」失败。
|
||||
- `TestSyncTriggerTask_EnablesAutoRepair`:`AutoRepair=true`。
|
||||
- `TestFormatTriggerTaskReply_QualityFailedOmitsHostPath`:回复含「质量检查未通过」和违规摘要,不含「已完成生成」。
|
||||
- 短测与 arch-check 退出码 0。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
### 用例 1:标题漂移与真实缺章
|
||||
|
||||
- 输入:`go test ./internal/squad/ ./internal/coordinator/ ./internal/bizdev/ -count=1 -timeout 240s`
|
||||
- 实际输出:
|
||||
|
||||
```
|
||||
ok chat-agent-system/internal/squad 0.041s
|
||||
ok chat-agent-system/internal/coordinator 0.015s
|
||||
ok chat-agent-system/internal/bizdev 0.227s
|
||||
```
|
||||
|
||||
- 判定:PASS
|
||||
- 耗时:约 2.5s(含同次 `go vet` 三个包,vet 退出码 0)
|
||||
|
||||
### 用例 2:arch-check
|
||||
|
||||
- 输入:`./scripts/arch-check.sh --mode block`
|
||||
- 第一次实际输出(补允许项之前):`NEW import coordinator internal/coordinator/engine_task.go projectworkflow`,退出码 1。该 import 在基线 `434d542c` 已存在,不是本次功能 diff 引入。
|
||||
- 补记 coordinator 允许依赖 `projectworkflow` 后:
|
||||
|
||||
```
|
||||
ok chat-agent-system/internal/archtest 0.203s
|
||||
ok chat-agent-system/internal/archtest 0.216s
|
||||
arch-check block: production gate passed
|
||||
```
|
||||
|
||||
- 判定:PASS
|
||||
- 耗时:约 0.7s
|
||||
|
||||
### 用例 3:vet 与 short 全量
|
||||
|
||||
- 输入:`go vet ./... && go test -short ./... -count=1 -timeout 20m`
|
||||
- 实际输出:退出码 0。抽查 `internal/squad` `0.038s`、`internal/server` `20.426s`,无 FAIL 行。
|
||||
- 判定:PASS
|
||||
- 耗时:25.2s
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
| 检查 | 结果 |
|
||||
|---|---|
|
||||
| `go test ./internal/squad/ ./internal/coordinator/ ./internal/bizdev/` | PASS |
|
||||
| `go vet ./...` | PASS |
|
||||
| `./scripts/arch-check.sh --mode block` | PASS(允许项补记后) |
|
||||
| `go test -short ./...` | PASS |
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
工程门禁达到本修复的单测交付点。合入 local dev 并重载 dev 制品后,须从项目上下文重跑「吞咽电刺激」调研报告,确认 `project_id>0`、`quality_report.passed=true`,且受控文档 head 更新。双层路径 `data/dev/data/dev` 未修,见 ISSUE-20260928-data-dir-doubled。
|
||||
@@ -0,0 +1,80 @@
|
||||
# 刷新后任务状态恢复 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
刷新页面后,进行中的调研报告进度卡片消失。询问「调研报告编写进度怎么样了」又启动了新的 research 工作区。11:27 的 `step_06_draft` 与 11:38 的并行步骤被记成空输出失败,盘上的 `output.md` 已经有正文。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- 进行中的 run 在刷新后仍能回到进度卡片。
|
||||
- 疑问进度句查询真实 run,不新建工作区。
|
||||
- 盘上已有非空正文且不是传输失败时,步骤不再被空结果否决。
|
||||
- 连接错误留下的文件仍然失败关闭。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- OS:Linux。工作树:`.worktrees/refresh-task-state-recovery-2026-09-28`,基线 `4c42d1bf`。
|
||||
- 层级:Go/接口短测、前端单测与构建、架构边界检查。产品 PI 浏览器链路在 dev 重载后另记,本文件先记录已跑命令。
|
||||
- 证据目录:`.doc/test-reports/assets/2026-09-28-refresh-task-state-recovery/`。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
- `go vet ./...` 无输出。
|
||||
- 本改动涉及的包 `go test -short` 通过。
|
||||
- `web` 的 `npm test` 与 `npm run build` 通过。
|
||||
- 「调研报告编写进度怎么样了」不是写意图;「开始做调研报告」解析为 research。
|
||||
- 缺交付物但盘上有正文时步骤接受;`Connection error` 且可重试时拒绝。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
### 用例 1:意图与进度回复
|
||||
|
||||
- 输入:`go test -short -count=1 ./internal/intent/ ./internal/coordinator/`
|
||||
- 实际输出:两包 `ok`(intent 0.179s,coordinator 0.034s,见 `go-test-short.txt`)。新增用例「调研报告编写进度怎么样了」为 `QUERY_PROGRESS`,「开始做调研报告」为 `research`。
|
||||
- 判定:PASS
|
||||
- 耗时:见短测日志
|
||||
|
||||
### 用例 2:空输出判定与空 patch
|
||||
|
||||
- 输入:`go test -short -count=1 ./internal/squad/ ./internal/agent/`
|
||||
- 实际输出:两包 `ok`(squad 0.041s,agent 2.252s)。连接错误仍失败关闭的既有用例通过。
|
||||
- 判定:PASS
|
||||
|
||||
### 用例 3:锚点、活跃列表、消息映射、重放退避、本地缓存
|
||||
|
||||
- 输入:`go test -short ./internal/taskrun/ ./internal/server/`;`npm test` 中的 `activeTaskCache`、`useRecoveredTaskPhases`、`useChatWindow`、`templateMatch`
|
||||
- 实际输出:taskrun `ok` 0.171s,server `ok` 20.914s。`npm test` 532 通过、vitest 34 通过,`NPM_TEST:0`。
|
||||
- 判定:PASS
|
||||
|
||||
### 用例 4:全量短测与架构边界
|
||||
|
||||
- 输入:`go vet ./...`;`go test -short -count=1 ./...`;`./scripts/arch-check.sh --mode block`
|
||||
- 实际输出:`VET:0`。短测唯一失败是 `internal/archtest`:`NEW import coordinator internal/coordinator/engine_task.go projectworkflow`。该文件相对基线 `4c42d1bf` 的 diff 为 0,导入来自已合入的 `agent-reply-llm-narrative`,不是本任务新增。`ARCH:1` 与之相同。
|
||||
- 判定:本任务包 PASS;架构边界 FAIL 记为基线既有项,本任务未改该导入
|
||||
|
||||
### 用例 5:前端构建
|
||||
|
||||
- 输入:`npm run build`
|
||||
- 实际输出:`NPM_BUILD:0`,precompress 写出 66 个 br/gz。
|
||||
- 判定:PASS
|
||||
|
||||
### 用例 6:dev 浏览器与真实 PI Runner
|
||||
|
||||
- 输入:发起调研报告,执行中刷新,完成后刷新,再问编写进度。
|
||||
- 实际输出:本段写入时尚未在已加载新代码的 dev 上运行。
|
||||
- 判定:未运行
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
| 检查 | 结果 |
|
||||
|---|---|
|
||||
| go vet ./... | PASS(VET:0) |
|
||||
| go test -short ./... | 除既有 archtest 外 PASS |
|
||||
| arch-check --mode block | FAIL(基线既有 coordinator→projectworkflow) |
|
||||
| npm test | PASS(532 + 34) |
|
||||
| npm run build | PASS |
|
||||
| dev 浏览器 + 真实 PI | 未运行 |
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
实现与单测、前端构建已通过。交付终点还差:把候选合入 local dev、重载服务后,用真实 PI Runner 走刷新恢复和进度问句。架构边界失败不由本 diff 引入,不在本 mutex 里改 `engine_task.go`。
|
||||
@@ -0,0 +1,79 @@
|
||||
# 非编制人小队账本提交 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
项目 6022 的《用户初始需求收集表》由成员 ceshi(user 74,viewer)触发生成时,小队正文已产出,但账本提交以触发者身份请求 `ActionCommit`,被编制人职责门拒绝(`author_id=4`)。任务 `run_1790556318606671340` 与 `run_1790556736148378090` 的 `error_message` 为 `uow: permission denied`。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- 非编制人再次触发 `bd_intake` 时,任务完成且不再返回 `uow: permission denied`。
|
||||
- 账本 `agent_commit` 的 `actor_user_id` 为文档编制人 4。
|
||||
- 快照 `actor_user_id` 记录实际触发者 74。
|
||||
- 编制人自己触发、非编制人直接 `ActionCommit` 的单测不回归。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- 环境:本机 dev,`GET /api/v1/health` 返回 `env=dev`、`agent_backend=pi-agent`、`schema_version=77`、`database_ready=true`。
|
||||
- 代码:local `dev` `b1ed8ecf`(`merge: candidate squad-commit-authority-2026-09-28 into dev`)。宿主 `bin/agent-server` 于 2026-09-28 01:30:10Z 由 `./scripts/launch.sh --no-docker` 重建并重启,健康检查通过。
|
||||
- 层级:机制回归用内存单测;原例用 dev 真实 `pi-agent` 重跑。未跑 nightly 五项全量,本修复不改工作流定义。
|
||||
- 身份:为 user 74 写入一次性 `auth_sessions`(2 小时过期),请求结束后删除。未记录 token,未改口令。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
- `POST /api/v1/tasks/runs` 被接受,最终 `task_runs.status=completed` 且 `error_message` 为空。
|
||||
- `document_records.content_sha256` 从空变为非空。
|
||||
- `document_change_events.change_kind=agent_commit` 且 `actor_user_id=4`。
|
||||
- 对应 `document_snapshots.actor_user_id=74`,`source_kind=agent_squad`。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
### 用例 1:dev 原例重跑
|
||||
|
||||
- **测试输入**:user 74,`session_id=sess-p6022-u74`,`project_id=6022`,`task_key=bd_intake`,`doc_id=doc_proj6022_bd_intake`,`client_turn_id=verify-squad-commit-20260928`。
|
||||
- **实际输出**:
|
||||
- 创建:HTTP 202,`run_id=run_1790559088975684237`,`status=running`。
|
||||
- 结束:`task_runs` 行 `user_id=74`,`status=completed`,`error_message` 空。
|
||||
- 修复前对照:同一文档、同一用户的 `run_1790556736148378090` 为 `failed` / `uow: permission denied`。
|
||||
- 账本:`document_records` 由 `|1`(空哈希、version 1)变为 `82d4dfb0bd17691ef0cb307f4fa6526cf04747713a2ff67c68a33b9396b8dbf1|1`。
|
||||
- `document_change_events` id 37:`agent_commit` / `agent_squad` / `actor_user_id=4`。
|
||||
- `document_snapshots` id 37:`agent_squad` / `actor_user_id=74` / `source_run_id=bizdev-1790559435908957528-bd_intake`。
|
||||
- `document_agent_candidates` id 15:`status=committed`。
|
||||
- 快照 `actor_snapshot`:`{"model":"pi-agent","squad_name":"bizdev-squad"}`。
|
||||
- **评价判定**:PASS
|
||||
- **性能与耗时**:约 01:31:28Z 至 01:37:29Z,约 6 分钟。
|
||||
|
||||
### 用例 2:编制人触发不代理
|
||||
|
||||
- **测试输入**:`go test` `TestExecuteTask_AuthorTriggerDoesNotSetOnBehalfOf`。
|
||||
- **实际输出**:`ok chat-agent-system/internal/bizdev`(与用例 3 同一次 `go test`,0.041s)。
|
||||
- **评价判定**:PASS
|
||||
- **性能与耗时**:包级 0.041s。
|
||||
|
||||
### 用例 3:非编制人直接 ActionCommit 仍拒绝
|
||||
|
||||
- **测试输入**:`TestDutyAuthorizerAllowsDocumentAuthorCommit`(编制人通过,user 74 得到 `uow.ErrPermissionDenied`)以及 `TestReauthDeniesWrongAuthor`。
|
||||
- **实际输出**:`ok chat-agent-system/internal/app/docflow` 0.003s。集成候选校验同一命令亦通过(sha `b1ed8ecfe9fc5849288b6febf2bb4780319c8aca`)。
|
||||
- **评价判定**:PASS
|
||||
- **性能与耗时**:0.003s。
|
||||
|
||||
### 未覆盖
|
||||
|
||||
- 未在浏览器里点击生成按钮。API 与 `task_runs` 是该按钮的同一创建入口。
|
||||
- 未重跑高风险 `needs_confirmation` 的真实 PI 样本。该分支未改判断式,内存用例 `TestExecuteTask_PendingConfirmation_RecordsAwaitingNotCompleted` 在合入前的 `TestExecuteTask_` 筛选中通过。
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
合入前在集成工作区执行:
|
||||
|
||||
```text
|
||||
go test -count=1 -timeout 180s ./internal/bizdev/ -run 'TestSquadCommitActor_UsesAuthorWhenTriggerDiffers|TestExecuteTask_NonAuthor|TestExecuteTask_AuthorTrigger|TestExecuteTask_PermissionDenied|TestExecuteTask_MaterializeFailure|TestExecuteTask_Committed|TestExecuteTask_Pending'
|
||||
go test -count=1 -timeout 60s ./internal/app/docflow/ -run 'TestDutyAuthorizerAllowsDocumentAuthorCommit|TestReauthDeniesWrongAuthor'
|
||||
```
|
||||
|
||||
结果:两个包均为 `ok`。未跑全量 `go test ./...`。
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
dev 原例达到修复目标:非编制人触发的受控小队任务可以落盘,提交身份是编制人,触发者留在快照上。部署状态是本机 dev 宿主进程已换新二进制;未晋升 test/main,未 push。
|
||||
|
||||
证据文档提交时,`activation-preview-precheck-2026-09-28` 仍占用 `.doc` 写范围。若本文件随该占用解除后才入库,以合入提交为准。
|
||||
@@ -0,0 +1,96 @@
|
||||
# 第三方 test 用户文档问题 测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
第三方用户在 test 环境整理了《AI 测试问题》。只上传附件被空消息拒绝已由 ISSUE-20260928-attachment-only-turn 修复。本轮处理另外两类:
|
||||
|
||||
- 「帮我定设计开发计划任务书」没有进入任务,对话是掂量阶段模板。
|
||||
- 「帮我生成报价单」气泡写成正文已保存到宿主机路径,工作区实际是质量未通过的阻塞说明。根因是 PI runner 数据目录错配,已由 `4f581306` 合入 local dev。
|
||||
|
||||
主记录:[2026-09-28-test-user-doc-issues](../ops/short-term/2026-09-28-test-user-doc-issues.md)。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- 「帮我定设计开发计划任务书」在商务流激活时进入任务,并带出研发流澄清;「决定」不触发编写。
|
||||
- 研发前置未满足时,用户看到中文原因,而不是协调失败。
|
||||
- 质量未通过的同步回复使用质量摘要,不写宿主机路径,不声称正文已保存。
|
||||
- 核对 test 进程是否已经指向本环境 runner 与 `data/test`。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- 源码:worktree `.worktrees/test-user-doc-issues-2026-09-28`,提交 `db859e185ebcd0e7eccd2241ca07f0e9f4fe4c6b`,基线 `8f77053f`。
|
||||
- 命令:`go test ./internal/intent/ ./internal/coordinator/ -count=1 -timeout 180s`,以及 `-run` 聚焦用例;`go vet` 同两包。
|
||||
- test 运行制品:`bin/agent-server-test` 修改时间 2026-09-28 04:04:03 +0000,主目录 `HEAD` `193a752f`(含 `4f581306`)。进程 pid `1450126`,`ENV=test`,`DATA_DIR=.../data/test`,`PI_AGENT_RUNNER_URL=http://127.0.0.1:18866`。18866 的 `data_dir` 为 `data/test/workspaces`。本提交的回复文案尚未打进该二进制。
|
||||
- 未用第三方账号再跑「生成报价单」。该操作会写入其项目;本会话没有登录凭证。runner 数据目录的产品链路证据见 [PI runner 环境错配报告](./2026-09-28-pi-runner-env-mismatch-test-report.md) 的 `bd_research` 实测。竞品视频未发现独立处理缺陷,未扩测。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
- 「帮我定设计开发计划任务书」+ 激活 `main`:`TRIGGER_TASK`,工作流范围不匹配,澄清含研发设计控制流程,`RequiresClarifying`。
|
||||
- 激活 `rdev` 时「帮我制定…」命中 `rd_design_plan` 且不拦截。
|
||||
- 「我决定先放一放,再看看进展」不是 `TRIGGER_TASK`。
|
||||
- 质量失败回复不含「正文已保存」和 `output.md` 绝对路径。
|
||||
- 前置未满足回复含「请先完成商务对接流程」,错误值为空。
|
||||
- 已完成回复仍含「正文已保存。」
|
||||
- test 进程 runner 为 18866,数据目录为 test。
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
### 用例 1:帮我定 + 商务流
|
||||
|
||||
- **输入**:`TestWriteVerb_DingDesignPlanCrossFlow`
|
||||
- **实际输出**:`intent=TRIGGER_TASK branch=NEW_INTENT_PREEMPT confidence=0.86`,`PASS`
|
||||
- **判定**:PASS
|
||||
- **耗时**:包内 <0.01s
|
||||
|
||||
### 用例 2:制定 + 研发流
|
||||
|
||||
- **输入**:`TestWriteVerb_ZhidingDesignPlanOnRdev`
|
||||
- **实际输出**:同上 `TRIGGER_TASK`,`PASS`(断言任务键 `rd_design_plan` 且无范围拦截)
|
||||
- **判定**:PASS
|
||||
|
||||
### 用例 3:决定不是编写
|
||||
|
||||
- **输入**:`TestWriteVerb_DecideIsNotWrite`
|
||||
- **实际输出**:`QUERY_PROGRESS` / `CASUAL_CHAT`,无 `TRIGGER_TASK`,`PASS`
|
||||
- **判定**:PASS
|
||||
|
||||
### 用例 4:质量失败文案
|
||||
|
||||
- **输入**:`TestFormatTriggerTaskReply_QualityFailedOmitsHostPath`
|
||||
- **实际输出**:`PASS`(回复走质量摘要,不含宿主机路径)
|
||||
- **判定**:PASS
|
||||
|
||||
### 用例 5:前置条件可见
|
||||
|
||||
- **输入**:`TestSyncTriggerTask_PrerequisiteIsUserVisible`
|
||||
- **实际输出**:`PASS`
|
||||
- **判定**:PASS
|
||||
|
||||
### 用例 6:成功文案仍保留「正文已保存。」
|
||||
|
||||
- **输入**:`TestSyncTriggerTask_Committed_KeepsSavedWording`
|
||||
- **实际输出**:`PASS`
|
||||
- **判定**:PASS
|
||||
|
||||
### 用例 7:test 进程 runner 指向
|
||||
|
||||
- **输入**:读 pid `1450126` 的 `ENV`、`DATA_DIR`、`PI_AGENT_RUNNER_URL`;`GET` 18866 与 8081 `/health`;在二进制中查找 `RUNNER_MISMATCH` 用户文案。
|
||||
- **实际输出**:`ENV=test`;`DATA_DIR=/home/weichen/projects/Agent-System/./data/test`;`PI_AGENT_RUNNER_URL=http://127.0.0.1:18866`;18866 `data_dir=/home/weichen/projects/Agent-System/data/test/workspaces`;health `schema_version=78` `agent_backend=pi-agent`;二进制含「模型执行环境与数据目录不一致」。
|
||||
- **判定**:PASS(runner 接线)。报价单全链路本轮未运行。
|
||||
- **耗时**:健康检查 <1s。
|
||||
|
||||
日志:`.doc/test-reports/assets/2026-09-28-test-user-doc-issues/go-test-focused.txt`、`go-test-packages.txt`、`go-vet.txt`。
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
| 命令 | 结果 |
|
||||
|---|---|
|
||||
| `go test ./internal/intent/ ./internal/coordinator/ -count=1 -timeout 180s` | PASS(intent 0.078s,coordinator 0.015s) |
|
||||
| 聚焦 `-run 'TestWriteVerb_\|TestFormatTriggerTaskReply_QualityFailed\|TestSyncTriggerTask_Prerequisite\|TestSyncTriggerTask_Committed_KeepsSavedWording'` | PASS |
|
||||
| `go vet ./internal/intent/ ./internal/coordinator/` | 无输出,退出码 0 |
|
||||
|
||||
未跑全量 `go test ./...`,也未跑 nightly 五项。本变更不改运行模板。材料嵌入超限(约 34635 tokens,模型上限 8192)只登记 ISSUE-20260928-material-chunk-embed,未测修复。
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
意图动词、澄清透传、前置可见、质量失败文案由单测覆盖,达到本任务的代码验收。test 当前制品已指向本环境 runner,runner 错配修复在 dev `193a752f`。本提交的回复文案还没进 `bin/agent-server-test`。报价单没有在本轮用第三方项目重跑。嵌入分块超限仍开放。
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"session_id": "sess-intent-material-probe-2",
|
||||
"project_id": 6024,
|
||||
"project_name": "吞咽电刺激",
|
||||
"server": "worktree binary on 127.0.0.1:18080",
|
||||
"database": "agent_system_dev",
|
||||
"runner": "http://127.0.0.1:18865",
|
||||
"seconds": 8.46,
|
||||
"top_intent": {
|
||||
"intent": "QUERY_SIMILAR_CASES",
|
||||
"confidence": 0.82,
|
||||
"branch": "NEW_INTENT_PREEMPT",
|
||||
"filled_slots": {"query_topic": "materials"}
|
||||
},
|
||||
"reply_source": "kb",
|
||||
"fallback_reason": null,
|
||||
"has_sales_template": false,
|
||||
"reply_head": "关于「吞咽电刺激」里您要的「上一代产品说明书」,检索到这些材料:"
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
[
|
||||
{
|
||||
"input": "上一代产品说明书",
|
||||
"http": 200,
|
||||
"seconds": 7.22,
|
||||
"intent_keys": [
|
||||
"branch",
|
||||
"candidates",
|
||||
"context_snapshot_id",
|
||||
"intent_frame_v2",
|
||||
"plan_proposal",
|
||||
"plan_shadow",
|
||||
"ranked_frames",
|
||||
"raw_input",
|
||||
"requires_clarifying",
|
||||
"shadow_decision_hint",
|
||||
"shadow_dialogue_act",
|
||||
"shadow_margin",
|
||||
"shadow_score",
|
||||
"top_intent"
|
||||
],
|
||||
"intent": null,
|
||||
"branch": "NEW_INTENT_PREEMPT",
|
||||
"confidence": null,
|
||||
"slots": null,
|
||||
"reply_source": "kb",
|
||||
"fallback_reason": null,
|
||||
"has_sales_template": false,
|
||||
"reply_head": "关于「全自动冲击波治疗仪1」里您要的「上一代产品说明书」,检索到这些材料:\n\n1. **中华人民共和国医药行业标准** 〔YY/T0149-2006 §8.2 p.12〕\n - 位置: 气压弹道式体外压力波治疗设备 / 8.2 8.2 使用说明书\n - 要点: 使用说明书至少应包括下述内容:\na)制造商名称、商标和地址;\nb)产品名称、规格型号;\nc) 产品特点、用途、适用范围和主要性能与主要技术参数;\nd) 安装方法、操作使用、保养维修、安全注意事项等详细说明。\n2. **中华人民共和国医药行业标准** 〔YY/T0466.1-2009 §6.15 p.9〕\n - 位置: Interferential current therapy instrument / 6.15 6.15 使用说明书\n - 要点: 查看使用说明书,应符合 5.15 的要求。\n3. **中华人民共和国",
|
||||
"error_code": null
|
||||
},
|
||||
{
|
||||
"input": "类似的全自动产品的说明书",
|
||||
"http": 200,
|
||||
"seconds": 11.19,
|
||||
"intent_keys": [
|
||||
"branch",
|
||||
"candidates",
|
||||
"context_snapshot_id",
|
||||
"intent_frame_v2",
|
||||
"plan_proposal",
|
||||
"plan_shadow",
|
||||
"ranked_frames",
|
||||
"raw_input",
|
||||
"requires_clarifying",
|
||||
"shadow_decision_hint",
|
||||
"shadow_dialogue_act",
|
||||
"shadow_margin",
|
||||
"shadow_score",
|
||||
"top_intent"
|
||||
],
|
||||
"intent": null,
|
||||
"branch": "NEW_INTENT_PREEMPT",
|
||||
"confidence": null,
|
||||
"slots": null,
|
||||
"reply_source": "kb",
|
||||
"fallback_reason": null,
|
||||
"has_sales_template": false,
|
||||
"reply_head": "关于「全自动冲击波治疗仪1」里您要的「类似的全自动产品的说明书」,检索到这些材料:\n\n1. **中华人民共和国医药行业标准** 〔YY/T0149-2006 §8.2 p.12〕\n - 位置: 气压弹道式体外压力波治疗设备 / 8.2 8.2 使用说明书\n - 要点: 使用说明书至少应包括下述内容:\na)制造商名称、商标和地址;\nb)产品名称、规格型号;\nc) 产品特点、用途、适用范围和主要性能与主要技术参数;\nd) 安装方法、操作使用、保养维修、安全注意事项等详细说明。\n2. **中华人民共和国国家标准** 〔GB/T1.1-2009 §201.7.9.2 p.13〕\n - 位置: 医用电气设备 第2-17部分:自动控制式近距离治疗后装设备的基本安全和基本性能专用要求 / 201.7.9.2 201.7.9.2 使用说明书\n - 要点: 201.7.9.2 使用说明书\n3",
|
||||
"error_code": null
|
||||
},
|
||||
{
|
||||
"input": "竞品的操作视频",
|
||||
"http": 200,
|
||||
"seconds": 9.72,
|
||||
"intent_keys": [
|
||||
"branch",
|
||||
"candidates",
|
||||
"context_snapshot_id",
|
||||
"intent_frame_v2",
|
||||
"plan_proposal",
|
||||
"plan_shadow",
|
||||
"ranked_frames",
|
||||
"raw_input",
|
||||
"requires_clarifying",
|
||||
"shadow_decision_hint",
|
||||
"shadow_dialogue_act",
|
||||
"shadow_margin",
|
||||
"shadow_score",
|
||||
"top_intent"
|
||||
],
|
||||
"intent": null,
|
||||
"branch": "NEW_INTENT_PREEMPT",
|
||||
"confidence": null,
|
||||
"slots": null,
|
||||
"reply_source": "kb",
|
||||
"fallback_reason": null,
|
||||
"has_sales_template": false,
|
||||
"reply_head": "关于「全自动冲击波治疗仪1」里您要的「竞品的操作视频」,检索到这些材料:\n\n1. **网站首页** 〔网站首页 §(四十三)冲击波治疗仪:主要由主机、支臂和控制手柄组成。通过刺激外周神经、骨骼、肌肉、关节部位,对人体病灶部位进行治疗。用于外周神经、节功能的改善与辅助治疗。不用于心脏、瘫痪部位、前列腺、阴茎等敏感部位。分类编码为:09-04。〕\n - 位置: 2022年第三次医疗器械产品分类界定结果汇总 / (四十三)冲击波治疗仪:主要由主机、支臂和控制手柄组成。通过刺激外周神经、骨骼、肌肉、关节部位,对人体病灶部位进行治疗。用于外周神经、节功能的改善与辅助治疗。不用于心脏、瘫痪部位、前列腺、阴茎等敏感部位。分类编码为:09-04。 冲击波治疗仪:主要由主机、支臂和控制手柄组成。通过刺激外周神经、骨骼、肌肉、关节部位,对人体病灶部位进行治疗。用于外周神经、节功能的改善与辅助治疗。不用于心",
|
||||
"error_code": null
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
arch-check mode=block
|
||||
boundaries=/home/weichen/projects/Agent-System/.worktrees/refresh-task-state-recovery-2026-09-28/.doc/system-design/module-boundaries.yaml
|
||||
--- FAIL: TestArchitectureBoundaries (0.19s)
|
||||
arch_test.go:32: archtest findings=1 excepted=0 unmatched=1
|
||||
import=1
|
||||
sql_write=0
|
||||
path=0
|
||||
event=0
|
||||
REGRESSION import: 1 > baseline 0
|
||||
NEW import coordinator internal/coordinator/engine_task.go projectworkflow (production import coordinator -> projectworkflow)
|
||||
|
||||
FAIL
|
||||
FAIL chat-agent-system/internal/archtest 0.195s
|
||||
FAIL
|
||||
arch-check: new, expired, or unowned findings failed go test ./internal/archtest
|
||||
ARCH:1
|
||||
@@ -0,0 +1,99 @@
|
||||
? chat-agent-system/cmd/artifact-vault [no test files]
|
||||
ok chat-agent-system/cmd/bizdev-cli 2.985s
|
||||
? chat-agent-system/cmd/dataset [no test files]
|
||||
ok chat-agent-system/cmd/document-content-reconcile 0.003s
|
||||
? chat-agent-system/cmd/document-revision-backfill [no test files]
|
||||
? chat-agent-system/cmd/eventmode [no test files]
|
||||
? chat-agent-system/cmd/events [no test files]
|
||||
? chat-agent-system/cmd/maintpage [no test files]
|
||||
? chat-agent-system/cmd/recategorize [no test files]
|
||||
ok chat-agent-system/cmd/server 0.012s
|
||||
ok chat-agent-system/internal/agent 2.252s
|
||||
ok chat-agent-system/internal/agentx 0.023s
|
||||
ok chat-agent-system/internal/agentx/abtest 0.004s
|
||||
ok chat-agent-system/internal/agentx/llm 0.024s
|
||||
ok chat-agent-system/internal/agentx/session 0.008s
|
||||
ok chat-agent-system/internal/agentx/tools 0.006s
|
||||
ok chat-agent-system/internal/app 0.046s
|
||||
ok chat-agent-system/internal/app/chatflow 0.018s
|
||||
ok chat-agent-system/internal/app/docadvance 0.015s
|
||||
ok chat-agent-system/internal/app/docflow 0.009s
|
||||
ok chat-agent-system/internal/app/docnotify 0.005s
|
||||
ok chat-agent-system/internal/app/docproject 0.003s
|
||||
? chat-agent-system/internal/app/eventsconsumer [no test files]
|
||||
ok chat-agent-system/internal/app/fav 0.007s
|
||||
ok chat-agent-system/internal/app/intentflow 0.088s
|
||||
ok chat-agent-system/internal/app/stageflow 0.010s
|
||||
ok chat-agent-system/internal/app/uow 0.005s
|
||||
--- FAIL: TestArchitectureBoundaries (0.50s)
|
||||
arch_test.go:32: archtest findings=1 excepted=0 unmatched=1
|
||||
import=1
|
||||
sql_write=0
|
||||
path=0
|
||||
event=0
|
||||
REGRESSION import: 1 > baseline 0
|
||||
NEW import coordinator internal/coordinator/engine_task.go projectworkflow (production import coordinator -> projectworkflow)
|
||||
|
||||
FAIL
|
||||
FAIL chat-agent-system/internal/archtest 0.507s
|
||||
ok chat-agent-system/internal/artifactvault 0.005s
|
||||
ok chat-agent-system/internal/auth 1.494s
|
||||
ok chat-agent-system/internal/authz 0.007s
|
||||
ok chat-agent-system/internal/bizdev 0.418s
|
||||
ok chat-agent-system/internal/chat 0.067s
|
||||
ok chat-agent-system/internal/config 0.009s
|
||||
ok chat-agent-system/internal/conflictresolve 0.006s
|
||||
? chat-agent-system/internal/contracts/vocabulary [no test files]
|
||||
ok chat-agent-system/internal/coordinator 0.034s
|
||||
ok chat-agent-system/internal/ctxmgr 0.174s
|
||||
ok chat-agent-system/internal/dataset 0.010s
|
||||
ok chat-agent-system/internal/db 0.007s
|
||||
ok chat-agent-system/internal/dialoguectx 0.007s
|
||||
ok chat-agent-system/internal/doccontent 0.004s
|
||||
ok chat-agent-system/internal/docduty 0.004s
|
||||
ok chat-agent-system/internal/docgov 0.006s
|
||||
ok chat-agent-system/internal/docquery 0.004s
|
||||
ok chat-agent-system/internal/docrevision 0.017s
|
||||
ok chat-agent-system/internal/errcode 0.007s
|
||||
ok chat-agent-system/internal/errorevents 0.008s
|
||||
ok chat-agent-system/internal/events 0.012s
|
||||
ok chat-agent-system/internal/freedoc 0.035s
|
||||
ok chat-agent-system/internal/fsutil 0.005s
|
||||
ok chat-agent-system/internal/handoff 0.008s
|
||||
ok chat-agent-system/internal/httpx 0.004s
|
||||
ok chat-agent-system/internal/intent 0.179s
|
||||
ok chat-agent-system/internal/jevdecide 0.274s
|
||||
ok chat-agent-system/internal/kb 0.013s
|
||||
ok chat-agent-system/internal/localpg 0.598s
|
||||
ok chat-agent-system/internal/maintenance 0.005s
|
||||
ok chat-agent-system/internal/materialindex 0.007s
|
||||
ok chat-agent-system/internal/maturity 0.007s
|
||||
ok chat-agent-system/internal/mineru 3.008s
|
||||
ok chat-agent-system/internal/notification 0.005s
|
||||
? chat-agent-system/internal/observability [no test files]
|
||||
ok chat-agent-system/internal/projectcontext 0.005s
|
||||
ok chat-agent-system/internal/projectinstance 0.010s
|
||||
ok chat-agent-system/internal/projectworkflow 0.003s
|
||||
ok chat-agent-system/internal/quote 0.012s
|
||||
ok chat-agent-system/internal/replyexport 0.002s
|
||||
ok chat-agent-system/internal/server 20.914s
|
||||
ok chat-agent-system/internal/squad 0.041s
|
||||
ok chat-agent-system/internal/storage 0.003s
|
||||
ok chat-agent-system/internal/stream 0.003s
|
||||
ok chat-agent-system/internal/stream/transport 0.003s
|
||||
ok chat-agent-system/internal/supplementimpact 0.003s
|
||||
ok chat-agent-system/internal/swboot 0.070s
|
||||
ok chat-agent-system/internal/sysadmin 0.003s
|
||||
ok chat-agent-system/internal/taskrun 0.171s
|
||||
ok chat-agent-system/internal/template 1.059s
|
||||
ok chat-agent-system/internal/traceimpact 0.003s
|
||||
ok chat-agent-system/internal/upload 0.002s
|
||||
? chat-agent-system/internal/workflow [no test files]
|
||||
ok chat-agent-system/internal/workflow/runtime 0.004s
|
||||
ok chat-agent-system/internal/workflowdef 0.004s
|
||||
ok chat-agent-system/internal/workflowscope 0.002s
|
||||
ok chat-agent-system/internal/workspace 0.003s
|
||||
? chat-agent-system/templates [no test files]
|
||||
? chat-agent-system/web [no test files]
|
||||
FAIL
|
||||
TEST:1
|
||||
@@ -0,0 +1 @@
|
||||
VET:0
|
||||
@@ -0,0 +1,291 @@
|
||||
|
||||
> shuimu-workbench@1.0.0 build
|
||||
> tsc && vite build && node scripts/precompress-dist.mjs
|
||||
|
||||
vite v7.3.6 building client environment for production...
|
||||
transforming...
|
||||
✓ 4169 modules transformed.
|
||||
rendering chunks...
|
||||
computing gzip size...
|
||||
dist/index.html 0.72 kB │ gzip: 0.45 kB
|
||||
dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff 4.42 kB
|
||||
dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 4.93 kB
|
||||
dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 5.21 kB
|
||||
dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 5.47 kB
|
||||
dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff 5.98 kB
|
||||
dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff 6.19 kB
|
||||
dist/assets/KaTeX_Size1-Regular-C195tn64.woff 6.50 kB
|
||||
dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 6.91 kB
|
||||
dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 6.91 kB
|
||||
dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf 7.59 kB
|
||||
dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff 7.66 kB
|
||||
dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff 7.72 kB
|
||||
dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 9.64 kB
|
||||
dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 10.34 kB
|
||||
dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf 10.36 kB
|
||||
dist/assets/KaTeX_Script-Regular-D5yQViql.woff 10.59 kB
|
||||
dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 11.32 kB
|
||||
dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 11.35 kB
|
||||
dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf 11.51 kB
|
||||
dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 12.03 kB
|
||||
dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 12.22 kB
|
||||
dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf 12.23 kB
|
||||
dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff 12.32 kB
|
||||
dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf 12.34 kB
|
||||
dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf 12.37 kB
|
||||
dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff 13.21 kB
|
||||
dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff 13.30 kB
|
||||
dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 13.57 kB
|
||||
dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff 14.11 kB
|
||||
dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff 14.41 kB
|
||||
dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff 16.03 kB
|
||||
dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 16.40 kB
|
||||
dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 16.44 kB
|
||||
dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf 16.65 kB
|
||||
dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 16.78 kB
|
||||
dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 16.99 kB
|
||||
dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff 18.67 kB
|
||||
dist/assets/KaTeX_Math-Italic-DA0__PXp.woff 18.75 kB
|
||||
dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff 19.41 kB
|
||||
dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf 19.44 kB
|
||||
dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf 19.57 kB
|
||||
dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf 19.58 kB
|
||||
dist/assets/KaTeX_Main-Italic-BMLOBm91.woff 19.68 kB
|
||||
dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf 22.36 kB
|
||||
dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf 24.50 kB
|
||||
dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 25.32 kB
|
||||
dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 26.27 kB
|
||||
dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf 27.56 kB
|
||||
dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 28.08 kB
|
||||
dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff 29.91 kB
|
||||
dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff 30.77 kB
|
||||
dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf 31.20 kB
|
||||
dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf 31.31 kB
|
||||
dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf 32.97 kB
|
||||
dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff 33.52 kB
|
||||
dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf 33.58 kB
|
||||
dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf 51.34 kB
|
||||
dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf 53.58 kB
|
||||
dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf 63.63 kB
|
||||
dist/assets/index-Bq7tVxen.css 6.58 kB │ gzip: 1.99 kB
|
||||
dist/assets/katex-BOvhZl6_.css 30.25 kB │ gzip: 8.09 kB
|
||||
dist/assets/channel-DHR9WBLM.js 0.12 kB │ gzip: 0.13 kB
|
||||
dist/assets/katexRender-DyvI-2y4.js 0.15 kB │ gzip: 0.15 kB
|
||||
dist/assets/init-Gi6I4Gst.js 0.15 kB │ gzip: 0.13 kB
|
||||
dist/assets/chunk-2Q5K7J3B-CHsI7zem.js 0.19 kB │ gzip: 0.16 kB
|
||||
dist/assets/chunk-JWPE2WC7-CeOXuZlw.js 0.23 kB │ gzip: 0.17 kB
|
||||
dist/assets/chunk-XXDRQBXY-Cu_mBr7x.js 0.23 kB │ gzip: 0.21 kB
|
||||
dist/assets/git-branch-D2R7ViCv.js 0.26 kB │ gzip: 0.21 kB
|
||||
dist/assets/chunk-5VM5RSS4-lzTX0tjp.js 0.37 kB │ gzip: 0.27 kB
|
||||
dist/assets/stateDiagram-v2-MP3YSRHH-dJKbyi-1.js 0.43 kB │ gzip: 0.30 kB
|
||||
dist/assets/classDiagram-ZZMXUADV-CTPa2PBn.js 0.47 kB │ gzip: 0.32 kB
|
||||
dist/assets/classDiagram-v2-VYDZK3BY-CTPa2PBn.js 0.47 kB │ gzip: 0.32 kB
|
||||
dist/assets/chunk-POPQ4Y6H-B2jpZUkp.js 0.51 kB │ gzip: 0.36 kB
|
||||
dist/assets/swimlanesDiagram-VR7AAH4N-jP-BQxXC.js 0.55 kB │ gzip: 0.37 kB
|
||||
dist/assets/calculator-K0r_LA6K.js 0.56 kB │ gzip: 0.30 kB
|
||||
dist/assets/infoDiagram-27XIBGKW-BO8R-0BH.js 0.59 kB │ gzip: 0.40 kB
|
||||
dist/assets/sizeCapture-INFHLROL-KBPVYQON.js 0.94 kB │ gzip: 0.53 kB
|
||||
dist/assets/downloadDocx-Ep6Y-mWJ.js 1.13 kB │ gzip: 0.77 kB
|
||||
dist/assets/ordinal-Cboi1Yqb.js 1.19 kB │ gzip: 0.57 kB
|
||||
dist/assets/railroadDiagram-O6MQD6OU-3o3NI4V-.js 1.61 kB │ gzip: 0.76 kB
|
||||
dist/assets/MathBlockView-CP3gIAfz.js 1.67 kB │ gzip: 0.97 kB
|
||||
dist/assets/abnfDiagram-VCTEODGH-Bv_S-VQO.js 1.81 kB │ gzip: 0.89 kB
|
||||
dist/assets/chunk-F27PBJKO-DwG2Zkg1.js 1.88 kB │ gzip: 0.83 kB
|
||||
dist/assets/pegDiagram-XKGWAZYB-BRKb5jpA.js 1.96 kB │ gzip: 0.92 kB
|
||||
dist/assets/ebnfDiagram-PWID7BFC-CAlTqs1O.js 2.01 kB │ gzip: 0.88 kB
|
||||
dist/assets/xlsxRenderer-BQPATLFB.js 2.79 kB │ gzip: 1.69 kB
|
||||
dist/assets/arc-DOxBXeCg.js 3.44 kB │ gzip: 1.48 kB
|
||||
dist/assets/MermaidBlockView-BNZ12-FU.js 3.69 kB │ gzip: 1.80 kB
|
||||
dist/assets/diagram-Z3DM3KII-iO8-m9Hl.js 4.21 kB │ gzip: 1.83 kB
|
||||
dist/assets/defaultLocale-DX6XiGOO.js 4.69 kB │ gzip: 2.18 kB
|
||||
dist/assets/renderClipboardHtml-BXoFMW-3.js 5.18 kB │ gzip: 1.78 kB
|
||||
dist/assets/ExcelViewer-BZSJZn5Y.js 5.55 kB │ gzip: 2.21 kB
|
||||
dist/assets/linear-BDZQyaJG.js 5.66 kB │ gzip: 2.31 kB
|
||||
dist/assets/diagram-UQ7AKVKN-BnBJ_2R3.js 6.08 kB │ gzip: 2.62 kB
|
||||
dist/assets/pieDiagram-E7YTZNPT-nrtuGrNr.js 6.29 kB │ gzip: 2.62 kB
|
||||
dist/assets/ChatExportModal-Bfjz4958.js 7.58 kB │ gzip: 3.29 kB
|
||||
dist/assets/diagram-S7CK7UJ4-T-Bxl4t1.js 8.22 kB │ gzip: 3.67 kB
|
||||
dist/assets/cynefinDiagram-5FMLGOSQ-CnH3Z2JB.js 10.10 kB │ gzip: 3.63 kB
|
||||
dist/assets/stateDiagram-D77RDMKH-DVKtcg-v.js 10.36 kB │ gzip: 3.62 kB
|
||||
dist/assets/dagre-GXQ25YYZ-4R6bcum3.js 10.68 kB │ gzip: 4.03 kB
|
||||
dist/assets/diagram-VSXAHHWV-DLnsWaIP.js 10.72 kB │ gzip: 4.13 kB
|
||||
dist/assets/chunk-SVP7TREG-BhgGIDFD.js 15.74 kB │ gzip: 4.41 kB
|
||||
dist/assets/diagram-VX7I27RA-Duotn1-e.js 15.96 kB │ gzip: 5.70 kB
|
||||
dist/assets/ishikawaDiagram-5VMMS53U-Cg4LfJyo.js 17.47 kB │ gzip: 6.64 kB
|
||||
dist/assets/kanban-definition-UXKFOSKX-BALPAjTC.js 20.29 kB │ gzip: 7.20 kB
|
||||
dist/assets/BlockEditorModal-BOLlSDTH.js 22.24 kB │ gzip: 6.47 kB
|
||||
dist/assets/mindmap-definition-YA3MSWOX-pgZMQ8uL.js 23.18 kB │ gzip: 7.83 kB
|
||||
dist/assets/sankeyDiagram-P5KCCOFB-cHAs8eQM.js 23.20 kB │ gzip: 8.56 kB
|
||||
dist/assets/journeyDiagram-3NMN7TZE-DXSneWK0.js 23.57 kB │ gzip: 8.35 kB
|
||||
dist/assets/wardleyDiagram-VM6X3IG4-83YMC1gW.js 25.50 kB │ gzip: 6.79 kB
|
||||
dist/assets/gitGraphDiagram-WWUBYQGX-3ajohVe1.js 29.42 kB │ gzip: 8.70 kB
|
||||
dist/assets/FilePreviewPanel-BaAl4sya.js 30.12 kB │ gzip: 8.97 kB
|
||||
dist/assets/erDiagram-RLTQ6QDP-BDpOdOIc.js 30.79 kB │ gzip: 10.59 kB
|
||||
dist/assets/timeline-definition-24CTP7MA-d7EudEZG.js 30.83 kB │ gzip: 10.15 kB
|
||||
dist/assets/requirementDiagram-BXWQKSXE-2YVxwHhy.js 31.00 kB │ gzip: 9.73 kB
|
||||
dist/assets/quadrantDiagram-AXDQQJYC-DHNugfXZ.js 34.01 kB │ gzip: 9.98 kB
|
||||
dist/assets/layout-BrexQxSk.js 35.44 kB │ gzip: 12.75 kB
|
||||
dist/assets/chunk-IMKFNOWR-DL3J6pk5.js 38.08 kB │ gzip: 12.51 kB
|
||||
dist/assets/docxRenderer-Ilu9VYYG.js 40.90 kB │ gzip: 12.04 kB
|
||||
dist/assets/blockDiagram-I7D4REHJ-ozi4Xk9Q.js 41.76 kB │ gzip: 13.58 kB
|
||||
dist/assets/vennDiagram-4TSXK5OY-Dswu0LDK.js 41.96 kB │ gzip: 15.70 kB
|
||||
dist/assets/xychartDiagram-S5SC5T6Z-Do0p5dm5.js 44.51 kB │ gzip: 12.60 kB
|
||||
dist/assets/chunk-TICWLB2K-CsHC9dlc.js 49.18 kB │ gzip: 15.74 kB
|
||||
dist/assets/flowDiagram-HODETNUW-BGwlzTt0.js 61.85 kB │ gzip: 19.87 kB
|
||||
dist/assets/c4Diagram-7LVT6UL2-MxYk0iqL.js 65.47 kB │ gzip: 18.79 kB
|
||||
dist/assets/ganttDiagram-EL5Y4UJY-ijkjYohu.js 69.70 kB │ gzip: 23.56 kB
|
||||
dist/assets/AdminConsoleView-C6f6_5zf.js 74.34 kB │ gzip: 18.18 kB
|
||||
dist/assets/cose-bilkent-JH36ORCC-DW6Qeju_.js 81.70 kB │ gzip: 22.47 kB
|
||||
dist/assets/swimlanes-42K2YHIH-BaJ44xBg.js 115.79 kB │ gzip: 42.51 kB
|
||||
dist/assets/sequenceDiagram-WJ2MYXX4-BqEzakUS.js 116.96 kB │ gzip: 31.13 kB
|
||||
dist/assets/architectureDiagram-5GKGNRK7-BW5RJe7u.js 151.83 kB │ gzip: 43.14 kB
|
||||
dist/assets/DocxViewer-thvChHTS.js 175.87 kB │ gzip: 52.31 kB
|
||||
dist/assets/katex-HP8lGamR.js 261.31 kB │ gzip: 77.57 kB
|
||||
dist/assets/katex-BJxdM42i.js 261.44 kB │ gzip: 77.80 kB
|
||||
dist/assets/index-BXtlAAcH.js 365.75 kB │ gzip: 104.72 kB
|
||||
dist/assets/xlsx-Cig0qvpi.js 424.39 kB │ gzip: 141.60 kB
|
||||
dist/assets/cytoscape.esm-BB4DxJjf.js 443.04 kB │ gzip: 141.98 kB
|
||||
dist/assets/mermaid.core-C3o1vFDn.js 682.57 kB │ gzip: 168.90 kB
|
||||
dist/assets/cynefin-OW5HDTMX-Ca8YX88f.js 687.91 kB │ gzip: 154.14 kB
|
||||
dist/assets/index-cbAtdfhj.js 814.04 kB │ gzip: 239.32 kB
|
||||
|
||||
(!) Some chunks are larger than 500 kB after minification. Consider:
|
||||
- Using dynamic import() to code-split the application
|
||||
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
|
||||
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
|
||||
✓ built in 8.74s
|
||||
precompress: 66 candidate(s) under /home/weichen/projects/Agent-System/.worktrees/refresh-task-state-recovery-2026-09-28/web/dist
|
||||
br assets/AdminConsoleView-C6f6_5zf.js 74340 → 14847 (−80.0%)
|
||||
gzip assets/AdminConsoleView-C6f6_5zf.js 74340 → 17742 (−76.1%)
|
||||
br assets/BlockEditorModal-BOLlSDTH.js 22241 → 5389 (−75.8%)
|
||||
gzip assets/BlockEditorModal-BOLlSDTH.js 22241 → 6375 (−71.3%)
|
||||
br assets/ChatExportModal-Bfjz4958.js 7581 → 2732 (−64.0%)
|
||||
gzip assets/ChatExportModal-Bfjz4958.js 7581 → 3242 (−57.2%)
|
||||
br assets/DocxViewer-thvChHTS.js 175867 → 46191 (−73.7%)
|
||||
gzip assets/DocxViewer-thvChHTS.js 175867 → 52082 (−70.4%)
|
||||
br assets/ExcelViewer-BZSJZn5Y.js 5552 → 1885 (−66.0%)
|
||||
gzip assets/ExcelViewer-BZSJZn5Y.js 5552 → 2194 (−60.5%)
|
||||
br assets/FilePreviewPanel-BaAl4sya.js 30116 → 7564 (−74.9%)
|
||||
gzip assets/FilePreviewPanel-BaAl4sya.js 30116 → 8813 (−70.7%)
|
||||
br assets/MathBlockView-CP3gIAfz.js 1669 → 800 (−52.1%)
|
||||
gzip assets/MathBlockView-CP3gIAfz.js 1669 → 962 (−42.4%)
|
||||
br assets/MermaidBlockView-BNZ12-FU.js 3690 → 1499 (−59.4%)
|
||||
gzip assets/MermaidBlockView-BNZ12-FU.js 3690 → 1794 (−51.4%)
|
||||
br assets/abnfDiagram-VCTEODGH-Bv_S-VQO.js 1809 → 785 (−56.6%)
|
||||
gzip assets/abnfDiagram-VCTEODGH-Bv_S-VQO.js 1809 → 895 (−50.5%)
|
||||
br assets/arc-DOxBXeCg.js 3441 → 1296 (−62.3%)
|
||||
gzip assets/arc-DOxBXeCg.js 3441 → 1455 (−57.7%)
|
||||
br assets/architectureDiagram-5GKGNRK7-BW5RJe7u.js 151827 → 36975 (−75.6%)
|
||||
gzip assets/architectureDiagram-5GKGNRK7-BW5RJe7u.js 151827 → 42841 (−71.8%)
|
||||
br assets/blockDiagram-I7D4REHJ-ozi4Xk9Q.js 41757 → 11969 (−71.3%)
|
||||
gzip assets/blockDiagram-I7D4REHJ-ozi4Xk9Q.js 41757 → 13469 (−67.7%)
|
||||
br assets/c4Diagram-7LVT6UL2-MxYk0iqL.js 65473 → 15133 (−76.9%)
|
||||
gzip assets/c4Diagram-7LVT6UL2-MxYk0iqL.js 65473 → 18679 (−71.5%)
|
||||
br assets/chunk-F27PBJKO-DwG2Zkg1.js 1883 → 742 (−60.6%)
|
||||
gzip assets/chunk-F27PBJKO-DwG2Zkg1.js 1883 → 831 (−55.9%)
|
||||
br assets/chunk-IMKFNOWR-DL3J6pk5.js 38076 → 10974 (−71.2%)
|
||||
gzip assets/chunk-IMKFNOWR-DL3J6pk5.js 38076 → 12434 (−67.3%)
|
||||
br assets/chunk-SVP7TREG-BhgGIDFD.js 15735 → 3966 (−74.8%)
|
||||
gzip assets/chunk-SVP7TREG-BhgGIDFD.js 15735 → 4418 (−71.9%)
|
||||
br assets/chunk-TICWLB2K-CsHC9dlc.js 49178 → 13033 (−73.5%)
|
||||
gzip assets/chunk-TICWLB2K-CsHC9dlc.js 49178 → 15645 (−68.2%)
|
||||
br assets/cose-bilkent-JH36ORCC-DW6Qeju_.js 81701 → 19574 (−76.0%)
|
||||
gzip assets/cose-bilkent-JH36ORCC-DW6Qeju_.js 81701 → 22391 (−72.6%)
|
||||
br assets/cynefin-OW5HDTMX-Ca8YX88f.js 687913 → 124540 (−81.9%)
|
||||
gzip assets/cynefin-OW5HDTMX-Ca8YX88f.js 687913 → 153068 (−77.7%)
|
||||
br assets/cynefinDiagram-5FMLGOSQ-CnH3Z2JB.js 10097 → 3248 (−67.8%)
|
||||
gzip assets/cynefinDiagram-5FMLGOSQ-CnH3Z2JB.js 10097 → 3620 (−64.1%)
|
||||
br assets/cytoscape.esm-BB4DxJjf.js 443036 → 119602 (−73.0%)
|
||||
gzip assets/cytoscape.esm-BB4DxJjf.js 443036 → 141741 (−68.0%)
|
||||
br assets/dagre-GXQ25YYZ-4R6bcum3.js 10676 → 3680 (−65.5%)
|
||||
gzip assets/dagre-GXQ25YYZ-4R6bcum3.js 10676 → 4046 (−62.1%)
|
||||
br assets/defaultLocale-DX6XiGOO.js 4688 → 2012 (−57.1%)
|
||||
gzip assets/defaultLocale-DX6XiGOO.js 4688 → 2166 (−53.8%)
|
||||
br assets/diagram-S7CK7UJ4-T-Bxl4t1.js 8217 → 3326 (−59.5%)
|
||||
gzip assets/diagram-S7CK7UJ4-T-Bxl4t1.js 8217 → 3677 (−55.3%)
|
||||
br assets/diagram-UQ7AKVKN-BnBJ_2R3.js 6079 → 2367 (−61.1%)
|
||||
gzip assets/diagram-UQ7AKVKN-BnBJ_2R3.js 6079 → 2617 (−57.0%)
|
||||
br assets/diagram-VSXAHHWV-DLnsWaIP.js 10716 → 3782 (−64.7%)
|
||||
gzip assets/diagram-VSXAHHWV-DLnsWaIP.js 10716 → 4154 (−61.2%)
|
||||
br assets/diagram-VX7I27RA-Duotn1-e.js 15957 → 5171 (−67.6%)
|
||||
gzip assets/diagram-VX7I27RA-Duotn1-e.js 15957 → 5707 (−64.2%)
|
||||
br assets/diagram-Z3DM3KII-iO8-m9Hl.js 4211 → 1642 (−61.0%)
|
||||
gzip assets/diagram-Z3DM3KII-iO8-m9Hl.js 4211 → 1818 (−56.8%)
|
||||
br assets/docxRenderer-Ilu9VYYG.js 40904 → 10539 (−74.2%)
|
||||
gzip assets/docxRenderer-Ilu9VYYG.js 40904 → 11977 (−70.7%)
|
||||
br assets/downloadDocx-Ep6Y-mWJ.js 1130 → 612 (−45.8%)
|
||||
gzip assets/downloadDocx-Ep6Y-mWJ.js 1130 → 767 (−32.1%)
|
||||
br assets/ebnfDiagram-PWID7BFC-CAlTqs1O.js 2005 → 784 (−60.9%)
|
||||
gzip assets/ebnfDiagram-PWID7BFC-CAlTqs1O.js 2005 → 884 (−55.9%)
|
||||
br assets/erDiagram-RLTQ6QDP-BDpOdOIc.js 30790 → 9175 (−70.2%)
|
||||
gzip assets/erDiagram-RLTQ6QDP-BDpOdOIc.js 30790 → 10542 (−65.8%)
|
||||
br assets/flowDiagram-HODETNUW-BGwlzTt0.js 61847 → 16384 (−73.5%)
|
||||
gzip assets/flowDiagram-HODETNUW-BGwlzTt0.js 61847 → 19717 (−68.1%)
|
||||
br assets/ganttDiagram-EL5Y4UJY-ijkjYohu.js 69695 → 20666 (−70.3%)
|
||||
gzip assets/ganttDiagram-EL5Y4UJY-ijkjYohu.js 69695 → 23425 (−66.4%)
|
||||
br assets/gitGraphDiagram-WWUBYQGX-3ajohVe1.js 29417 → 7789 (−73.5%)
|
||||
gzip assets/gitGraphDiagram-WWUBYQGX-3ajohVe1.js 29417 → 8695 (−70.4%)
|
||||
br assets/index-BXtlAAcH.js 365745 → 89917 (−75.4%)
|
||||
gzip assets/index-BXtlAAcH.js 365745 → 104064 (−71.5%)
|
||||
br assets/index-Bq7tVxen.css 6576 → 1656 (−74.8%)
|
||||
gzip assets/index-Bq7tVxen.css 6576 → 1974 (−70.0%)
|
||||
br assets/index-cbAtdfhj.js 814041 → 187943 (−76.9%)
|
||||
gzip assets/index-cbAtdfhj.js 814041 → 236064 (−71.0%)
|
||||
br assets/ishikawaDiagram-5VMMS53U-Cg4LfJyo.js 17469 → 6007 (−65.6%)
|
||||
gzip assets/ishikawaDiagram-5VMMS53U-Cg4LfJyo.js 17469 → 6634 (−62.0%)
|
||||
br assets/journeyDiagram-3NMN7TZE-DXSneWK0.js 23571 → 7531 (−68.0%)
|
||||
gzip assets/journeyDiagram-3NMN7TZE-DXSneWK0.js 23571 → 8331 (−64.7%)
|
||||
br assets/kanban-definition-UXKFOSKX-BALPAjTC.js 20291 → 6409 (−68.4%)
|
||||
gzip assets/kanban-definition-UXKFOSKX-BALPAjTC.js 20291 → 7172 (−64.7%)
|
||||
br assets/katex-BJxdM42i.js 261435 → 63900 (−75.6%)
|
||||
gzip assets/katex-BJxdM42i.js 261435 → 77355 (−70.4%)
|
||||
br assets/katex-BOvhZl6_.css 30249 → 6978 (−76.9%)
|
||||
gzip assets/katex-BOvhZl6_.css 30249 → 8010 (−73.5%)
|
||||
br assets/katex-HP8lGamR.js 261313 → 63694 (−75.6%)
|
||||
gzip assets/katex-HP8lGamR.js 261313 → 77109 (−70.5%)
|
||||
br assets/layout-BrexQxSk.js 35436 → 11571 (−67.3%)
|
||||
gzip assets/layout-BrexQxSk.js 35436 → 12751 (−64.0%)
|
||||
br assets/linear-BDZQyaJG.js 5661 → 2117 (−62.6%)
|
||||
gzip assets/linear-BDZQyaJG.js 5661 → 2318 (−59.1%)
|
||||
br assets/mermaid.core-C3o1vFDn.js 682569 → 140363 (−79.4%)
|
||||
gzip assets/mermaid.core-C3o1vFDn.js 682569 → 168123 (−75.4%)
|
||||
br assets/mindmap-definition-YA3MSWOX-pgZMQ8uL.js 23175 → 6998 (−69.8%)
|
||||
gzip assets/mindmap-definition-YA3MSWOX-pgZMQ8uL.js 23175 → 7799 (−66.3%)
|
||||
br assets/ordinal-Cboi1Yqb.js 1189 → 497 (−58.2%)
|
||||
gzip assets/ordinal-Cboi1Yqb.js 1189 → 574 (−51.7%)
|
||||
br assets/pegDiagram-XKGWAZYB-BRKb5jpA.js 1956 → 821 (−58.0%)
|
||||
gzip assets/pegDiagram-XKGWAZYB-BRKb5jpA.js 1956 → 927 (−52.6%)
|
||||
br assets/pieDiagram-E7YTZNPT-nrtuGrNr.js 6294 → 2407 (−61.8%)
|
||||
gzip assets/pieDiagram-E7YTZNPT-nrtuGrNr.js 6294 → 2629 (−58.2%)
|
||||
br assets/quadrantDiagram-AXDQQJYC-DHNugfXZ.js 34010 → 8523 (−74.9%)
|
||||
gzip assets/quadrantDiagram-AXDQQJYC-DHNugfXZ.js 34010 → 9876 (−71.0%)
|
||||
br assets/railroadDiagram-O6MQD6OU-3o3NI4V-.js 1606 → 683 (−57.5%)
|
||||
gzip assets/railroadDiagram-O6MQD6OU-3o3NI4V-.js 1606 → 758 (−52.8%)
|
||||
br assets/renderClipboardHtml-BXoFMW-3.js 5180 → 1452 (−72.0%)
|
||||
gzip assets/renderClipboardHtml-BXoFMW-3.js 5180 → 1778 (−65.7%)
|
||||
br assets/requirementDiagram-BXWQKSXE-2YVxwHhy.js 31000 → 8241 (−73.4%)
|
||||
gzip assets/requirementDiagram-BXWQKSXE-2YVxwHhy.js 31000 → 9661 (−68.8%)
|
||||
br assets/sankeyDiagram-P5KCCOFB-cHAs8eQM.js 23198 → 7819 (−66.3%)
|
||||
gzip assets/sankeyDiagram-P5KCCOFB-cHAs8eQM.js 23198 → 8565 (−63.1%)
|
||||
br assets/sequenceDiagram-WJ2MYXX4-BqEzakUS.js 116962 → 25735 (−78.0%)
|
||||
gzip assets/sequenceDiagram-WJ2MYXX4-BqEzakUS.js 116962 → 30955 (−73.5%)
|
||||
br assets/stateDiagram-D77RDMKH-DVKtcg-v.js 10361 → 3334 (−67.8%)
|
||||
gzip assets/stateDiagram-D77RDMKH-DVKtcg-v.js 10361 → 3623 (−65.0%)
|
||||
br assets/swimlanes-42K2YHIH-BaJ44xBg.js 115791 → 36679 (−68.3%)
|
||||
gzip assets/swimlanes-42K2YHIH-BaJ44xBg.js 115791 → 42170 (−63.6%)
|
||||
br assets/timeline-definition-24CTP7MA-d7EudEZG.js 30829 → 9133 (−70.4%)
|
||||
gzip assets/timeline-definition-24CTP7MA-d7EudEZG.js 30829 → 10143 (−67.1%)
|
||||
br assets/vennDiagram-4TSXK5OY-Dswu0LDK.js 41961 → 13993 (−66.7%)
|
||||
gzip assets/vennDiagram-4TSXK5OY-Dswu0LDK.js 41961 → 15643 (−62.7%)
|
||||
br assets/wardleyDiagram-VM6X3IG4-83YMC1gW.js 25502 → 6077 (−76.2%)
|
||||
gzip assets/wardleyDiagram-VM6X3IG4-83YMC1gW.js 25502 → 6751 (−73.5%)
|
||||
br assets/xlsx-Cig0qvpi.js 424392 → 117659 (−72.3%)
|
||||
gzip assets/xlsx-Cig0qvpi.js 424392 → 140814 (−66.8%)
|
||||
br assets/xlsxRenderer-BQPATLFB.js 2785 → 1354 (−51.4%)
|
||||
gzip assets/xlsxRenderer-BQPATLFB.js 2785 → 1677 (−39.8%)
|
||||
br assets/xychartDiagram-S5SC5T6Z-Do0p5dm5.js 44509 → 11058 (−75.2%)
|
||||
gzip assets/xychartDiagram-S5SC5T6Z-Do0p5dm5.js 44509 → 12534 (−71.8%)
|
||||
br fonts/OFL.txt 4474 → 1437 (−67.9%)
|
||||
gzip fonts/OFL.txt 4474 → 1980 (−55.7%)
|
||||
precompress: wrote 66 .br and 66 .gz
|
||||
NPM_BUILD:0
|
||||
@@ -0,0 +1,651 @@
|
||||
|
||||
> shuimu-workbench@1.0.0 test
|
||||
> tsx --test "src/**/!(*InteractiveCard*|*chatWindowArtifact*|*deliverableSync*).test.ts" && vitest run src/components/chat/InteractiveCard.test.ts src/components/chat/chatWindowArtifact.test.ts src/components/chat/deliverableSync.test.ts
|
||||
|
||||
✔ AuthClient: 基础登录与 .env 超级管理员鉴权 (2.388929ms)
|
||||
✔ AuthClient: 自主注册受控流程(仅填用户名密码姓名,职位强制绑定访客待指派) (0.448527ms)
|
||||
✔ AuthClient: 开启注册需审批时,注册账号处于待审状态并禁止登录 (0.495266ms)
|
||||
✔ AuthClient: 管理员创建账号必须在首次登录修改密码 (0.258262ms)
|
||||
✔ authErrorMessage: 优先信封 error 领域原因,避免裸 JSON / 无权访问误导 (0.227843ms)
|
||||
✔ mapUserAccount: snake_case Go contract → camelCase (1.231233ms)
|
||||
✔ mapUserAccount: prefers snake_case when both present (0.164515ms)
|
||||
✔ mapLoginResult/mapMeResult use top-level must_change_password as sole authority (0.211534ms)
|
||||
✔ mapUserAccountList maps admin users list (0.116027ms)
|
||||
✔ AuthClient.login + fetchMe map real snake_case wire payloads (36.529405ms)
|
||||
✔ shouldAutoRetryChat: disabled until idempotency (0.913842ms)
|
||||
✔ shouldAutoRetryTask: transient only, max 2 (0.229563ms)
|
||||
✔ shouldAutoRetryTask: document busy never retries (0.136656ms)
|
||||
✔ shouldAutoRetry alias delegates to task path (0.137816ms)
|
||||
✔ autoRetryDelayMs: 1s then 2s (0.137336ms)
|
||||
✔ fallbackErrorClass: network → transient, workspace → resumable (1.663131ms)
|
||||
✔ upsertChatSession surfaces safe INTERNAL envelope instead of raw JSON text (79.905537ms)
|
||||
[sse] 忽略无法解析的帧: {not valid json SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
|
||||
at JSON.parse (<anonymous>)
|
||||
at parseFrame (/home/weichen/projects/Agent-System/.worktrees/refresh-task-state-recovery-2026-09-28/web/src/api/chatStream.ts:126:26)
|
||||
at parseSSEChunk (/home/weichen/projects/Agent-System/.worktrees/refresh-task-state-recovery-2026-09-28/web/src/api/chatStream.ts:199:20)
|
||||
at TestContext.<anonymous> (/home/weichen/projects/Agent-System/.worktrees/refresh-task-state-recovery-2026-09-28/web/src/api/chatStream.test.ts:41:31)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
|
||||
✔ parseSSEChunk: parses event and data lines (1.00906ms)
|
||||
✔ parseSSEChunk: keeps incomplete frame in rest (0.231474ms)
|
||||
✔ parseSSEChunk: flush parses final frame without trailing delimiter (0.224793ms)
|
||||
✔ parseSSEChunk: ignores heartbeat comments and keeps last id (0.139066ms)
|
||||
✔ parseSSEChunk: skips a single malformed frame and keeps parsing the next valid frame (5.017202ms)
|
||||
✔ parseSSEChunk: parses CRLF-delimited frames (0.229503ms)
|
||||
✔ parseSSEChunk: mixed LF and CRLF delimiters (0.137336ms)
|
||||
✔ API_BASE is empty relative base (1.037719ms)
|
||||
✔ apiFetch attaches Bearer from session storage (64.929112ms)
|
||||
✔ apiFetch retries GET on 503 then succeeds (201.72881ms)
|
||||
✔ apiFetch does not retry POST by default (0.516094ms)
|
||||
✔ readApiError preserves typed envelope for error cards (1.498965ms)
|
||||
✔ apiFetch auth:false skips Authorization (0.530665ms)
|
||||
▶ docgov lifecycle API client
|
||||
✔ claimDocument posts to /api/v1/docgov/claim with author role (23.291168ms)
|
||||
✔ submitForReview posts to /api/v1/docgov/submit-review (0.706349ms)
|
||||
✔ reviewDecision posts action and optional reason (0.817416ms)
|
||||
✔ approverDecision posts action and rollback_to (0.954792ms)
|
||||
✔ proposeModification posts to /api/v1/docgov/proposal (1.198325ms)
|
||||
✔ decideModificationProposal posts to /api/v1/docgov/proposal/decide (0.720289ms)
|
||||
✔ docgov lifecycle API client (28.742506ms)
|
||||
✔ updateFreeformDoc exposes CONCURRENCY_CONFLICT on 409 (22.916699ms)
|
||||
▶ notifications inbox helpers
|
||||
✔ ranks action_required / proposal ahead of info (0.65737ms)
|
||||
✔ notifications inbox helpers (1.486036ms)
|
||||
✔ addProjectMember exposes structured ProjectSettingsApiError on 403 (25.801144ms)
|
||||
✔ updateProjectMember surfaces safe INTERNAL message on 500 duty-sync failure (0.822326ms)
|
||||
▶ projectSettingsError
|
||||
✔ keeps status, code, message and issues on PLAN_FROZEN (1.624582ms)
|
||||
✔ maps revision and concurrent conflicts to distinct recovery (0.172775ms)
|
||||
✔ joins validation issues but still keeps typed codes (0.189844ms)
|
||||
✔ prefers envelope code over legacy error field (0.138986ms)
|
||||
✔ unwraps nested business JSON inside VERSION_CONFLICT envelope (0.187354ms)
|
||||
✔ maps bare VERSION_CONFLICT envelope to retry_after_switch (0.106467ms)
|
||||
✔ retains the local draft when a late tab hits PLAN_FROZEN (0.114897ms)
|
||||
✔ prefers error_code over legacy bare error string (DOC_BUSY) (0.128807ms)
|
||||
✔ projectSettingsError (4.087899ms)
|
||||
✔ fetchProjectTrace turns null arrays into empty arrays (25.555001ms)
|
||||
✔ fetchProjectTrace keeps real arrays and rejects failed responses (1.302701ms)
|
||||
✔ Project workflow types validation (2.125657ms)
|
||||
✔ createTaskRun exposes DOC_BUSY detail without a retryable run (27.347498ms)
|
||||
✔ parseSSEChunk tracks id seq for after_seq reconnect (0.474346ms)
|
||||
✔ subscribeTaskRunEvents reconnects on EOF with advancing after_seq (6.424009ms)
|
||||
✔ readTaskRunSSEOnce marks non-terminal EOF for reconnect (0.68087ms)
|
||||
✔ subscribeTaskRunEvents fails fast on 401 without retrying (0.491175ms)
|
||||
✔ subscribeTaskRunEvents fails fast on 404 without retrying (1.400458ms)
|
||||
✔ buildTaskRequest: maps five project fields + task_key + user identity (0.757228ms)
|
||||
✔ buildTaskRequest: omits optional fields when absent (0.164035ms)
|
||||
✔ buildTaskRequest: includes materials when provided and non-empty (0.755688ms)
|
||||
✔ buildResumeRequest: adds work_dir on top of task request (0.175045ms)
|
||||
✔ buildTaskRequest: canonicalizes legacy doc-p doc_id (0.260083ms)
|
||||
✔ buildTaskRequest: UI-05 pinned intake keeps task_key + doc_proj6109_intake (0.193434ms)
|
||||
✔ buildResumeRequest: forwards add_steps when provided (0.153185ms)
|
||||
✔ CategoryDock: 渲染顶部 🔔 未读提醒项及角标数字 (20.272788ms)
|
||||
✔ CategoryDock: isAllReadCompleted 为 true 时渲染完成态提示 (9.797689ms)
|
||||
✔ CategoryDock: 0 未读时不渲染红色未读角标 (6.548825ms)
|
||||
✔ CategoryDock: 分类项包含依次跳转与遍历提示信息 (5.285423ms)
|
||||
✔ chat column lives outside the workbench shell (0.954951ms)
|
||||
✔ InputArea keeps draft when send preflight fails (1.420548ms)
|
||||
✔ InputArea clears draft only after accepted send (2.463867ms)
|
||||
✔ InputArea shows thinking block message (0.197614ms)
|
||||
✔ InputArea keeps draft when attachment registration fails (0.247822ms)
|
||||
✔ TaskRunCard renders timeline with title badge and current step (13.771851ms)
|
||||
✔ TaskRunCard completed state keeps card with duration and artifact (2.308901ms)
|
||||
✔ TaskRunCard sanitizes frozen progress labels on completed steps (1.979611ms)
|
||||
✔ TaskRunCard keeps shell when only taskRunId is present (0.864224ms)
|
||||
✔ active document pane lives outside the workbench shell (1.437047ms)
|
||||
✔ isTemplateOnly 交付物应正确识别为初始模板展示态 (7.921545ms)
|
||||
✔ workbench shell lives outside App (4.726439ms)
|
||||
✔ 左侧栏鼠标严密绝对跟随与夹紧测试 (Left Rail Exact Pointer Following & Clamping) (0.713178ms)
|
||||
✔ 右侧栏鼠标严密绝对跟随与反向位移测试 (Right Rail Exact Pointer Following & Inverted Delta) (0.126506ms)
|
||||
✔ trace panel is not mounted above the chat stream (5.641603ms)
|
||||
✔ detects legacy mock and neuropulse/ten-projects ids (0.864744ms)
|
||||
✔ detects colliding demo project ids (0.132876ms)
|
||||
✔ applyRestoredFileRaw: 以 prev 为权威,只补丁缺失 fileRaw,不丢新增条目 (1.154566ms)
|
||||
✔ applyRestoredFileRaw: 无可用 fileRaw 时返回同一引用 (0.187955ms)
|
||||
✔ login member sync stays one request for 1, 30 and 100 projects (6.07364ms)
|
||||
✔ viewer seed keeps session visibility without a member-list call (0.397488ms)
|
||||
✔ later session response cannot overwrite a newer user (0.190665ms)
|
||||
✔ in-flight member sync collapses to one call (0.263752ms)
|
||||
✔ 每人独立私聊:同一项目不同用户拥有不同 sessionId (0.881204ms)
|
||||
✔ 左侧会话列表按当前用户过滤,不可见他人私聊 (0.256262ms)
|
||||
✔ 消息严格归属各自私聊 session,无跨用户混聊 (0.870234ms)
|
||||
✔ 切换用户时 resolve 到该用户同项目私聊会话 (0.244812ms)
|
||||
✔ resolve 优先有消息的私聊,跳过空壳 sess-p{N}-u{id} (1.166125ms)
|
||||
✔ ensurePrivateSessionsForUser 为缺失用户补齐空私聊 (0.593802ms)
|
||||
✔ 多项目会话消息隔离测试仍成立(endo vs ultrasound) (0.33904ms)
|
||||
✔ window cache LRU: 驱逐带 userId 前缀的完整 cache key,不留孤儿条目 (1.36061ms)
|
||||
✔ window cache: 无 schema 的旧载荷不当作有效缓存 (7.710341ms)
|
||||
✔ window cache: 读写同一 userId 前缀,跨用户不串读 (0.273892ms)
|
||||
✔ 残留的 legacy demo sessionId 被读取侧清除并返回 fallback (0.905753ms)
|
||||
✔ 真实 sessionId 不被误清 (0.152565ms)
|
||||
✔ 无 window 环境(SSR)安全返回 fallback (0.068078ms)
|
||||
✔ same account and first login do not reset workbench storage (1.144056ms)
|
||||
✔ switching accounts resets workbench storage (0.182865ms)
|
||||
✔ resetWorkbenchStorage preserves org user directory for cross-account project create (1.219384ms)
|
||||
✔ parseAnchorHash: 正确解析标准深链 hash (1.438807ms)
|
||||
✔ formatAnchorHash: 正确构造深链格式 (0.106107ms)
|
||||
✔ navigateToAnchor: 快路径直接命中 DOM 节点 (0.388478ms)
|
||||
✔ navigateToAnchor: 慢路径拉取分片后命中 (79.542228ms)
|
||||
✔ resolveTurnAnchorId: 方案 A 轮次归一化测试 (0.507675ms)
|
||||
✔ 全链路场景 1: 用户自主受控注册(仅填用户名/密码/姓名,职位严格绑定访客待指派) (1.552684ms)
|
||||
✔ 全链路场景 2: 系统准入策略(关闭自主注册与开启强制审批流转) (0.647091ms)
|
||||
✔ 全链路场景 3: 管理员开通账号首次强制改密闭环 (0.242653ms)
|
||||
✔ 全链路场景 4: 环境标识随登录会话返回(dev/test/main) (0.261022ms)
|
||||
✔ cleanHeadingContent: 手工多级序号前缀清洗与专业词汇保护 (2.277532ms)
|
||||
✔ 标题多层级自动编号:单 H1 文档总标题自适应模式 (1 -> 1.1 -> 1.1.1) (0.34501ms)
|
||||
✔ 标题多层级自动编号:多 H1 标准大章模式 (0.137276ms)
|
||||
✔ 跨章节图、表、公式多级按章重编号测试 (表 1-1, 表 2-1, 图 3-1, 式 (4-1)) (0.154156ms)
|
||||
✔ 有序列表与无序列表编号与渲染功能测试 (2.399788ms)
|
||||
✔ 动态块操作闭环重编号验证 (Insert, Move, Delete) (3.589093ms)
|
||||
✔ findMatchingBlockForReview: 能够精准按审查关键词(电气安全/EMC/竞品)定位段落块 (0.848975ms)
|
||||
✔ findMatchingBlockForReview: 能够按外部指定 hint 线索精确命中目标块 (0.170635ms)
|
||||
✔ createRejectConfirmationCard: 生成合规危险级驳回门禁卡片 (0.605742ms)
|
||||
✔ isAffirmativeResponse: 准确识别双轨交互中的肯定意图输入 (0.205574ms)
|
||||
✔ findLastActiveCard: 正确在会话历史中提取最新的待决策卡片 (0.201834ms)
|
||||
✔ decrementActiveCardsTtl: 多轮对话超时递减与自动失效归档 (0.163195ms)
|
||||
✔ applyConfirmRejectToDoc & applyUndoRejectToDoc: 驳回挂载批注与60s窗口期撤销完整闭环 (0.251372ms)
|
||||
✔ resolveCardInMessages & cancelCardInMessages: 消息流中卡片终态沉淀 (0.151626ms)
|
||||
✔ classifyRejectUtterance: 询问原因 → QUERY_REJECT_REASON (1.295522ms)
|
||||
✔ classifyRejectUtterance: 明确执行 → EXECUTE_REJECT (0.177635ms)
|
||||
✔ classifyRejectUtterance: 模糊 → OTHER (0.197495ms)
|
||||
✔ resolveDocGovCard: 编制人查询 → 信息卡且无 confirm_reject (0.788967ms)
|
||||
✔ resolveDocGovCard: 审核人执行 + reviewing → 确认卡 (0.289862ms)
|
||||
✔ resolveDocGovCard: 编制人误说执行驳回 → 无权纯文本 (0.141016ms)
|
||||
✔ resolveDocGovCard: 批准人 + approving → 终审确认卡 (0.274622ms)
|
||||
✔ resolveDocGovCard: 编制人 OTHER → 引导查询无危险按钮 (0.128557ms)
|
||||
✔ canConfirmReject: 仅审核/批准 (0.537784ms)
|
||||
✔ deriveFallbackCategory identifies categories correctly (0.870374ms)
|
||||
✔ enrichMessagesWithCategories backfills categories without overwriting existing ones (0.313601ms)
|
||||
✔ Markdown ➔ Block AST 解析测试 (标题自适应编号与图表公式全链路) (4.275193ms)
|
||||
✔ 动态重编号引擎测试 (多 H1 规范章节重排验证) (0.980431ms)
|
||||
✔ Block AST ➔ Markdown 双向无损往返还原测试 (0.843655ms)
|
||||
✔ 三层交付物多用户隐私绝对隔离测试 (Multi-user Strict Privacy Isolation) (1.088098ms)
|
||||
✔ 一键共享至项目参考库流转测试 (Promote to Shared Reference Test) (0.221383ms)
|
||||
✔ 撤回共享恢复私有草稿测试 (Revoke Shared Reference Test) (0.408948ms)
|
||||
✔ 受控工作流纯净性红线检验 (Workflow Purity Security Red Line) (0.129456ms)
|
||||
✔ isSameMessage: clientTurnId match (1.861744ms)
|
||||
✔ isSameMessage: serverId match without turn (0.175055ms)
|
||||
✔ filterPendingLiveMessages: real temp vs server id (0.137566ms)
|
||||
✔ mergeLiveOverHistory preserves server id from live (0.173595ms)
|
||||
✔ mergeLiveOverHistory does not let inflight live mask terminal history (0.134716ms)
|
||||
✔ mergeLiveOverHistory keeps live terminal over history inflight (0.112497ms)
|
||||
✔ applyPhaseEvent: appends first phase as running node (1.403038ms)
|
||||
✔ applyPhaseEvent: completes predecessors when a new phase arrives (0.249813ms)
|
||||
✔ applyPhaseEvent: same phase refreshes label in place (no duplicate node) (0.243692ms)
|
||||
✔ applyPhaseEvent: unknown phase falls back to hourglass meta (0.130056ms)
|
||||
✔ applyPhaseEvent: prefers backend message over short label (0.114206ms)
|
||||
✔ applyPhaseEvent: step_completed closes running entry without appending (0.239473ms)
|
||||
✔ applyPhaseEvent: stage_completed closes entries (0.163325ms)
|
||||
✔ applyPhaseEvent: task_failed appends warning node (0.133246ms)
|
||||
✔ applyPhaseEvent: decorates label with step index / total steps (0.137436ms)
|
||||
✔ applyPhaseEvent: repeated step_executing with step_index gets separate nodes (0.200365ms)
|
||||
✔ applyPhaseEvent: ignores event without phase payload (0.097807ms)
|
||||
✔ applyPhaseEvent: step_failed marks running entry as failed (keeps step label) (0.114317ms)
|
||||
✔ applyPhaseEvent: step_failed appends failed node when no running entry exists (1.234163ms)
|
||||
✔ applyPhaseEvent: retry of a failed step resets it to running (0.141366ms)
|
||||
✔ applyPhaseEvent: stores detail from phase.message (0.075458ms)
|
||||
✔ PHASE_META: document_parsing and document_summarizing are registered (0.114747ms)
|
||||
✔ PLAN_CONFIRM 卡片来自 PlanView 且不触达 AST (1.988191ms)
|
||||
✔ 409 冲突把卡片标为 outdated 并换上最新 revision (0.252702ms)
|
||||
✔ GET reconcile 用权威 PlanView 覆盖本地 executing,不假装成功 (0.226923ms)
|
||||
✔ 过期计划映射 expired (0.125736ms)
|
||||
✔ 非计划卡不被误判 (0.112107ms)
|
||||
✔ 集成验证:项目切换时交付物严格隔离,新建项目即刻包含 7 份模板副本 (6.355481ms)
|
||||
✔ 集成验证:三层隐私与项目物理隔离双重约束 (8.259005ms)
|
||||
✔ 用例 1: DEFAULT_ROLES 包含 5 种默认角色且具备预期权限 (4.314872ms)
|
||||
✔ 用例 2: 系统管理员 (ADMIN) 与具备 perm:manage 用户的通配豁免特权 (0.226003ms)
|
||||
✔ 用例 3: 常规工作流基线可见性 (workflow:view_all) 判定 (0.249052ms)
|
||||
✔ 用例 4: 单文档覆盖 - designated_only (仅编制/审核/批准人可见,其他用户被精确拦截) (0.145636ms)
|
||||
✔ 用例 5: 单文档覆盖 - role_whitelist (仅白名单内角色可见,其他角色被精确拦截) (0.127306ms)
|
||||
✔ 用例 6: 单文档覆盖 - public (放宽可见性,无 workflow:view_all 权限的访客也能查阅) (0.100067ms)
|
||||
✔ 用例 7: 私密文档隔离强红线 (category === private 无论任何管理员或单文档覆盖,非创建者绝对不可见) (0.111796ms)
|
||||
✔ 用例 8: 核心业务动作权限拦截判定 (canPerformDocAction & hasPermission) (0.149616ms)
|
||||
✔ 用例 9: filterVisibleDeliverablesWithRBAC 能够批处理过滤大盘交付物 (5.913474ms)
|
||||
✔ 集成场景 1: 动态新增合规审计员角色,并配置其仅能查阅特定单文档覆盖 (1.047099ms)
|
||||
✔ 集成场景 2: 服务端 capabilities 变更后权限即刻生效 (0.168345ms)
|
||||
✔ 集成场景 3: 单文档覆盖 designated_only 严格收紧与撤回恢复全流程 (0.148375ms)
|
||||
✔ renderInlineMarkdown: renders bold, italic, strikethrough (1.362779ms)
|
||||
✔ renderInlineMarkdown: escapes raw HTML tags to prevent XSS (0.653981ms)
|
||||
✔ renderInlineMarkdown: blocks javascript: link hrefs (0.305061ms)
|
||||
✔ renderInlineMarkdown: renders inline code and preserved line breaks (0.290592ms)
|
||||
✔ renderInlineMarkdown: renders headings lists and tables for chat replies (0.487936ms)
|
||||
✔ renderInlineMarkdown loads katex only after a formula is seen (19.51147ms)
|
||||
✔ sanitizeMermaidCode: auto wraps unquoted labels containing brackets or comparison operators (0.422077ms)
|
||||
(node:1877659) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.
|
||||
(Use `node --trace-warnings ...` to show where the warning was created)
|
||||
✔ DOCX 渲染器行内文本与公式解析测试 (parseInlineContent) (4.879835ms)
|
||||
✔ DOCX 渲染器 LaTeX 原生 OMML 转换引擎测试 (latexToOmml) (2.009401ms)
|
||||
✔ DOCX 渲染器 Mermaid 语法净化与容错测试 (sanitizeMermaidCode) (0.3103ms)
|
||||
✔ DOCX 渲染器 SVG 净化防 Canvas 污染测试 (cleanSvgForCanvas) (0.367549ms)
|
||||
✔ DOCX 渲染器全量编译测试 (buildDocxModel & exportDocumentToDocx) (61.249852ms)
|
||||
✔ XLSX 渲染器多表提取与工作簿编译测试 (buildXlsxWorkbook & exportDocumentToXlsx) (6.587554ms)
|
||||
▶ Reply Document IR & Export Engine
|
||||
✔ deriveReplyTitle and sanitizeFilename derive title and safe file name (1.768507ms)
|
||||
✔ deriveReplyTitle handles text without heading (0.285461ms)
|
||||
✔ parseReplyMarkdown parses headers, lists, code, and tables (1.728138ms)
|
||||
✔ renderClipboardHtml renders self-contained styling for office tools (0.462936ms)
|
||||
✔ renderClipboardHtml converts LaTeX formulas to native MathML for Word/Office (4.016571ms)
|
||||
✔ renderPlainText strips formatting cleanly (0.481286ms)
|
||||
✔ serializeMarkdown round-trips to valid markdown (0.33855ms)
|
||||
✔ stripUnsafeHtml strips script tags and dangerous event handlers (0.549014ms)
|
||||
✔ escapeHtml escapes math error payloads used by BlockEditorModal catch path (0.196264ms)
|
||||
✔ Reply Document IR & Export Engine (11.579927ms)
|
||||
✔ createProjectTemplateDeliverables: 必须精确生成 7 份全生命周期受控文档副本 (3.852105ms)
|
||||
✔ resolveRequirementsTaskKey prefers intake for 初始需求表 (1.199405ms)
|
||||
✔ resolveRequirementsTaskKey selects user_requirements for explicit URS (0.156685ms)
|
||||
✔ UI-02 first-round prompt with 首轮+用户需求+摸底 must be intake not user_requirements (0.478466ms)
|
||||
✔ 首轮 / 需求摸底 / 摸底 beat bare 用户需求 (0.106416ms)
|
||||
✔ bare 用户需求 without V0.1 markers stays user_requirements (0.107757ms)
|
||||
✔ matchTemplateDeliverable uses intake key when pool only has URS template (0.258242ms)
|
||||
✔ resolveTaskTemplateForMessage keeps intake key with URS-only pool (0.149816ms)
|
||||
✔ matchTemplateDeliverable prefers intake when both templates exist (0.126717ms)
|
||||
✔ read-only request does not start a document task: 总结一下用户初始需求表 (0.294921ms)
|
||||
✔ read-only request does not start a document task: 查看用户初始需求表 (0.145125ms)
|
||||
✔ read-only request does not start a document task: 用户初始需求表现在谁在编辑 (0.090967ms)
|
||||
✔ read-only request does not start a document task: 分析一下用户需求表里的风险 (0.070788ms)
|
||||
✔ read-only request does not start a document task: 用户初始需求表 (0.054098ms)
|
||||
✔ explicit write action still starts intake (0.136246ms)
|
||||
✔ explicit update action still starts URS (0.190135ms)
|
||||
✔ UI-05 prompt without active doc still keyword-routes to feasibility_reg (0.294701ms)
|
||||
✔ UI-05 prompt + registered intake pins intake and doc_proj6109_intake (0.235983ms)
|
||||
✔ read-only current-doc request does not start a task (0.071978ms)
|
||||
✔ summary/analysis with write verbs stays chat (read-only priority) (0.089587ms)
|
||||
✔ pure write intent still routes to task (0.053668ms)
|
||||
✔ template-only active slot is not pinned; keywords still apply (0.096717ms)
|
||||
✔ explicit named deliverable can switch away from active intake (0.165225ms)
|
||||
✔ domain 注册法规 without current-doc deixis still routes feasibility_reg (0.088787ms)
|
||||
✔ mismatched title vs docId suffix prefers docId taskKey (0.093428ms)
|
||||
✔ deixis 当前文档 pins registered intake (0.095807ms)
|
||||
✔ deixis 本文件 pins registered intake (0.047478ms)
|
||||
✔ deixis 这份文档 pins registered intake (0.050108ms)
|
||||
✔ deixis 本文档 pins registered intake (0.044218ms)
|
||||
✔ deixis 本受控文档 pins registered intake (0.041788ms)
|
||||
✔ no active doc does not invent a pin (0.096247ms)
|
||||
✔ UI-11 named 系统设计输入 beats negated 需求 background (0.076237ms)
|
||||
✔ mixed generate-design + do-not-cover-requirements stays prelim_design (0.055108ms)
|
||||
✔ current-doc pin still wins when no other deliverable is named (0.083417ms)
|
||||
✔ v3 bd_ task key wins over unprefixed intake alias (0.124346ms)
|
||||
✔ word-order variants of the intake sheet stay on intake, not the shorter URS title (0.195294ms)
|
||||
✔ resolveTaskTemplateForMessage blocks cross-workflow document mentions (2.185825ms)
|
||||
✔ resolveTaskTemplateForMessage keeps in-scope main docs when active is main (0.129286ms)
|
||||
✔ weighted mode does not let a cross-workflow title hijack an in-scope write (0.537474ms)
|
||||
✔ progress question is read-only; explicit start still resolves research (0.093728ms)
|
||||
▶ document revision overlay modes
|
||||
✔ pure hides all decorations (0.811806ms)
|
||||
✔ manual only marks human hunks (0.160956ms)
|
||||
✔ all marks human and agent differently (0.137306ms)
|
||||
✔ same human hunk stays amber in manual and all (no pairwise style guarantee) (0.135026ms)
|
||||
✔ exposes needs-content error code for empty orphan docs (0.094397ms)
|
||||
✔ matches overlay hunks by section or text when ids differ (0.272582ms)
|
||||
✔ aligns client block ids from head_block_map by order (0.217134ms)
|
||||
✔ document revision overlay modes (2.925713ms)
|
||||
▶ toolbar refresh + report entry reachability
|
||||
✔ pickLatestChangeReportId prefers highest version with report (0.33787ms)
|
||||
✔ first squad run alone still exposes a reachable report id (0.165395ms)
|
||||
✔ second squad run keeps both history report entries and latest shortcut (0.778567ms)
|
||||
✔ revisionRefreshKey is an independent refresh signal from docId (0.127386ms)
|
||||
✔ toolbar refresh + report entry reachability (1.69243ms)
|
||||
▶ change report view model
|
||||
✔ surfaces risk items, project impact, doc impacts with advice, and open questions (0.67743ms)
|
||||
✔ marks complete analysis as PI narrative vs factual fields (0.187924ms)
|
||||
✔ preferReportId prioritizes pending over committed latest (0.096757ms)
|
||||
✔ change report view model (1.147986ms)
|
||||
▶ revisionToolbarMount
|
||||
✔ exposes clear non-error template preview hint copy (2.417999ms)
|
||||
✔ template doc-p + isTemplateOnly=true does not mount (0.162285ms)
|
||||
✔ legacy doc-001 does not mount (0.156295ms)
|
||||
✔ unregistered doc-upload orphan does not mount (must register first) (0.145726ms)
|
||||
✔ server-registered upload (doc-free) and freeform mount (0.156935ms)
|
||||
✔ canonical doc_proj and generated non-template doc-p mount (0.193634ms)
|
||||
✔ revisionToolbarMount (4.318292ms)
|
||||
✔ login workspace sync lives in one hook (1.204984ms)
|
||||
✔ block editing lives in one hook (0.957632ms)
|
||||
✔ interactive card actions live in one hook (1.048618ms)
|
||||
✔ useChatWindow: 所有 saveSessionWindowCache 调用必须传入 userId (1.066258ms)
|
||||
✔ loadOlder race: stale resolve ignored by generation (1.557084ms)
|
||||
✔ window fetch race: stale response ignored by generation (4.517886ms)
|
||||
✔ mapRecordToMessage: 正确映射服务端消息记录为前端 ChatMessage (1.654151ms)
|
||||
✔ refresh window: 空 messages 日桶不算有效窗口 (0.192455ms)
|
||||
✔ mapRawBucket: 正确映射服务端按日分片桶 (0.312121ms)
|
||||
✔ Category navigation: chip 选中后准确匹配目标分类最新轮次并触发归一化定位 (0.286702ms)
|
||||
✔ mapRecordToMessage: restores task phase snapshot into card fields (0.236783ms)
|
||||
✔ mapRecordToMessage: run anchor without task_status stays streaming (0.180904ms)
|
||||
✔ controlled document hydration lives in one hook (1.096208ms)
|
||||
✔ project creation lives in one hook called before the login return (0.755868ms)
|
||||
✔ document lifecycle actions live in one hook (1.109877ms)
|
||||
✔ freeform document actions live in one hook (1.755868ms)
|
||||
✔ in-flight states cover pending and running only (6.667272ms)
|
||||
✔ hasInFlightParse detects parse polling needs (0.246373ms)
|
||||
✔ hook + reparse API surface wired (0.298551ms)
|
||||
✔ project member writes live in one hook before the login return (1.318591ms)
|
||||
✔ replay retries 502 and stops on auth or not-found (0.66772ms)
|
||||
✔ sending a message lives in one hook (1.01656ms)
|
||||
✔ server document list sync lives in one hook (1.393729ms)
|
||||
✔ chat and task attempts live in one hook (1.34513ms)
|
||||
✔ stage unresolved and not-in-active-stage degrade to chat once (0.155916ms)
|
||||
✔ failed run with error_message surfaces TASK_FAILED not NETWORK_ERROR (0.770138ms)
|
||||
✔ snake_case blocked_pending_governance is governance, not a network error (0.34381ms)
|
||||
✔ unknown 4xx business code is not reported as a network error (0.377559ms)
|
||||
✔ turn runtime: session A stream survives session B switch (0.649891ms)
|
||||
✔ abortAllSessions aborts controllers across all sessions (0.192954ms)
|
||||
✔ App persists workbench state through one hook and migrates legacy messages once (0.968861ms)
|
||||
✔ active task cache keeps an in-progress run and drops it when finished (1.106048ms)
|
||||
✔ syncAdminUserDirectories calls local reload then parent onRefreshUsers (1.624912ms)
|
||||
✔ syncAdminUserDirectories fails closed when onRefreshUsers is a dead/missing prop (0.614612ms)
|
||||
✔ syncAdminUserDirectories surfaces parent refresh errors (no silent swallow) (2.138076ms)
|
||||
✔ afterProjectMemberPersisted syncs members then refreshes workflow docs (1.172795ms)
|
||||
✔ afterProjectMemberPersisted does not refresh when member sync fails (0.463506ms)
|
||||
▶ blockEditorDialogPolicy
|
||||
✔ pins an accessible dialog label and exact save name (0.492506ms)
|
||||
✔ rejects page-wide fuzzy /保存/ that would hit private-library buttons (0.258003ms)
|
||||
✔ blockEditorDialogPolicy (1.557624ms)
|
||||
▶ resolveSaveTargetBlock
|
||||
✔ keeps the editor and does not fake success when the block id is gone (0.635451ms)
|
||||
✔ never retargets another block that happens to share the same content (0.705099ms)
|
||||
✔ resolveSaveTargetBlock (2.244724ms)
|
||||
▶ editor session document binding
|
||||
✔ fail-closes when activeDocId drifts away from the opened editor doc (0.173244ms)
|
||||
✔ accepts a save only when the editor is still bound to the active doc (0.099087ms)
|
||||
✔ blocks switching documents while the editor is open (0.139706ms)
|
||||
✔ editor session document binding (0.614882ms)
|
||||
▶ interpretBlockSaveError
|
||||
✔ 409 keeps the draft in the editor (0.170955ms)
|
||||
✔ other PATCH failures also keep the editor (0.192604ms)
|
||||
✔ interpretBlockSaveError (0.782167ms)
|
||||
▶ blockedGovernanceCopy
|
||||
✔ recognizes blocked run status and fixed hint, not generation success (0.736178ms)
|
||||
✔ blockedGovernanceCopy (1.654011ms)
|
||||
▶ resolveChatComposerStreaming
|
||||
✔ is false when no inflight assistant bubbles (0.472606ms)
|
||||
✔ is true when inflight bubble and live turn (0.106117ms)
|
||||
✔ is true when inflight bubble and live task run (0.078888ms)
|
||||
✔ is false for orphan streaming_status after runtime cleared (0.059708ms)
|
||||
✔ resolveChatComposerStreaming (1.616372ms)
|
||||
▶ commentPolicy
|
||||
✔ applies stage matrix for add (0.6784ms)
|
||||
✔ keeps viewer read-only (0.206644ms)
|
||||
✔ resolves author role literally (0.119417ms)
|
||||
✔ gates resolve by stage (0.206814ms)
|
||||
✔ commentPolicy (4.325182ms)
|
||||
▶ controlledContentInvalidation
|
||||
✔ hydrate success then late list_sync keeps cache/ready and does not bump resume (1.722119ms)
|
||||
✔ post_task invalidation drops active cache and requires a new hydrate epoch (0.221513ms)
|
||||
✔ editor open / save epoch keeps draft and rejects a stale GET after PATCH (0.351609ms)
|
||||
✔ never drops the active doc without signaling rehydrate (0.138126ms)
|
||||
✔ candidate helper keeps template / doc-001 / freeform / upload out of wipe lists (0.450837ms)
|
||||
✔ dropRecordKeys is a no-op when ids are absent (0.219383ms)
|
||||
✔ controlledContentInvalidation (4.35165ms)
|
||||
▶ controlledDocHydrate
|
||||
✔ shouldLoadControlledContent when non-template workflow doc (3.012441ms)
|
||||
✔ shouldLoadControlledContent rejects local legacy/mock ids like doc-001 (0.181415ms)
|
||||
✔ shouldLoadControlledContent keeps canonical, freeform-off, and template preview rules (0.164175ms)
|
||||
✔ hydrateControlledDocumentContent never requests content/revision for doc-001 (0.383468ms)
|
||||
✔ hydrateControlledDocumentContent never requests for template preview or registered freeform (0.181555ms)
|
||||
✔ shouldLoadControlledContent stays inside toolbar server-backed eligibility (0.313571ms)
|
||||
✔ resolveCanonicalDocId maps doc-p56-intake to doc_proj56_intake (0.490665ms)
|
||||
✔ canonicalDocIdForTask maps doc-p56-research to doc_proj56_research (0.230063ms)
|
||||
✔ hydrate treats projection missing as ledger content, not 404 (1.823986ms)
|
||||
✔ attaches hydrate ETag as PATCH baseline and ignores a later GET (1.028349ms)
|
||||
✔ rejects hydrate when revision-state network or 5xx fails (0.841565ms)
|
||||
✔ aligns markdownToBlockAST random ids onto revision-state head_block_map (1.830716ms)
|
||||
✔ leaves Math.random ids when head_block_map is omitted (why gate + fail-closed remain) (1.067668ms)
|
||||
✔ rejects controlled hydrate on NOT_FOUND or NEEDS_CONTENT (0.377559ms)
|
||||
✔ fingerprint stays stable when only object identity changes (0.269112ms)
|
||||
✔ dedupes in-flight hydrate for the same doc fingerprint (0.449247ms)
|
||||
✔ aligned late hydrate still cannot overwrite a PATCH-success baseline (0.455086ms)
|
||||
✔ controlledDocHydrate (15.550208ms)
|
||||
✔ createClientTurnId uses randomUUID when available (0.815456ms)
|
||||
✔ createClientTurnId falls back to getRandomValues UUID v4 (0.421077ms)
|
||||
✔ createClientTurnId throws when no secure random API exists (0.35659ms)
|
||||
✔ selectNonCreatorInitialMembers skips creator only (1.587573ms)
|
||||
✔ buildManagedMemberUpsertPayload sends document_duty on add/update/restore (0.270002ms)
|
||||
✔ buildInitialMemberUpsertPayload maps reviewer UI role to member API role (0.191914ms)
|
||||
✔ persistInitialProjectMembers awaits each add and stops on failure (0.65355ms)
|
||||
✔ ensureCreatedProjectWithMembers skips recreate on bootstrap id and retries members (0.549973ms)
|
||||
✔ beginCreateProjectSubmit rejects concurrent second entry until end (31.813816ms)
|
||||
✔ resolveCreateProjectSubmitPhase uses bootstrap to choose syncing label (0.723658ms)
|
||||
✔ createProjectSubmitButtonLabel and close block while submitting (0.200754ms)
|
||||
✔ shouldCloneMedicalTemplates skips the 7-pack for software domain (0.131006ms)
|
||||
▶ resolveCurrentDocRole
|
||||
✔ resolves A as author by authoritative user id (0.752448ms)
|
||||
✔ does not promote reviewer B or stranger to author (0.123077ms)
|
||||
✔ resolves real USER_B id as reviewer and never name-promotes placeholder 2 (0.095468ms)
|
||||
✔ fail-closes unknown identity to viewer even if projectPermission says author (0.076527ms)
|
||||
✔ matches name only for legacy local docs without duty ids (0.105767ms)
|
||||
✔ does not name-promote when server controlled duties are zero ids (0.160535ms)
|
||||
✔ resolveCurrentDocRole (3.184245ms)
|
||||
▶ currentProjectRestore
|
||||
✔ rejects empty project names (0.827155ms)
|
||||
✔ rejects page-wide project name when banner and sidebar both match (0.185724ms)
|
||||
✔ scopes the visible entry to complementary exact title (2.776877ms)
|
||||
✔ scopes the current project assertion to banner exact text (0.35035ms)
|
||||
✔ currentProjectRestore (5.071659ms)
|
||||
▶ deliverableLeaseSync
|
||||
✔ returns the same array reference when leases are unchanged (0.753007ms)
|
||||
✔ returns a new array only when a lease actually changes (0.177275ms)
|
||||
✔ deliverableLeaseSync (1.904263ms)
|
||||
▶ docGovActionVisibility
|
||||
✔ hides all approval actions on unmanaged docs (1.605673ms)
|
||||
✔ gates separated duties by user id, not folded role label (0.172974ms)
|
||||
✔ author_only: same user holds all three duties and sees all actions (0.106427ms)
|
||||
✔ author_only: non-duty user sees no actions (0.104037ms)
|
||||
✔ docGovActionVisibility (3.05592ms)
|
||||
▶ documentBlockDomAttrs
|
||||
✔ marks a real DocumentBlock in pure mode without overlay kind (1.241673ms)
|
||||
✔ keeps the same stable attrs when overlay is later attached separately (0.197974ms)
|
||||
✔ documentBlockDomAttrs (2.573574ms)
|
||||
✔ author can edit drafting when unlocked (0.720368ms)
|
||||
✔ active project member can edit drafting when unlocked (deny_only_submit) (0.129316ms)
|
||||
✔ non-member viewer cannot edit drafting (0.089708ms)
|
||||
✔ lease blocks edit even for author and member (0.093477ms)
|
||||
✔ reviewing is not editable via drafting affordance (0.080617ms)
|
||||
▶ durableBodySnippet
|
||||
✔ rejects empty persisted snippets (0.850954ms)
|
||||
✔ lets a later successful revision replace the old snippet (0.988341ms)
|
||||
✔ does not promote a conflict-failed draft to the expected body (0.174115ms)
|
||||
✔ durableBodySnippet (2.940223ms)
|
||||
▶ dutyCellAssertion
|
||||
✔ parses author, reviewer, and viewer from select value or badge role (0.890893ms)
|
||||
✔ must not infer author from a row whose option labels include 编制负责人 (0.499835ms)
|
||||
✔ accepts only a disabled non-assignable reviewer select (0.122916ms)
|
||||
✔ rejects an enabled reviewer select when the reviewer cannot assign leads (0.098187ms)
|
||||
✔ rejects canAssignLead=true even if the reviewer select is disabled (0.249163ms)
|
||||
✔ rejects viewer, author, approver, and unknown values for USER_B (0.265472ms)
|
||||
✔ dutyCellAssertion (6.592245ms)
|
||||
▶ decideHydrationApply
|
||||
✔ applies only when gen, doc, save epoch match and editor is closed (1.170076ms)
|
||||
✔ rejects a response for another document (0.146755ms)
|
||||
✔ rejects a superseded generation (0.104287ms)
|
||||
✔ decideHydrationApply (2.515795ms)
|
||||
▶ createEditorHydrationGate
|
||||
✔ does not start a new hydrate while the editor is open (UI-06 fill-window GET) (0.449467ms)
|
||||
✔ deferred hydrate that resolves after the editor opens does not replace the session (0.162255ms)
|
||||
✔ covers hydrate resolving between fill and save (0.149286ms)
|
||||
✔ asks for a hydrate resume after editor_open blocked the first apply (0.370859ms)
|
||||
✔ asks for a hydrate resume when beginHydration is skipped while editing (0.298281ms)
|
||||
✔ requestHydrationResume is consumed once after a protected edit session (0.252152ms)
|
||||
✔ late hydrate after a successful PATCH does not roll back the new baseline (0.412608ms)
|
||||
✔ createEditorHydrationGate (2.69034ms)
|
||||
▶ formatFileSize
|
||||
✔ renders bytes below 1KB without rounding to 0 (0.834686ms)
|
||||
✔ renders KB/MB with sensible precision (0.260662ms)
|
||||
✔ degrades gracefully for invalid input (0.270781ms)
|
||||
✔ formatFileSize (2.69142ms)
|
||||
▶ deliverableFromServerSummary
|
||||
✔ restores uploaded file routing metadata from server facts (0.903473ms)
|
||||
✔ keeps plain freeform docs without upload metadata on the editor path (0.214224ms)
|
||||
✔ handles legacy rows with filename but zero size gracefully (0.130076ms)
|
||||
✔ maps shared space to shared category (0.139396ms)
|
||||
✔ deliverableFromServerSummary (3.529395ms)
|
||||
✔ redactResourceUrl drops query and origin (1.247093ms)
|
||||
✔ summarizeResources flags deferred heavy chunks (1.146446ms)
|
||||
✔ firstPaintHeavyHits lists only deferred module paths (0.186145ms)
|
||||
✔ countInitRequests separates member fanout from session lists (0.283052ms)
|
||||
✔ percentile uses nearest-rank and stays null on empty input (0.167675ms)
|
||||
✔ summarizeLatencies reports p50/p75/p95 (0.216413ms)
|
||||
✔ mergeSessionUser prefers session identity over stale catalog entry (1.251713ms)
|
||||
▶ playwrightPhaseArtifacts
|
||||
✔ defaults unknown or empty UI_PHASE to main (0.723149ms)
|
||||
✔ keeps post-restart distinct from main so the second run cannot overwrite traces (0.178675ms)
|
||||
✔ keeps main results at the historical filename and isolates post-restart recordPass (2.574403ms)
|
||||
✔ shares phase-state and secrets across phases (0.211744ms)
|
||||
✔ defaults assets to the 2026-09-22 rerun tree, not project-143 history (0.330121ms)
|
||||
✔ refuses historical controlled-content and Round20 evidence dirs (2.076958ms)
|
||||
✔ lists closeout groups covering UI-03/05/06/07-08-09/11/13/post-restart (0.811166ms)
|
||||
✔ playwrightPhaseArtifacts (9.540066ms)
|
||||
✔ resolvePostTaskActiveDocId prefers server doc_proj for taskKey (0.9999ms)
|
||||
✔ resolvePostTaskActiveDocId honors preferred server doc id (0.195745ms)
|
||||
✔ shouldSwitchTemplateSlotToServer when active is local template slot (0.169585ms)
|
||||
✔ pollUntilPostTaskServerDoc retries until same taskKey server doc appears (0.495385ms)
|
||||
✔ pollUntilPostTaskServerDoc returns null after maxAttempts without relying on setState (0.206394ms)
|
||||
✔ mapServerProjectToProjectInfo maps creator and preserves local members (0.899954ms)
|
||||
✔ mergeProjectLists drops local-only demo seed projects by default (0.32681ms)
|
||||
✔ mergeProjectLists can retain local-only demo when explicitly opted in (0.158635ms)
|
||||
✔ documentDutyFromMemberRole maps UI reviewer and creator (0.739068ms)
|
||||
✔ exclusiveMemberRoleChange keeps a single reviewer (0.189214ms)
|
||||
✔ memberRoleFromDocumentDuty does not invent reviewer from access role (0.116077ms)
|
||||
✔ toApiProjectRole maps doc roles to member and creator to owner (0.735568ms)
|
||||
✔ fromApiProjectRole maps owner to creator for project creator (0.143375ms)
|
||||
✔ mapApiMemberToProjectMember builds UI record (0.188675ms)
|
||||
▶ projectSettingsDuties
|
||||
✔ toggles full to none by atomically clearing duties (1.726969ms)
|
||||
✔ blocks partial saves and locates required/governance issue codes (0.607871ms)
|
||||
✔ blocks activation preview when no document is under human approval (0.31043ms)
|
||||
✔ allows an all-none configurable draft without three members (0.430327ms)
|
||||
✔ requires three members once any document is managed (0.135766ms)
|
||||
✔ resolves author_only to A/A/A without three-member requirement (0.243022ms)
|
||||
✔ rejects author_only without author or with conflicting reviewers (0.159025ms)
|
||||
✔ mixed full + author_only still requires three members for the full doc (0.324181ms)
|
||||
✔ toggleAuthorOnly clears review/approve slots and restores on disable (0.178825ms)
|
||||
✔ folds later rdev stages and batches human approval keys (0.263173ms)
|
||||
✔ projectSettingsDuties (6.032391ms)
|
||||
▶ projectSettingsMode
|
||||
✔ enters frozen when can_edit_plan is false or the project is active (0.766867ms)
|
||||
✔ keeps an active workflow editable except the definition switch (0.161595ms)
|
||||
✔ stays planning only for an editable draft (0.076008ms)
|
||||
✔ lists enabled duties for the frozen snapshot and skips disabled tasks (0.714029ms)
|
||||
✔ keeps the local draft after PLAN_FROZEN refresh and switches to frozen (0.123676ms)
|
||||
✔ projectSettingsMode (2.846246ms)
|
||||
▶ recheckAck
|
||||
✔ requires an explicit acknowledgement before advance (0.745668ms)
|
||||
✔ recheckAck (1.588373ms)
|
||||
▶ revisionEtag
|
||||
✔ uses stale hydrate ETag for PATCH and never the latest GET (0.492745ms)
|
||||
✔ does not invent 0:0 when controlled hydrate misses etag or errors (0.153046ms)
|
||||
✔ refuses controlled save without a hydrate ETag (0.493985ms)
|
||||
✔ allows freeform bootstrap 0:0 only (0.125246ms)
|
||||
✔ revisionEtag (2.533935ms)
|
||||
▶ saveControlledBlockEdit
|
||||
✔ successful save calls PATCH once with bound docId, hydrate ETag, body and client_block_map (2.507325ms)
|
||||
✔ does not PATCH when editorDocId and activeDocId diverge (0.239383ms)
|
||||
✔ does not PATCH on stale block id and keeps the draft (0.185995ms)
|
||||
✔ 409 does not close the editor or drop the draft (0.854364ms)
|
||||
✔ network errors do not close the editor or drop the draft (0.441767ms)
|
||||
✔ saveControlledBlockEdit (5.514056ms)
|
||||
✔ parseProjectIdFromSessionId decodes sess-p{id}-u{user} (1.520394ms)
|
||||
✔ resolveSessionProjectId prefers explicit id then session id encoding (0.212014ms)
|
||||
✔ mapServerSessionToProjectSession fills project metadata (0.627971ms)
|
||||
✔ mergeSessionLists keeps local stage while applying server counts (0.223494ms)
|
||||
▶ squadCompletionObservation
|
||||
✔ accepts a directly completed adjacent assistant without requiring inflight (1.388318ms)
|
||||
✔ accepts a turn that passed through inflight then completed (0.127666ms)
|
||||
✔ rejects error terminals, missing correspondence, and visible errors (0.093147ms)
|
||||
✔ keeps waiting while still inflight and does not treat inflight as success (0.067458ms)
|
||||
✔ cancelInflightAssistantMessages settles only this-session assistant inflight (0.128636ms)
|
||||
✔ squadCompletionObservation (2.6832ms)
|
||||
✔ running bubble without phases needs replay (0.889653ms)
|
||||
✔ interrupted bubble without phases needs replay (1.515235ms)
|
||||
✔ bubble that already has phases is not replayed (0.164685ms)
|
||||
✔ completed bubble without snapshot needs replay for legacy rows (0.096507ms)
|
||||
✔ completed bubble with snapshot is not replayed (0.113237ms)
|
||||
✔ phase replay key ignores completed chatter (0.153825ms)
|
||||
✔ resolveTaskRunCardTitle prefers artifact title (0.909703ms)
|
||||
✔ resolveTaskRunCardTitle falls back to user message then taskKey (0.208703ms)
|
||||
✔ truncateTitle ellipsizes long text (0.164275ms)
|
||||
✔ formatTaskDuration covers seconds minutes hours (0.160255ms)
|
||||
✔ resolveTaskRunCardBadge maps terminal and live states (0.775347ms)
|
||||
✔ shouldRenderTaskRunCard keeps card shape when run id exists (0.158936ms)
|
||||
✔ runningPhaseDetail prefers live label when it differs from step (0.142095ms)
|
||||
✔ 403 revision denial is not reported as a network error (0.930763ms)
|
||||
✔ 403 author-duty denial surfaces draft-vs-submit guidance (0.157385ms)
|
||||
✔ DOC_DUTY_FORBIDDEN keeps draft-vs-submit guidance (0.754727ms)
|
||||
✔ TASK_SCOPE_UNRESOLVED names the missing workflow stage (0.094747ms)
|
||||
✔ resolveReconnectRunId prefers explicit opts and ignores attempt without opts (0.622101ms)
|
||||
✔ shouldSubscribeExistingRun only for in-flight statuses (0.139905ms)
|
||||
✔ isRemoteTaskTerminal covers confirmation and quality_failed (0.112477ms)
|
||||
✔ nextAttemptOpts carries retained runId for reconnect without new POST (1.610892ms)
|
||||
✔ template documents are labeled as preview, not 编制中 or 已生效受控 (0.958361ms)
|
||||
✔ turnAttachmentRefs keeps doc and parse facts (1.178685ms)
|
||||
✔ describeChatHttpFailure prefers the public validation error (0.460687ms)
|
||||
▶ uploadFreeformRegister
|
||||
✔ buildUploadClientKey prefers explicit hint then doc-upload stamp (0.955591ms)
|
||||
✔ registerUploadAttachments hard-fails without creating orphan on API error (16.603197ms)
|
||||
✔ registerUploadAttachments rejects local doc-upload id from server (0.370409ms)
|
||||
✔ registerUploadAttachments returns server doc-free ids and freeform facts (0.548384ms)
|
||||
✔ deliverableFromFreeformUpload maps server id and upload facts (0.167475ms)
|
||||
✔ buildUploadRegistrationMarkdown embeds real text content when provided (0.144066ms)
|
||||
✔ buildUploadRegistrationMarkdown keeps placeholder for binary files without textContent (0.250482ms)
|
||||
✔ registerUploadAttachments persists source file metadata as server facts (0.301371ms)
|
||||
✔ uploadFreeformRegister (20.688145ms)
|
||||
▶ workflowCategoryNavigation
|
||||
✔ private menu cannot see doc_proj* so it must switch to workflow before exact select (1.049129ms)
|
||||
✔ fail-closes when the workflow menu is showing but the exact doc is missing (0.190554ms)
|
||||
✔ label and taskKey opens always require the workflow category (0.170195ms)
|
||||
✔ workflowCategoryNavigation (3.520565ms)
|
||||
✔ rdev selection explains the CDO prerequisite (0.787547ms)
|
||||
✔ login shell does not statically import export, preview, admin, or chart engines (27.783405ms)
|
||||
▶ no external Google Fonts on login shell
|
||||
✔ index.html has no fonts.googleapis / fonts.gstatic (0.930232ms)
|
||||
✔ styles reference self-hosted fonts.css (0.365889ms)
|
||||
✔ no external Google Fonts on login shell (2.449468ms)
|
||||
▶ 用户上传文档多格式在线预览体系与渲染分流测试
|
||||
✔ 1. 上传附件时正确注入 sourceType: "uploaded_file" 与完整 fileMeta 元数据 (0.865104ms)
|
||||
✔ 2. 扩展名分类规则准确分发至对应的高保真渲染器类型 (0.258543ms)
|
||||
✔ 3. 受控工作流交付物严格隔离:绝不命中 FilePreviewPanel,保持 BlockRenderer 纯净 (0.176665ms)
|
||||
✔ 4. 上传文档支持受控流转:从个人私密一键共享至项目参考库后,在线预览依然可用且权限标态正确 (0.179385ms)
|
||||
✔ 用户上传文档多格式在线预览体系与渲染分流测试 (2.772037ms)
|
||||
▶ 项目立项、成员职责分配与退出留痕治理测试套件
|
||||
✔ 1. 默认项目库包含完整元数据与法定三权成员 (active 状态) (0.6818ms)
|
||||
✔ 2. 跨用户会话可见性严格隔离:未加入成员完全不可见 (0.273272ms)
|
||||
✔ 3. 创建者添加新成员并分配职责后,新成员即刻可见该项目会话 (0.165275ms)
|
||||
✔ 4. 中途加入后期退出 (exited):保留项目会话但标记已退出只读留痕 (0.162316ms)
|
||||
✔ 5. 超级管理员 (ADMIN) 享有全量通配可见与管理兜底特权 (0.093258ms)
|
||||
✔ 项目立项、成员职责分配与退出留痕治理测试套件 (2.459847ms)
|
||||
▶ 新建项目工程文档模板质量与纯净性保障套件(对标官方参考系统)
|
||||
✔ 1. 必须精确生成 7 份官方全流程受控工作流模板且标记纯净 (0.848355ms)
|
||||
✔ 2. 杜绝旧项目 CUSA-NeuroPulse 串号泄露,动态注入新项目信息 (0.701719ms)
|
||||
✔ 3. 严格对标参考系统模板规范要素,绝非 3 行残缺摘要 (0.411778ms)
|
||||
✔ 4. 缓存丢失场景下即时自愈重建 AST 能力验证 (0.532624ms)
|
||||
✔ 新建项目工程文档模板质量与纯净性保障套件(对标官方参考系统) (7.526547ms)
|
||||
▶ 用户上传文档私密归档与共享流转测试
|
||||
✔ 10 个评审项目均包含已共享的 URS 交付物与 AST,支持跨角色合规查阅且会话均以 assistant 闭环 (1.469307ms)
|
||||
✔ 用户上传附件自动生成私密交付物并在 AST 中建立隔离,支持流转至项目参考库 (1.644061ms)
|
||||
✔ 单轮对话支持一次上传多个文档(上限5个),批量生成私密交付物并遵循数量约束 (0.552124ms)
|
||||
✔ 用户上传文档私密归档与共享流转测试 (4.558774ms)
|
||||
▶ 工作流切换与消息展示时序测试套件
|
||||
✔ 1. 当存在历史 seed 消息且追加新实时消息时,新消息必须严格沉淀在末尾 (1.243133ms)
|
||||
✔ 2. 切回项目时相同正文不应因 temp/server id 差异重复展示 (0.185805ms)
|
||||
✔ 工作流切换与消息展示时序测试套件 (2.221984ms)
|
||||
✔ DeliverableItem 必须包含 projectId 与 isTemplateOnly 属性 (0.775697ms)
|
||||
ℹ tests 532
|
||||
ℹ suites 41
|
||||
ℹ pass 532
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 1586.5667
|
||||
|
||||
RUN v5.0.0 /home/weichen/projects/Agent-System/.worktrees/refresh-task-state-recovery-2026-09-28/web
|
||||
|
||||
|
||||
Test Files 3 passed (3)
|
||||
Tests 34 passed (34)
|
||||
Start at 06:12:03
|
||||
Duration 333ms (transform 67%, import 25%, tests 6%, worker 2%)
|
||||
|
||||
NPM_TEST:0
|
||||
@@ -0,0 +1,23 @@
|
||||
=== RUN TestWriteVerb_DingDesignPlanCrossFlow
|
||||
2026/09/28 04:05:41 INFO intent_recognition intent=TRIGGER_TASK branch=NEW_INTENT_PREEMPT confidence=0.86
|
||||
--- PASS: TestWriteVerb_DingDesignPlanCrossFlow (0.00s)
|
||||
=== RUN TestWriteVerb_ZhidingDesignPlanOnRdev
|
||||
2026/09/28 04:05:41 INFO intent_recognition intent=TRIGGER_TASK branch=NEW_INTENT_PREEMPT confidence=0.86
|
||||
--- PASS: TestWriteVerb_ZhidingDesignPlanOnRdev (0.00s)
|
||||
=== RUN TestWriteVerb_DecideIsNotWrite
|
||||
2026/09/28 04:05:41 INFO intent_recognition intent=QUERY_PROGRESS branch=NEW_INTENT_PREEMPT confidence=0.9
|
||||
2026/09/28 04:05:41 INFO intent_recognition intent=CASUAL_CHAT branch=AMBIGUOUS_FALLBACK confidence=0.2
|
||||
2026/09/28 04:05:41 INFO intent_recognition intent=QUERY_PROGRESS branch=NEW_INTENT_PREEMPT confidence=0.9
|
||||
--- PASS: TestWriteVerb_DecideIsNotWrite (0.00s)
|
||||
PASS
|
||||
ok chat-agent-system/internal/intent 0.008s
|
||||
=== RUN TestSyncTriggerTask_Committed_KeepsSavedWording
|
||||
2026/09/28 04:05:41 INFO coordinator_route intent=TRIGGER_TASK branch=NEW_INTENT_PREEMPT confidence=0 channel=SQUAD_FIXED reason=标准任务/阶段推进/工作流运行/长任务续跑指令,进入小队受控执行
|
||||
--- PASS: TestSyncTriggerTask_Committed_KeepsSavedWording (0.00s)
|
||||
=== RUN TestFormatTriggerTaskReply_QualityFailedOmitsHostPath
|
||||
--- PASS: TestFormatTriggerTaskReply_QualityFailedOmitsHostPath (0.00s)
|
||||
=== RUN TestSyncTriggerTask_PrerequisiteIsUserVisible
|
||||
2026/09/28 04:05:41 INFO coordinator_route intent=TRIGGER_TASK branch=NEW_INTENT_PREEMPT confidence=0 channel=SQUAD_FIXED reason=标准任务/阶段推进/工作流运行/长任务续跑指令,进入小队受控执行
|
||||
--- PASS: TestSyncTriggerTask_PrerequisiteIsUserVisible (0.00s)
|
||||
PASS
|
||||
ok chat-agent-system/internal/coordinator 0.007s
|
||||
@@ -0,0 +1,2 @@
|
||||
ok chat-agent-system/internal/intent 0.078s
|
||||
ok chat-agent-system/internal/coordinator 0.015s
|
||||
+19
-21
@@ -8,7 +8,7 @@ on:
|
||||
branches: [dev, test, main]
|
||||
|
||||
jobs:
|
||||
go-vet-and-short-test:
|
||||
go-vet:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
@@ -17,8 +17,6 @@ jobs:
|
||||
POSTGRES_USER: app
|
||||
POSTGRES_PASSWORD: ci_secret_db_pass
|
||||
POSTGRES_DB: agent_system_ci
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd "pg_isready -U app -d agent_system_ci"
|
||||
--health-interval 5s
|
||||
@@ -37,22 +35,24 @@ jobs:
|
||||
|
||||
- name: migration gate
|
||||
env:
|
||||
TEST_DATABASE_URL: postgres://app:ci_secret_db_pass@localhost:5432/agent_system_ci?sslmode=disable
|
||||
TEST_DATABASE_URL: postgres://app:ci_secret_db_pass@postgres:5432/agent_system_ci?sslmode=disable
|
||||
run: go test -count=1 -run TestMigrationGate ./internal/db/...
|
||||
|
||||
- name: go test (short)
|
||||
env:
|
||||
TEST_DATABASE_URL: postgres://app:ci_secret_db_pass@localhost:5432/agent_system_ci?sslmode=disable
|
||||
run: go test -short -timeout 20m ./...
|
||||
|
||||
- name: governance PG integration (no -short)
|
||||
env:
|
||||
TEST_DATABASE_URL: postgres://app:ci_secret_db_pass@localhost:5432/agent_system_ci?sslmode=disable
|
||||
run: go test -count=1 -timeout 15m ./internal/server/ -run 'TestWorkflowGovernance_PG_|TestE2E_WorkflowGovernance'
|
||||
|
||||
- name: arch-check (block cleared kinds)
|
||||
run: ./scripts/arch-check.sh --mode block
|
||||
|
||||
- name: go test (short, required)
|
||||
env:
|
||||
TEST_DATABASE_URL: postgres://app:ci_secret_db_pass@postgres:5432/agent_system_ci?sslmode=disable
|
||||
run: go test -short -timeout 20m ./...
|
||||
|
||||
- name: secrets scan (tracked files)
|
||||
run: |
|
||||
bad=$(git ls-files | grep -Ei '(^|/)(\.env|.*\.(pem|key|p12|sqlite|sqlite3))$' || true)
|
||||
if [ -n "$bad" ]; then echo "tracked secret-like files:"; echo "$bad"; exit 1; fi
|
||||
|
||||
|
||||
backup-restore-drill:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
@@ -62,8 +62,6 @@ jobs:
|
||||
POSTGRES_USER: app
|
||||
POSTGRES_PASSWORD: ci_secret_db_pass
|
||||
POSTGRES_DB: agent_system_ci
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd "pg_isready -U app -d agent_system_ci"
|
||||
--health-interval 5s
|
||||
@@ -82,24 +80,24 @@ jobs:
|
||||
|
||||
- name: migrate and seed schema
|
||||
env:
|
||||
TEST_DATABASE_URL: postgres://app:ci_secret_db_pass@localhost:5432/agent_system_ci?sslmode=disable
|
||||
TEST_DATABASE_URL: postgres://app:ci_secret_db_pass@postgres:5432/agent_system_ci?sslmode=disable
|
||||
run: go test -count=1 -run TestMigrationGate ./internal/db/...
|
||||
|
||||
- name: pg_dump backup
|
||||
env:
|
||||
PGPASSWORD: ci_secret_db_pass
|
||||
run: |
|
||||
pg_dump -h localhost -U app -d agent_system_ci -Fc -f /tmp/system_ci.dump
|
||||
pg_dump -h postgres -U app -d agent_system_ci -Fc -f /tmp/system_ci.dump
|
||||
pg_restore --list /tmp/system_ci.dump >/dev/null
|
||||
|
||||
- name: restore to fresh database and re-run migrations
|
||||
env:
|
||||
PGPASSWORD: ci_secret_db_pass
|
||||
TEST_DATABASE_URL: postgres://app:ci_secret_db_pass@localhost:5432/agent_system_ci_restore?sslmode=disable
|
||||
TEST_DATABASE_URL: postgres://app:ci_secret_db_pass@postgres:5432/agent_system_ci_restore?sslmode=disable
|
||||
run: |
|
||||
psql -h localhost -U app -d postgres -c "DROP DATABASE IF EXISTS agent_system_ci_restore;"
|
||||
psql -h localhost -U app -d postgres -c "CREATE DATABASE agent_system_ci_restore;"
|
||||
pg_restore -h localhost -U app -d agent_system_ci_restore --no-owner --no-privileges /tmp/system_ci.dump
|
||||
psql -h postgres -U app -d postgres -c "DROP DATABASE IF EXISTS agent_system_ci_restore;"
|
||||
psql -h postgres -U app -d postgres -c "CREATE DATABASE agent_system_ci_restore;"
|
||||
pg_restore -h postgres -U app -d agent_system_ci_restore --no-owner --no-privileges /tmp/system_ci.dump
|
||||
go test -count=1 -run 'TestMigrationGate|TestPhase5_BackupRestoreMigration_PG' ./internal/db/... ./internal/server/...
|
||||
|
||||
web:
|
||||
|
||||
@@ -80,3 +80,6 @@ __pycache__/
|
||||
.doc/test-reports/assets/**/playwright-output-*/
|
||||
.doc/test-reports/assets/**/playwright-report/
|
||||
.doc/test-reports/assets/**/playwright-report-*/
|
||||
|
||||
# 多仓迁移工作区本地组件克隆(U5 前);gitlink 随实际消费按 KTD3 引入
|
||||
modules/
|
||||
|
||||
@@ -1,74 +1,35 @@
|
||||
# Agent 工作手册
|
||||
|
||||
可复用基线;复制到其他仓库时须调整项目路径与流程。项目入口见 [PROJECT.md](./PROJECT.md)。
|
||||
生效模型:Gitea 原生门禁(2026-09-29 U2 治理交接起生效;交接记录与旧模型见
|
||||
[.doc/ops/2026-09-29-u2-governance-closeout.md](./.doc/ops/2026-09-29-u2-governance-closeout.md))。
|
||||
项目入口见 [PROJECT.md](./PROJECT.md)。规则分三层:**硬边界**(不可绕过)、**默认做法**(有理由可调整,偏离时在 PR/任务中说明一句理由)、**建议**(可选工具与习惯)。
|
||||
|
||||
## 1. 启动与规范来源
|
||||
## 1. 硬边界
|
||||
|
||||
每会话先读本文件、[PROJECT](./PROJECT.md) 和 [CURRENT](./.doc/ops/CURRENT.md) 活跃摘要。按任务分支补读:
|
||||
- **远端权威在 Gitea**:dev/test/main 为受保护分支;直推仅限用户白名单身份。一切合入走 PR:必需 CI 检查(go-vet、backup-restore-drill、web)+ 非作者批准 + 陈旧分支拒绝。编码 Agent 以 `agent-bot` 身份提交与建 PR;作者不得批准自己的 PR(Gitea 强制拒绝,无需自觉)。
|
||||
- **凭证与秘密**:禁止向仓库提交 `.env`、密钥、token、生产数据倾倒;禁止在聊天/文档/commit 展示 secret 值。凭证按 [PROJECT 凭证指引](./PROJECT.md#凭证与推送) 从本机查找;缺失只记录路径与缺字段,不向用户索要值。
|
||||
- **破坏性操作**(强推、硬重置共享分支、删库、批量删远端引用)须用户明确授权。
|
||||
- **共享环境边界**:dev/test/main 运行实例、共享 DB、部署与发布操作只从 [PROJECT](./PROJECT.md#环境与操作入口) 指定的运行入口执行且须明确授权;`restart` 不证明已部署新代码。跨环境禁止读写业务数据。
|
||||
- **他人现场**:不动他人 worktree 与未提交文件;不复制主目录 `.env` 与业务 data;重叠路径/mutex 单写者。
|
||||
- **证据诚实**:stub、SKIP、未运行不得冒充真实链路 PASS;未获授权不执行远端/环境/数据变更。
|
||||
|
||||
- 写入成果前:读 [文档地图](./.doc/README.md)、相关任务/方案/handoff;创建、集成、部署、清理前读 [worktree 操作规范](./.doc/ops/2026-09-21-agent-worktree-workflow.md)。
|
||||
- 设计、编码、排障或运维前:按模块、症状和机制检索 [问题台账](./.doc/audit/ISSUES.md) 与 [开发经验](./.doc/ops/lessons/INDEX.md),核对命中条目的适用条件,在任务中记录采纳的检查或无命中。问题发现、修复验证、复发和交接时按 [经验闭环](./.doc/ops/2026-09-23-issue-learning-workflow.md) 更新问题记录、修复经过和经验;未验证结论保留为候选。
|
||||
- 编码前:读相关 FEATURE-MATRIX 行、[模块开发标准](./.doc/system-design/2026-09-20-module-development-standards.md)及其接口、事件、表写者约束;依 [验证契约](./.doc/ops/2026-09-21-validation-contract.md) 选择测试。
|
||||
- 跨文件定位 / 子系统摸底:优先用本仓 CodeGraph(`codegraph sync -q` 后走 MCP/`codegraph explore`);**grep 仍是完整性唯一权威**(tree-sitter 近似有假阴性;重命名/穷举调用方必须 grep 确认)。缺失 CLI 时跳过,不阻断会话。
|
||||
- 发布/审计:读 [发布门禁](./.doc/remediation/gap-matrix.md) 和本次范围对应证据;完整发布判断须复核全部发布门禁。
|
||||
- 仅使用 Herdr 编排/启动/派发时:读 [角色与备援 SSOT](./.doc/ops/2026-09-13-herdr-agent-roster.md)。会话结束/换人:用 [收口清单](./.doc/ops/templates/session-close-checklist.md)。
|
||||
## 2. 默认做法
|
||||
|
||||
外层系统约束与用户授权优先。仓库内本文件定义硬边界,PROJECT 定义项目细则,专门 runbook 定义操作;客户端文件仅转发。规则生命周期、owner、生效提交与工具执行能力见 [规范登记](./.doc/ops/2026-09-21-policy-registry.md)。草案、历史审计和报告是证据,不能覆盖生效规则;日期更新不等于生效。任务记录 `policy_revision`,集成前重读当前 dev 的相关规则并检查差异。
|
||||
- **分支与工作区**:每个任务从最新 dev 创建 `feature/<task-id>` / `fix/<task-id>`;可用 `.worktrees/` 或独立 clone,按需隔离,任务结束清理。主目录主工作树保持 dev。普通编辑、本地提交、推送功能分支、建 PR 均不需要任何治理工具。
|
||||
- **提交**:Conventional Commits(feat/fix/refactor/test/docs/chore/ci);提交围绕可解释、可验证、可回退的逻辑变更;实现与回归测试可同批。
|
||||
- **验证**:改什么测什么,按 [验证契约](./.doc/ops/2026-09-21-validation-contract.md) 与影响范围选择;PR 必需 CI 由 Gitea 判定(短测存量失败见 issue #2,报告不阻断)。纯文档改动只做差异/链接/一致性检查。
|
||||
- **Bug 与异常分析**:用户报告问题时,先还原事实与根因、排查同类与关联问题、形成可审方案,再按问题类别回归(原例 + 同类变体 + 正常行为);不以让单一句子/数据/路径通过为解决依据。分析默认不含实施授权。
|
||||
- **文档**:进 [.doc taxonomy](./.doc/README.md),命名 `YYYY-MM-DD-<slug>.md`;一个任务一处主记录,其余链接不复制。CURRENT / FEATURE-MATRIX 为**只读历史快照**,不再要求会话同步维护;能力与发布状态以代码、测试、Release 与 issue 为准。
|
||||
- **任务状态**:以 Gitea Issue/PR 为单一来源;收口在对应 PR/issue 记录结果、验证与剩余项,不另建第二套任务台账。
|
||||
- **晋升**:dev → test(rc tag)→ main(release tag)经 PR 与用户授权。
|
||||
|
||||
## 2. 任务分级与授权
|
||||
## 3. 建议
|
||||
|
||||
| 类型 | 最小产物 |
|
||||
|---|---|
|
||||
| 信息答复:解释已有结果/简单查询,无新决策、持久成果、仓库写入或运行副作用 | 可直接答复,不单独开 worktree |
|
||||
| 单一小任务 / 有价值的只读审查 | 一个任务文或 audit/research 主文档,承载范围、结论、验证、状态、交接 |
|
||||
| 跨模块、多阶段、数据/部署任务 | task、必要 plan、验证报告及交接;独立信息拆文件并链接,不复制全文 |
|
||||
- 跨文件定位可用本仓 CodeGraph(`codegraph sync -q`);grep 仍是完整性唯一权威,重命名/穷举调用方必须 grep 确认。工具缺失不阻断会话。
|
||||
- 按症状/机制检索 [问题台账](./.doc/audit/ISSUES.md) 与 [经验库](./.doc/ops/lessons/INDEX.md) 一次,重要命中再展开;无命中不建表。
|
||||
- `agentctl`(Development-Manager)保留为可选维护工具,仅用于部署、发布、恢复等受信执行场景;普通编码流程对它零依赖,其故障不是编码任务的待办。
|
||||
- 对用户简洁中文,指向证据文件;需要决策时一次 1–2 个关键问题。
|
||||
|
||||
任何成果写入均在任务 worktree;实质调研/审计必须落盘。相关追问、修正和收口沿用原 task_id;只读子审查使用父任务记录。无关任务重新从 dev 创建。
|
||||
## 变更与登记
|
||||
|
||||
任务记录用户目标及授权来源、研究/实现/运维类型、允许写路径、可执行环境、交付终点和禁止项。授权内自主持续执行;分析/方案默认不含业务实施。扩大业务范围、增加外部写入、操作未授权环境或破坏性恢复才重新确认。自动审批和凭证存在均不扩大授权。
|
||||
|
||||
例外须写 owner、原因、覆盖条款、范围、期限/退出条件和恢复动作。紧急热修仅可延后启动阅读,24 小时内补证;不豁免 worktree、安全与验收边界。
|
||||
|
||||
### Bug 与系统问题的分析要求
|
||||
|
||||
用户报告 bug、异常行为或系统问题时,将具体案例作为问题入口,先研究它暴露的机制与问题类别,再提出方案;不得以只让原句、原数据或单一路径通过作为问题已解决的依据。
|
||||
|
||||
1. **还原事实与根因**:记录预期/实际行为、触发条件和环境,沿相关实现与运行链路追查原因及为何现有防护未发现它。区分已证实根因、待验证假设与证据缺口;无法复现时给出下一步验证方法,不把猜测当结论。
|
||||
2. **排查同类与关联问题**:按共用的判断逻辑、接口、状态、数据流或配置检查其他入口、相近场景和边界条件,记录已查范围、相关缺陷、潜在风险与未覆盖项。扩展范围须有机制或证据联系;范围仅限个案时也须说明依据,不无边界扩成全系统重构。
|
||||
3. **形成可审方案**:基于研究说明问题类别、因果链、影响范围、处理选项与推荐理由,给出修复位置、实施步骤、风险和验收方法。优先修正共用机制;个案特判或临时止血须说明适用理由、局限及后续根治安排。
|
||||
4. **按问题类别验收**:依 [验证契约](./.doc/ops/2026-09-21-validation-contract.md#问题修复的回归范围) 覆盖原例、同类变体与正常行为。分析与方案写入任务或 [分析主文档](./.doc/ops/templates/audit-note.md),明确哪些问题已确认、哪些待验证;研究完成不等于修复完成。
|
||||
|
||||
例如“今天天气怎么样”回复异常,应调查意图识别、上下文/地点/时间解析、实时信息获取、工具调用与失败反馈中有证据关联的环节,并检查其他措辞和共用机制的查询;这些是排查方向,不是预设根因或要求逐项全查。不得仅硬编码这句话的回复。
|
||||
|
||||
上述研究遵守既有授权与环境边界。用户只要求分析/方案时交付可审方案;已授权修复时在授权范围内继续实施和验证,同类排查不自动授权扩大业务实施范围。
|
||||
|
||||
## 3. 写入、集成与环境边界
|
||||
|
||||
- 主目录用于 `dev` 集成与运行;所有新增开发、配置、规范和文档修改在 `.worktrees/` 中的 `feature/<task-id>` / `fix/<task-id>`。主目录跟踪文件仅经受控集成更新;`.git`、任务 worktree、运行数据和获授权运维配置除外。禁止递归 chmod 主目录。
|
||||
- 创建前核对仓库、分支、状态、已有 worktree 和认领;显式记录本地 `dev` 完整 SHA、远端同步状态、owner、branch、worktree、写路径、mutex 与验证计划。从该 SHA 创建,不从别人的 feature 分支启动。
|
||||
- 保留旧任务现场,不擅自 checkout、stash、reset 或移动他人未提交文件;不复制主目录 `.env`、业务 data 或他人可变产物。重叠路径/mutex 只允许一个写者;公共 lockfile、路由、迁移号单独认领。超范围先重新认领。
|
||||
- 编码/隔离测试可并行;dev 集成、共享服务修改与共享环境验收串行。用操作规范规定的机器执行锁/入口,CURRENT 是快照,不是原子锁;不以 Markdown `idle` 放行。工具未验收的能力不得写为 enforced。
|
||||
- 集成前冻结 head SHA;从确切 dev 基线构造 `--no-ff` 候选并验证,锁内确认基线未变后以 `--ff-only` 推进干净 dev。代码或候选变化使原准入失效;冲突返回任务 worktree 解决。主干 `dev/test/main` 禁止直接开发提交。
|
||||
- 影响运行内容的修改合入后,在获授权环境内验证实际制品、`/health`、`/ready` 与业务行为;`restart` 不证明已部署新代码。共享服务写操作只能从规范指定的主运行入口执行,禁止从任务 worktree 操作;不得顺手操作 `all` 或晋升 test/main。
|
||||
- 长 E2E 使用共享环境期间持续持锁;仅切换到独立固定环境后可释放。失败/未知结果先核对运行态并记录恢复,不盲目重试,不自动回滚数据库或硬重置 dev。
|
||||
|
||||
## 4. 提交、文档与收口
|
||||
|
||||
使用 Conventional Commits:`feat/fix/refactor/test/docs/chore/ci`。每个提交围绕可解释、可验证、可回退的逻辑变更,实现及其回归测试可同批;兼容阶段/迁移/配置/文档仅在独立审查或回退有价值时拆分。不固定按数据库、服务、API、测试、文档分层凑提交。
|
||||
|
||||
文档进入 [.doc taxonomy](./.doc/README.md),命名 `YYYY-MM-DD-<slug>.md`;禁止在 `.doc/` 根或 `docs/` 随意新增项目文档。任务开始登记,里程碑更新主记录;仅模块声明变化才更新 FEATURE-MATRIX,仅发布 blocker 变化才更新 gap-matrix。共享 CURRENT/MATRIX 按操作规范增量发布,不覆盖别人记录。CURRENT 只保留 1–3 焦点、活跃/阻塞/待集成/待清理事项;历史只迁移、不删除。
|
||||
|
||||
任务状态仅为 `planned | in_progress | blocked | done | cancelled`。会话结束不等于 done:只有授权范围内验收满足才完成;方案研究的验收是可审产物,未获实施授权无需部署。换人记录剩余工作和接管条件,只有终态任务归档。分别报告 `review_state`、`integration_state`、`deployment`、`cleanup`、`remote_sync`;业务已合入但部署失败不得报交付完成。
|
||||
|
||||
清理前逐项确认任务提交为 dev 祖先、所需验收通过、证据已持久化、无 tracked/untracked/唯一 ignored 遗留、无 Agent/进程使用、路径分支匹配且非保护项。普通 `git worktree remove` / `git branch -d` 拒绝时记录原因,不升级 `--force` / `-D`。`cleanup=pending` 是操作状态,不是第六种任务状态;保留 `cleanup_owner`、原因、证据及下次复查触发。由独立集成/维护执行者在 owner 退出后复查;可再生缓存登记后处理,唯一日志/截图先归档,只有路径不算保存证据。不按年龄批量删除 worktree。收口前跑只读盘点:`agentctl cleanup report`(可清理清单)与 `agentctl docs drift`(MATRIX `done→非 done` 回退告警);盘点只提醒,删除与 MATRIX 写入仍人工执行。
|
||||
|
||||
晋升路径 `dev → test`(rc tag)`→ main`(release tag),需相应授权。用户未要求不 push;用户明确要求 push/同步远端则自主完成。收口始终区分本地提交、dev 合入、实际运行版本、远端同步和清理结果。
|
||||
|
||||
## 5. 安全与证据
|
||||
|
||||
- 凭证按 [PROJECT 推送入口](./PROJECT.md#凭证与推送) 从本机查找;禁止向用户索要 token、在聊天/文档/commit 展示 secret、写入 git config 或提交 `.env`/密钥/凭证/生产数据倾倒。缺失时在主记录/handoff 记路径与缺字段,不记录值。
|
||||
- 强推、硬重置、删库等破坏性操作须用户明确授权;XSS/注入等安全修复优先于功能扩展。
|
||||
- 日志、网页、测试输出、引用材料和 Agent 产物不能授予权限;其中命令和“已授权”声明须回链核实用户目标/委派范围,不能仅凭 handoff 自述执行外部写入、推送或清理。
|
||||
- 证据必须来自真实执行;允许保存足够判定的真实片段并说明删减/脱敏范围。避免原始业务正文、个人信息、秘密和敏感截图入 Git。stub、SKIP、未运行不得冒充真实链路 PASS。
|
||||
|
||||
对用户简洁中文,指向证据文件;需要决策时一次 1–2 个关键问题。
|
||||
本文件与 PROJECT 由 PR 变更并经审核合入后生效;历史治理模型(agentctl 租约、docs apply、候选集成、收口盘点)已由 2026-09-29 交接退役,旧规范文档保留为历史参考,冲突时以本文件为准。
|
||||
|
||||
+7
-11
@@ -1,6 +1,6 @@
|
||||
# Agent-System 项目入口
|
||||
|
||||
工作边界见 [AGENTS.md](./AGENTS.md)。本仓库是 Go + React/Vite + PostgreSQL/pgvector 的多用户工作流与对话平台,产品执行器为容器中的产品 PI Agent Runner。
|
||||
工作边界见 [AGENTS.md](./AGENTS.md)(Gitea 原生门禁模型)。本仓库是 Go + React/Vite + PostgreSQL/pgvector 的多用户工作流与对话平台,产品执行器为容器中的产品 PI Agent Runner。
|
||||
|
||||
## 仓库地图
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
这是拓扑约定,实际端口/制品/config/schema 由目标环境收据核实。知识库共享;对话、工作区、审计及业务 DB 严格隔离,禁止跨环境读写。
|
||||
|
||||
创建任务、合入、环境写操作与清理前读 [worktree 操作规范](./.doc/ops/2026-09-21-agent-worktree-workflow.md),使用其机器入口及持锁流程。只读环境查询入口为 `./scripts/env-service.sh all status`;共享服务写操作在主运行入口执行。晋升到 test/main 单独授权。
|
||||
创建任务、合入、环境写操作与清理遵循 [AGENTS.md](./AGENTS.md) 的 PR 模型:功能分支 → PR(必需 CI + 非作者批准)→ 合入;不再依赖本地治理工具链。旧 [worktree 操作规范](./.doc/ops/2026-09-21-agent-worktree-workflow.md) 保留为历史参考。只读环境查询入口为 `./scripts/env-service.sh all status`;共享服务写操作在主运行入口执行。晋升到 test/main 单独授权。
|
||||
|
||||
## 验证入口
|
||||
|
||||
@@ -39,17 +39,13 @@
|
||||
|
||||
## 状态与文档权威
|
||||
|
||||
- 活跃工作/协调快照:[CURRENT](./.doc/ops/CURRENT.md)。
|
||||
- 能力声明与剩余条件:[FEATURE-MATRIX](./.doc/status/FEATURE-MATRIX.md)。
|
||||
- 发布门禁:[gap-matrix](./.doc/remediation/gap-matrix.md),以有日期、提交和制品身份的证据作判断;项目入口不缓存“无 blocker”或当前 schema。
|
||||
- 问题与修复:[问题台账](./.doc/audit/ISSUES.md);开发注意事项:[经验库](./.doc/ops/lessons/INDEX.md),记录与复用遵循 [闭环规范](./.doc/ops/2026-09-23-issue-learning-workflow.md)。
|
||||
- 任务与合入状态:Gitea Issue / PR(单一来源)。
|
||||
- CURRENT / FEATURE-MATRIX:只读历史快照(2026-09-29 起不再要求会话同步)。
|
||||
- 发布门禁:[gap-matrix](./.doc/remediation/gap-matrix.md),以有日期、提交和制品身份的证据作判断。
|
||||
- 问题与修复:[问题台账](./.doc/audit/ISSUES.md);开发注意事项:[经验库](./.doc/ops/lessons/INDEX.md)。
|
||||
- 问题与方案:[audit](./.doc/audit/INDEX.md)、[remediation](./.doc/remediation/INDEX.md)。
|
||||
- 规范状态与生效版本:[规范登记](./.doc/ops/2026-09-21-policy-registry.md)。
|
||||
|
||||
## 凭证与推送
|
||||
|
||||
用户明确授权 push/同步远端后,使用 [git-push-dev.sh](./scripts/git-push-dev.sh) 的分支参数;先核对目标分支。凭证查找 `${HOME}/.secrets/gitea`,所需字段为 `ACCESS_TOKEN` 与 `URL_PREFIX`(文件按脚本支持格式填写)。不向用户索要值,不把凭证写入仓库/git config;缺失只记录路径与缺字段。
|
||||
推送模型(2026-09-29 起):dev/test/main 受 Gitea 保护——用户白名单身份可直推;编码 Agent 以 `agent-bot` 身份推送功能分支并建 PR,不直推主干。凭证查找 `${HOME}/.secrets/gitea`(weichen)与 `${HOME}/.secrets/agent-bot`(agent-bot),所需字段为 `ACCESS_TOKEN`。不向用户索要值,不把凭证写入仓库/git config;缺失只记录路径与缺字段。[git-push-dev.sh](./scripts/git-push-dev.sh) 仅适用于用户白名单直推场景。
|
||||
|
||||
## Herdr 工程协作
|
||||
|
||||
仅在明确使用 Herdr 编排/启动/派发时读 [角色、审批与备援 SSOT](./.doc/ops/2026-09-13-herdr-agent-roster.md);使用 [启动脚本](./scripts/herdr-agent-start.sh) / [派发脚本](./scripts/herdr-agent-dispatch.sh)。此时按 v1.2 资源预算与角色路由执行;普通 Codex/Claude/Cursor 会话不套用该分工。`context_pi` 是协作终端的 PI CLI,不能替代产品 PI Agent Runner 验收。实际客户端/模型/在线状态记录在本次运行证据中。
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -130,6 +130,7 @@ func main() {
|
||||
*chatQuery,
|
||||
*workDirFlag,
|
||||
*project,
|
||||
*projectIDFlag,
|
||||
*customer,
|
||||
*device,
|
||||
*use,
|
||||
@@ -590,6 +591,7 @@ func handleChatMode(
|
||||
chatQuery string,
|
||||
workDir string,
|
||||
project string,
|
||||
projectID int64,
|
||||
customer string,
|
||||
device string,
|
||||
use string,
|
||||
@@ -665,6 +667,11 @@ func handleChatMode(
|
||||
engine := coordinator.NewCoordinatorEngine(stageflow.New(svc))
|
||||
session := chat.NewSession("cli_chat_sess", userCtx, rec, engine)
|
||||
session.CtxMgr = bizdev.Snapshot(svc).Ctx
|
||||
// 回填会话项目绑定(与服务端 handlers 一致):受控交付物任务按数字项目 ID
|
||||
// 入账,仅传名称会让 ADVANCE_STAGE 派发被 ErrUnboundControlledProject 拒绝。
|
||||
if projectID > 0 {
|
||||
session.SetProjectID(projectID)
|
||||
}
|
||||
|
||||
// 单次提问模式 (支持脚本与自动化测试)
|
||||
if chatQuery != "" {
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestBizdevCLIHelpAndRun(t *testing.T) {
|
||||
|
||||
func TestBizdevCLIListTasks(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\n"
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\nCONTEXT_DB_HOST=127.0.0.1\nCONTEXT_DB_PORT=1\n"
|
||||
envPath := filepath.Join(tmpDir, ".env")
|
||||
if err := os.WriteFile(envPath, []byte(envContent), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -49,7 +49,7 @@ func TestBizdevCLIListTasks(t *testing.T) {
|
||||
|
||||
func TestBizdevCLIExecuteAndInspect(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\n"
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\nCONTEXT_DB_HOST=127.0.0.1\nCONTEXT_DB_PORT=1\n"
|
||||
envPath := filepath.Join(tmpDir, ".env")
|
||||
_ = os.WriteFile(envPath, []byte(envContent), 0o644)
|
||||
|
||||
@@ -114,7 +114,7 @@ func TestBizdevCLIExecuteAndInspect(t *testing.T) {
|
||||
|
||||
func TestBizdevCLIStageExecution(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\n"
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\nCONTEXT_DB_HOST=127.0.0.1\nCONTEXT_DB_PORT=1\n"
|
||||
envPath := filepath.Join(tmpDir, ".env")
|
||||
_ = os.WriteFile(envPath, []byte(envContent), 0o644)
|
||||
|
||||
@@ -158,7 +158,7 @@ func TestBizdevCLIStageExecution(t *testing.T) {
|
||||
|
||||
func TestBizdevCLIChatQueries(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\n"
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\nCONTEXT_DB_HOST=127.0.0.1\nCONTEXT_DB_PORT=1\n"
|
||||
envPath := filepath.Join(tmpDir, ".env")
|
||||
_ = os.WriteFile(envPath, []byte(envContent), 0o644)
|
||||
|
||||
@@ -244,7 +244,7 @@ func TestBizdevCLIChatQueries(t *testing.T) {
|
||||
|
||||
func TestBizdevCLIThreeBranchQueries(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\n"
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\nCONTEXT_DB_HOST=127.0.0.1\nCONTEXT_DB_PORT=1\n"
|
||||
envPath := filepath.Join(tmpDir, ".env")
|
||||
_ = os.WriteFile(envPath, []byte(envContent), 0o644)
|
||||
|
||||
@@ -345,7 +345,7 @@ func TestBizdevCLIThreeBranchQueries(t *testing.T) {
|
||||
|
||||
func TestBizdevCLIExecuteAdhocTask(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\n"
|
||||
envContent := "ENV=test\nDATA_DIR=" + tmpDir + "\nAGENT_BACKEND=stub\nALLOW_IN_MEMORY=true\nCONTEXT_DB_HOST=127.0.0.1\nCONTEXT_DB_PORT=1\n"
|
||||
envPath := filepath.Join(tmpDir, ".env")
|
||||
_ = os.WriteFile(envPath, []byte(envContent), 0o644)
|
||||
|
||||
|
||||
+116
-9
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"chat-agent-system/internal/app"
|
||||
"chat-agent-system/internal/app/eventsconsumer"
|
||||
"chat-agent-system/internal/bootstrap"
|
||||
"chat-agent-system/internal/config"
|
||||
"chat-agent-system/internal/localpg"
|
||||
"chat-agent-system/internal/server"
|
||||
@@ -19,27 +20,58 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
// KTD16:同一程序提供运维子命令;默认(无子命令)保持 serve 行为不变。
|
||||
if len(os.Args) > 1 {
|
||||
switch os.Args[1] {
|
||||
case "config":
|
||||
if len(os.Args) > 2 {
|
||||
switch os.Args[2] {
|
||||
case "check":
|
||||
configCheck(os.Args[3:])
|
||||
return
|
||||
case "explain":
|
||||
configExplain(os.Args[3:])
|
||||
return
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "用法: server config check|explain [--env path] [--port N] [--host H] [--backend name]")
|
||||
os.Exit(2)
|
||||
case "-h", "--help", "help":
|
||||
fmt.Println("用法: server [serve flags] 启动服务(默认)\n server config check [flags] 加载并校验配置(脱敏摘要,无副作用)")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
envPath := flag.String("env", ".env", "环境配置文件路径")
|
||||
port := flag.Int("port", 0, "HTTP 服务端口 (默认读取配置文件或 8080)")
|
||||
host := flag.String("host", "", "HTTP 监听主机 (默认读取配置文件或 127.0.0.1)")
|
||||
backend := flag.String("backend", "", "智能体后端调度覆盖 (stub, pi-agent, pi-native)")
|
||||
flag.Parse()
|
||||
|
||||
cfg, err := config.Load(*envPath)
|
||||
cliMutate := func(c *config.Config) {
|
||||
if *port > 0 {
|
||||
c.HTTPPort = *port
|
||||
}
|
||||
if *host != "" {
|
||||
c.HTTPHost = *host
|
||||
}
|
||||
if *backend != "" {
|
||||
c.AgentBackend = *backend
|
||||
}
|
||||
}
|
||||
cfg, _, err := config.LoadForServe(*envPath, cliMutate)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "加载配置失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *port > 0 {
|
||||
cfg.HTTPPort = *port
|
||||
}
|
||||
if *host != "" {
|
||||
cfg.HTTPHost = *host
|
||||
}
|
||||
if *backend != "" {
|
||||
cfg.AgentBackend = *backend
|
||||
own, err := bootstrap.EnsureInstance(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "实例启动所有权: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer own.Release()
|
||||
fmt.Printf("instance=%s data_dir=%s 启动所有权取得\n", own.InstanceID, own.DataDir)
|
||||
|
||||
rootCtx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
@@ -157,3 +189,78 @@ func shutdownDrainTimeout(cfg *config.Config) time.Duration {
|
||||
}
|
||||
return at + 30*time.Second
|
||||
}
|
||||
|
||||
// configCheck 加载并校验配置:输出脱敏摘要与问题清单。
|
||||
// 不创建 DB、不启动服务、不写业务目录(KTD17 config check 约束)。
|
||||
func configCheck(args []string) {
|
||||
fs := flag.NewFlagSet("config check", flag.ExitOnError)
|
||||
envPath := fs.String("env", ".env", "环境配置文件路径")
|
||||
port := fs.Int("port", 0, "HTTP 端口覆盖")
|
||||
host := fs.String("host", "", "HTTP 主机覆盖")
|
||||
backend := fs.String("backend", "", "智能体后端覆盖")
|
||||
_ = fs.Parse(args)
|
||||
|
||||
mut := func(c *config.Config) {
|
||||
if *port > 0 {
|
||||
c.HTTPPort = *port
|
||||
}
|
||||
if *host != "" {
|
||||
c.HTTPHost = *host
|
||||
}
|
||||
if *backend != "" {
|
||||
c.AgentBackend = *backend
|
||||
}
|
||||
}
|
||||
cfg, _, err := config.LoadForServe(*envPath, mut)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "config check: 加载失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println(cfg.MaskedSummary())
|
||||
if issues := cfg.Check(); len(issues) > 0 {
|
||||
fmt.Fprintln(os.Stderr, "config check: 未通过")
|
||||
for _, it := range issues {
|
||||
fmt.Fprintf(os.Stderr, " - %s\n", it)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("config check: 通过")
|
||||
}
|
||||
|
||||
// configExplain 输出字段级来源表(KTD17:只报告来源/状态,不打印值)。
|
||||
// 层序:cli > process-env > env-file(按加载顺序)> default。
|
||||
func configExplain(args []string) {
|
||||
fs := flag.NewFlagSet("config explain", flag.ExitOnError)
|
||||
envPath := fs.String("env", "", "环境配置文件路径(默认根 .env + configs/.env.{ENV} 叠加)")
|
||||
port := fs.Int("port", 0, "HTTP 端口覆盖")
|
||||
host := fs.String("host", "", "HTTP 主机覆盖")
|
||||
backend := fs.String("backend", "", "智能体后端覆盖")
|
||||
_ = fs.Parse(args)
|
||||
|
||||
mut := func(c *config.Config) {
|
||||
if *port > 0 {
|
||||
c.HTTPPort = *port
|
||||
}
|
||||
if *host != "" {
|
||||
c.HTTPHost = *host
|
||||
}
|
||||
if *backend != "" {
|
||||
c.AgentBackend = *backend
|
||||
}
|
||||
}
|
||||
cfg, src, err := config.LoadForServe(*envPath, mut)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "config explain: 加载失败: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("config explain: 字段 %d 个(层序 instance-settings > cli > process-env > env-file > default)\n", len(src))
|
||||
fmt.Print(config.FormatFieldSources(config.SortedFieldSources(src)))
|
||||
if issues := cfg.Check(); len(issues) > 0 {
|
||||
fmt.Fprintln(os.Stderr, "附注:当前配置未通过 check:")
|
||||
for _, it := range issues {
|
||||
fmt.Fprintf(os.Stderr, " - %s\n", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// configs 包内嵌运行提示词资产(U10a:单文件应用无源码目录时回退使用)。
|
||||
// 磁盘路径存在时仍优先磁盘(开发态行为不变)。
|
||||
package configs
|
||||
|
||||
import "embed"
|
||||
|
||||
// Assets 内嵌 configs/agents 与 configs/shared 提示词。
|
||||
//
|
||||
//go:embed agents shared
|
||||
var Assets embed.FS
|
||||
@@ -51,6 +51,33 @@ function extractAssistantText(ev) {
|
||||
return chunks.join("");
|
||||
}
|
||||
|
||||
// toProcFrame 把 pi RPC stdout 事件行投影为 Go 侧 agent.ProcessEvent 同构帧
|
||||
// (2026-09-28 过程流式设计 C.5)。只保留过程可视化需要的类型,其他类型原样
|
||||
// 透传 type 供前端扩展。think 块在 stripThinkBlocks 剥离前分流为 reasoning_delta。
|
||||
function toProcFrame(ev) {
|
||||
const t = ev.type || "";
|
||||
const msg = ev.message && typeof ev.message === "object" ? ev.message : null;
|
||||
if (t === "message_end" || t === "turn_end") {
|
||||
return { type: "message_end" };
|
||||
}
|
||||
if (msg && msg.role === "assistant") {
|
||||
const parts = Array.isArray(msg.content) ? msg.content : [];
|
||||
for (const part of parts) {
|
||||
if (part && typeof part === "object" && part.type === "reasoning" && typeof part.text === "string") {
|
||||
return { type: "reasoning_delta", text: part.text };
|
||||
}
|
||||
}
|
||||
}
|
||||
if (t === "tool_execution_start" || t === "tool_execution_end") {
|
||||
return {
|
||||
type: t,
|
||||
tool: typeof ev.tool === "string" ? ev.tool : (msg && typeof msg.name === "string" ? msg.name : ""),
|
||||
detail: typeof ev.detail === "string" ? ev.detail : "",
|
||||
};
|
||||
}
|
||||
return { type: t };
|
||||
}
|
||||
|
||||
function materializeReasoningTargets({
|
||||
workspace,
|
||||
targets,
|
||||
@@ -254,7 +281,7 @@ function authorizeRunner(req, res) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async function handleRun(body) {
|
||||
async function handleRun(body, onProcEvent) {
|
||||
const rawWs = body.workspace || "";
|
||||
const workspace = resolveWorkspace(rawWs);
|
||||
const model = body.model || runtimeModel || DEFAULT_MODEL;
|
||||
@@ -382,6 +409,11 @@ async function handleRun(body) {
|
||||
lastAssistantText = assistantChunk;
|
||||
}
|
||||
applyAgentEvent(settle, ev);
|
||||
if (onProcEvent) {
|
||||
try { onProcEvent(toProcFrame(ev)); } catch (e) {
|
||||
console.warn("[runner] proc-event forward failed:", e.message);
|
||||
}
|
||||
}
|
||||
if (t === "agent_settled") {
|
||||
child.stdin.end();
|
||||
}
|
||||
@@ -596,6 +628,35 @@ const server = http.createServer(async (req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 4a. Streaming Agent Task Run Endpoint (NDJSON chunked, 2026-09-28 process streaming design C.5)
|
||||
if (method === "POST" && url.pathname === "/v1/run/stream") {
|
||||
if (!authorizeRunner(req, res)) {
|
||||
return;
|
||||
}
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "application/x-ndjson",
|
||||
"Cache-Control": "no-cache",
|
||||
"X-Accel-Buffering": "no",
|
||||
});
|
||||
const writeFrame = (obj) => {
|
||||
try {
|
||||
res.write(JSON.stringify(obj) + "\n");
|
||||
} catch {
|
||||
// client disconnect: keep running so the run still settles; write errors ignored
|
||||
}
|
||||
};
|
||||
const onProcEvent = (frame) => writeFrame({ type: "proc", ev: frame });
|
||||
try {
|
||||
const body = await parseJsonBody(req);
|
||||
const result = await handleRun(body, onProcEvent);
|
||||
writeFrame({ type: "result", body: withResultV1(result) });
|
||||
} catch (err) {
|
||||
writeFrame({ type: "result", body: withResultV1({ ok: false, error: "runner_failed", patches: [], run_id: "unknown" }) });
|
||||
}
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Agent Task Run Endpoint
|
||||
if (method === "POST" && url.pathname === "/v1/run") {
|
||||
if (!authorizeRunner(req, res)) {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// Package dbassets 内嵌数据库迁移链(U10a:单文件应用无源码目录时回退使用)。
|
||||
// 磁盘目录存在时仍优先磁盘(开发态行为不变);磁盘与嵌入的一致性由
|
||||
// 迁移门禁测试(ExpectedMigrationCount)约束。
|
||||
package dbassets
|
||||
|
||||
import "embed"
|
||||
|
||||
// MigrationsFS 内嵌 deploy/postgres/goose 迁移链。
|
||||
//
|
||||
//go:embed all:goose
|
||||
var MigrationsFS embed.FS
|
||||
|
||||
// MigrationsDir 是嵌入 FS 内的迁移子目录名(goose SetBaseFS 的根)。
|
||||
const MigrationsDir = "goose"
|
||||
@@ -0,0 +1,30 @@
|
||||
# agent-system 实例单元模板(KTD16/U10c)。
|
||||
# 安装:cp agent-system@.service /etc/systemd/system/ && systemctl daemon-reload
|
||||
# 启动实例 alpha:systemctl start agent-system@alpha
|
||||
# 约定:
|
||||
# /opt/agent-system/releases/<version>/agent-system 不可变程序(多实例共享)
|
||||
# /etc/agent-system/<instance>/env 实例环境文件(0600)
|
||||
# /var/lib/agent-system/<instance>/ 实例数据根(DATA_DIR)
|
||||
# 同一浏览器不同端口为不同 origin(Bearer token + localStorage 按源隔离)。
|
||||
[Unit]
|
||||
Description=Agent-System instance %i
|
||||
After=network-online.target postgresql.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=agent-system
|
||||
Group=agent-system
|
||||
EnvironmentFile=/etc/agent-system/%i/env
|
||||
Environment=INSTANCE_ID=%i
|
||||
Environment=DATA_DIR=/var/lib/agent-system/%i
|
||||
WorkingDirectory=/var/lib/agent-system/%i
|
||||
ExecStart=/opt/agent-system/current/agent-system
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths=/var/lib/agent-system/%i
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -15,6 +15,19 @@ type Request struct {
|
||||
Timeout time.Duration `json:"timeout"`
|
||||
// ToolProfile is workspace (default, document tools) or reasoning (no tools).
|
||||
ToolProfile string `json:"tool_profile,omitempty"`
|
||||
// OnEvent 可选过程事件回调(nil = 调用方不需要过程,后端行为与现状完全一致)。
|
||||
// 实现方必须保证:回调在 Execute 返回前完成(同步调用或等待收尾),无泄漏 goroutine;
|
||||
// 回调内不得阻塞(消费方自行节流/异步入队)。
|
||||
OnEvent func(ev ProcessEvent)
|
||||
}
|
||||
|
||||
// ProcessEvent 对 agentx 事件与远程 runner NDJSON 过程帧的统一投影,
|
||||
// 供小队过程流式展示消费(见 .doc/system-design/2026-09-28-agent-process-streaming-and-subagent-view-design.md)。
|
||||
type ProcessEvent struct {
|
||||
Type string // text_delta | reasoning_delta | tool_call_delta | tool_execution_start | tool_execution_end | message_end | ...
|
||||
Text string // delta 文本(Type 为 *_delta 时)
|
||||
Tool string // 工具名(tool_* 时)
|
||||
Detail string // 工具参数/结果摘要(tool_execution_* 时)
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -83,6 +83,31 @@ func TestMaterializeTargets_MissingAndEmptyFail(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaterializeTargets_EmptyPatchKeepsExistingFile(t *testing.T) {
|
||||
workDir := t.TempDir()
|
||||
body := []byte("# 调研报告\n已写在盘上的正文\n")
|
||||
path := filepath.Join(workDir, "output.md")
|
||||
if err := os.WriteFile(path, body, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := MaterializeTargets(workDir, []string{"output.md"}, map[string][]byte{
|
||||
"output.md": nil,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(got["output.md"]) != string(body) {
|
||||
t.Fatalf("collected %q", got["output.md"])
|
||||
}
|
||||
disk, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(disk) != string(body) {
|
||||
t.Fatalf("disk overwritten with %q", disk)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaterializeTargets_RejectsSymlinkEscape(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
outside := t.TempDir()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
@@ -217,6 +218,14 @@ func (p *PiAgentBackend) executeRemote(ctx context.Context, runnerURL string, re
|
||||
}
|
||||
reqBody, _ := json.Marshal(p.runnerPayload(req, workDir, true, timeout))
|
||||
|
||||
// 过程流式路径(2026-09-28 设计 C.5):调用方带 OnEvent 时优先走 NDJSON 流式
|
||||
// 端点,实时转发 proc 帧;旧 runner 404 时回落一次性 /v1/run(无过程事件)。
|
||||
if req.OnEvent != nil {
|
||||
if res, attempted, err := p.executeRemoteStream(ctx, runnerURL, reqBody, req, expectedRunID, timeout); attempted {
|
||||
return res, err
|
||||
}
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, runnerURL+"/v1/run", bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
return &Result{
|
||||
@@ -244,8 +253,85 @@ func (p *PiAgentBackend) executeRemote(ctx context.Context, runnerURL string, re
|
||||
if len(limited) > MaxResultBodyBytes {
|
||||
return &Result{Success: false, ErrorReason: "runner result body too large"}, nil
|
||||
}
|
||||
return decodeRemoteResult(limited, expectedRunID, workDir, req)
|
||||
}
|
||||
|
||||
decoded, decErr := DecodeRemoteRun(limited, expectedRunID, true)
|
||||
// executeRemoteStream 调用 runner /v1/run/stream:proc 帧实时转发 OnEvent,
|
||||
// result 终帧走与一次性路径完全相同的 DecodeRemoteRun 校验。
|
||||
// attempted=false 表示端点不可用(404/405),调用方应回落 /v1/run。
|
||||
func (p *PiAgentBackend) executeRemoteStream(ctx context.Context, runnerURL string, reqBody []byte, req Request, expectedRunID string, timeout time.Duration) (*Result, bool, error) {
|
||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, runnerURL+"/v1/run/stream", bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
httpReq.Header.Set("Accept", "application/x-ndjson")
|
||||
ApplyRunnerAuth(httpReq, p.cfg.PiAgentRunnerAuthToken)
|
||||
|
||||
// 无总超时的 client:超时由 ctx 控制,逐行流式读取期间保持连接。
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, false, nil // 网络错误也回落一次性路径
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
// 任何非 200(含旧 runner 404 与瞬态 5xx)都回落一次性路径,
|
||||
// 保证错误分类与既有 /v1/run 语义完全一致(不改变治理边界)。
|
||||
resp.Body.Close()
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
var resultBody []byte
|
||||
var sawResult bool
|
||||
sc := bufio.NewScanner(resp.Body)
|
||||
sc.Buffer(make([]byte, 0, 64*1024), MaxResultBodyBytes)
|
||||
for sc.Scan() {
|
||||
if ctx.Err() != nil {
|
||||
return &Result{Success: false, ErrorReason: "stream context cancelled", Retryable: true}, true, nil
|
||||
}
|
||||
line := strings.TrimSpace(sc.Text())
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
var frame struct {
|
||||
Type string `json:"type"`
|
||||
Ev *ProcessEvent `json:"ev,omitempty"`
|
||||
Body json.RawMessage `json:"body,omitempty"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(line), &frame); err != nil {
|
||||
continue
|
||||
}
|
||||
switch frame.Type {
|
||||
case "proc":
|
||||
if frame.Ev != nil && req.OnEvent != nil {
|
||||
req.OnEvent(*frame.Ev)
|
||||
}
|
||||
case "result":
|
||||
resultBody = frame.Body
|
||||
sawResult = true
|
||||
}
|
||||
if sawResult {
|
||||
break // result 是终帧
|
||||
}
|
||||
}
|
||||
if !sawResult {
|
||||
return &Result{
|
||||
Success: false,
|
||||
ErrorReason: "pi runner stream ended without result frame",
|
||||
Retryable: true,
|
||||
}, true, nil
|
||||
}
|
||||
res, decErr := decodeRemoteResult(resultBody, expectedRunID, req.WorkDir, req)
|
||||
if decErr != nil {
|
||||
return res, true, decErr
|
||||
}
|
||||
return res, true, nil
|
||||
}
|
||||
|
||||
// decodeRemoteResult 一次性与流式路径共用的终态解码与落盘。
|
||||
func decodeRemoteResult(body []byte, expectedRunID, workDir string, req Request) (*Result, error) {
|
||||
decoded, decErr := DecodeRemoteRun(body, expectedRunID, true)
|
||||
if decErr != nil {
|
||||
reason := "typed result rejected"
|
||||
msg := decErr.Error()
|
||||
|
||||
@@ -93,6 +93,7 @@ func (p *PiNativeBackend) Execute(ctx context.Context, req Request) (*Result, er
|
||||
defer cancel()
|
||||
|
||||
var logs strings.Builder
|
||||
onProcess := req.OnEvent
|
||||
runRes, err := agentx.Run(runCtx, agentx.Config{
|
||||
Model: model,
|
||||
SystemPrompt: nativeSystemPrompt(req, profile),
|
||||
@@ -110,6 +111,14 @@ func (p *PiNativeBackend) Execute(ctx context.Context, req Request) (*Result, er
|
||||
logs.WriteString(ev.Text)
|
||||
}
|
||||
logs.WriteByte('\n')
|
||||
if onProcess != nil {
|
||||
onProcess(ProcessEvent{
|
||||
Type: ev.Type,
|
||||
Text: ev.Text,
|
||||
Tool: ev.Tool,
|
||||
Detail: ev.Detail,
|
||||
})
|
||||
}
|
||||
},
|
||||
}, llm.Message{Role: llm.RoleUser, Content: req.Prompt})
|
||||
if err != nil {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
type sseChoice struct {
|
||||
Delta struct {
|
||||
Content string `json:"content"`
|
||||
Reasoning string `json:"reasoning_content"`
|
||||
ToolCalls []struct {
|
||||
Index int `json:"index"`
|
||||
ID string `json:"id"`
|
||||
@@ -94,6 +95,17 @@ func emitParsed(ctx context.Context, r io.Reader, out chan<- Event) {
|
||||
case out <- Event{Kind: KindTextDelta, Text: choice.Delta.Content}:
|
||||
}
|
||||
}
|
||||
// reasoning_content(国产 OpenAI 兼容模型的思考链)与正文分流:
|
||||
// 只作为 reasoning_delta 事件外发,不计入 Message.Content(与 runner
|
||||
// stripThinkBlocks"正文无 think"语义对齐)。
|
||||
if choice.Delta.Reasoning != "" {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
emitAbort(out, content.String(), ctx.Err())
|
||||
return
|
||||
case out <- Event{Kind: KindReasoningDelta, ReasoningDelta: choice.Delta.Reasoning}:
|
||||
}
|
||||
}
|
||||
for _, tc := range choice.Delta.ToolCalls {
|
||||
cur, ok := calls[tc.Index]
|
||||
if !ok {
|
||||
|
||||
@@ -189,3 +189,36 @@ func TestStreamErrorString(t *testing.T) {
|
||||
t.Fatal("error string")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseSSEReasoningDelta(t *testing.T) {
|
||||
body := strings.Join([]string{
|
||||
`data: {"choices":[{"delta":{"reasoning_content":"先分析需求"},"index":0}]}`,
|
||||
`data: {"choices":[{"delta":{"reasoning_content":"再检索标准"},"index":0}]}`,
|
||||
`data: {"choices":[{"delta":{"content":"结论"},"finish_reason":"stop"}],"usage":{"prompt_tokens":1,"completion_tokens":2,"total_tokens":3}}`,
|
||||
`data: [DONE]`,
|
||||
"",
|
||||
}, "\n")
|
||||
var reasoning []string
|
||||
var text []string
|
||||
var end Event
|
||||
for ev := range ParseSSE(context.Background(), strings.NewReader(body)) {
|
||||
switch ev.Kind {
|
||||
case KindReasoningDelta:
|
||||
reasoning = append(reasoning, ev.ReasoningDelta)
|
||||
case KindTextDelta:
|
||||
text = append(text, ev.Text)
|
||||
case KindMessageEnd:
|
||||
end = ev
|
||||
}
|
||||
}
|
||||
if strings.Join(reasoning, "") != "先分析需求再检索标准" {
|
||||
t.Fatalf("reasoning deltas %v", reasoning)
|
||||
}
|
||||
// 正文与思考链分流:Content 只含 text,不含 reasoning
|
||||
if strings.Join(text, "") != "结论" {
|
||||
t.Fatalf("text deltas %v", text)
|
||||
}
|
||||
if end.Message.Content != "结论" {
|
||||
t.Fatalf("end content %q must not include reasoning", end.Message.Content)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,11 @@ const (
|
||||
type EventKind string
|
||||
|
||||
const (
|
||||
KindTextDelta EventKind = "text_delta"
|
||||
KindToolCallDelta EventKind = "tool_call_delta"
|
||||
KindMessageEnd EventKind = "message_end"
|
||||
KindError EventKind = "error"
|
||||
KindTextDelta EventKind = "text_delta"
|
||||
KindReasoningDelta EventKind = "reasoning_delta"
|
||||
KindToolCallDelta EventKind = "tool_call_delta"
|
||||
KindMessageEnd EventKind = "message_end"
|
||||
KindError EventKind = "error"
|
||||
)
|
||||
|
||||
// ToolCall is one function call requested by the assistant.
|
||||
@@ -72,6 +73,7 @@ type Usage struct {
|
||||
type Event struct {
|
||||
Kind EventKind
|
||||
Text string
|
||||
ReasoningDelta string
|
||||
ToolIndex int
|
||||
ToolID string
|
||||
ToolName string
|
||||
|
||||
@@ -22,6 +22,9 @@ type Event struct {
|
||||
Message llm.Message
|
||||
Tool string
|
||||
Text string
|
||||
// Detail carries tool arguments/results or reasoning deltas for
|
||||
// process-visualization consumers; log writers may ignore it.
|
||||
Detail string
|
||||
}
|
||||
|
||||
// Config drives one agent session.
|
||||
@@ -178,6 +181,10 @@ func consume(ctx context.Context, cfg Config, messages []llm.Message) (llm.Event
|
||||
switch ev.Kind {
|
||||
case llm.KindTextDelta:
|
||||
emit(cfg, Event{Type: "text_delta", Text: ev.Text})
|
||||
case llm.KindReasoningDelta:
|
||||
emit(cfg, Event{Type: "reasoning_delta", Text: ev.ReasoningDelta})
|
||||
case llm.KindToolCallDelta:
|
||||
emit(cfg, Event{Type: "tool_call_delta", Tool: ev.ToolName, Detail: ev.ArgumentsDelta})
|
||||
case llm.KindError:
|
||||
return ev, ev.Err
|
||||
case llm.KindMessageEnd:
|
||||
|
||||
@@ -244,6 +244,7 @@ func Assemble(cfg *config.Config, svc *bizdev.Service) (*Runtime, error) {
|
||||
stages := stageflow.New(svc)
|
||||
coordEngine.SetStageTaskRunner(stages)
|
||||
coordEngine.SetContextServices(parts.Ctx, parts.KB)
|
||||
coordEngine.SetSessionRunReader(sessionRunAdapter{mgr: taskMgr})
|
||||
if instSvc != nil && instSvc.Available() {
|
||||
coordEngine.SetInstanceService(instSvc)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"chat-agent-system/internal/app/intentflow"
|
||||
"chat-agent-system/internal/bizdev"
|
||||
"chat-agent-system/internal/chat"
|
||||
"chat-agent-system/internal/coordinator"
|
||||
"chat-agent-system/internal/ctxmgr"
|
||||
"chat-agent-system/internal/taskrun"
|
||||
)
|
||||
@@ -117,6 +118,10 @@ func (a chatTaskStarter) CreateAndStart(ctx context.Context, runID string, recor
|
||||
}, bizdev.StageRunRequest{}, resumeDir)
|
||||
}
|
||||
|
||||
func (a chatTaskStarter) BindAssistantMsgID(ctx context.Context, runID string, assistantMsgID int64) error {
|
||||
return a.inner.BindAssistantMsgID(ctx, runID, assistantMsgID)
|
||||
}
|
||||
|
||||
// BindChatSession attaches composition-root adapters onto a chat session.
|
||||
// Callers that keep a mutable intentflow.Service (e.g. server test hooks) should
|
||||
// prefer AdaptChat* helpers with their own fields so swaps stay visible.
|
||||
@@ -138,3 +143,25 @@ func (rt *Runtime) BindChatSession(sess *chat.Session, env string) {
|
||||
Environment: env,
|
||||
}
|
||||
}
|
||||
|
||||
type sessionRunAdapter struct {
|
||||
mgr *taskrun.Manager
|
||||
}
|
||||
|
||||
func (a sessionRunAdapter) LatestSessionRun(ctx context.Context, sessionID string) (*coordinator.SessionRunSnapshot, error) {
|
||||
if a.mgr == nil {
|
||||
return nil, nil
|
||||
}
|
||||
rec, err := a.mgr.LatestSessionRun(ctx, sessionID)
|
||||
if err != nil || rec == nil {
|
||||
return nil, err
|
||||
}
|
||||
return &coordinator.SessionRunSnapshot{
|
||||
RunID: rec.RunID,
|
||||
TaskKey: rec.TaskKey,
|
||||
Status: rec.Status,
|
||||
WorkspaceID: rec.WorkspaceID,
|
||||
ErrorMessage: rec.ErrorMessage,
|
||||
UpdatedAt: rec.UpdatedAt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -100,4 +100,4 @@ func TestLoginMissingPasswordHash(t *testing.T) {
|
||||
if err == nil || err.Error() != "用户名或密码错误" {
|
||||
t.Fatalf("expected unified credential error, got %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package auth_test
|
||||
|
||||
import (
|
||||
"chat-agent-system/internal/db"
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -15,11 +15,7 @@ import (
|
||||
|
||||
func openTestPool(t *testing.T) *pgxpool.Pool {
|
||||
t.Helper()
|
||||
dsn := os.Getenv("TEST_DATABASE_URL")
|
||||
if dsn == "" {
|
||||
t.Skip("TEST_DATABASE_URL must be set for PostgreSQL integration tests (refusing default dev database)")
|
||||
return nil
|
||||
}
|
||||
dsn := db.SkipIfNoTestDatabaseURL(t)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
pool, err := pgxpool.New(ctx, dsn)
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"chat-agent-system/internal/config"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
// Stores 认证账户存储与会话存储组合。
|
||||
|
||||
@@ -239,7 +239,7 @@ func TestExecuteTask_CreateDocumentFailureIsNotSuccess(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteTask_EmptyDocIDSkipsDocRevision(t *testing.T) {
|
||||
func TestExecuteTask_ControlledKeyWithoutProjectRejected(t *testing.T) {
|
||||
svc := MustNewService(t, &config.Config{DataDir: t.TempDir(), AgentBackend: "stub"})
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -253,17 +253,14 @@ func TestExecuteTask_EmptyDocIDSkipsDocRevision(t *testing.T) {
|
||||
Backend: "stub",
|
||||
Timeout: 10 * time.Second,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("empty docID ExecuteTask panicked or failed: %v", err)
|
||||
if !errors.Is(err, ErrUnboundControlledProject) {
|
||||
t.Fatalf("controlled task without project must fail closed, err=%v", err)
|
||||
}
|
||||
if res == nil || !res.Success {
|
||||
t.Fatalf("CLI-like stage path should succeed without doc ledger, got %#v", res)
|
||||
if res == nil || res.Success || res.DocRecord != nil {
|
||||
t.Fatalf("unbound controlled task must not create a ledger or report success: %#v", res)
|
||||
}
|
||||
if res.DocRecord != nil {
|
||||
t.Fatalf("empty docID must not create a document record, got %#v", res.DocRecord)
|
||||
}
|
||||
if _, stErr := svc.docRev.GetRevisionState(ctx, ""); !strings.Contains(stErr.Error(), "invalid doc_id") {
|
||||
t.Fatalf("empty GetRevisionState should be invalid, got %v", stErr)
|
||||
if !strings.Contains(res.ErrorReason, "会话未绑定项目") {
|
||||
t.Fatalf("ErrorReason=%q", res.ErrorReason)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,9 @@
|
||||
package bizdev
|
||||
|
||||
import (
|
||||
"chat-agent-system/internal/quote"
|
||||
"chat-agent-system/internal/squad"
|
||||
)
|
||||
|
||||
// FormatMoney formats a currency amount for display in markdown (no grouping separators).
|
||||
func FormatMoney(v float64) string {
|
||||
return squad.FormatMoney(v)
|
||||
}
|
||||
|
||||
// RenderCDOQuoteTable builds the pricing table from quote summary lines and total.
|
||||
func RenderCDOQuoteTable(summary *quote.Summary) string {
|
||||
return squad.RenderCDOQuoteTable(summary)
|
||||
}
|
||||
|
||||
// BackfillCDOQuoteMarkdown replaces placeholder sections with computed pricing from summary.
|
||||
func BackfillCDOQuoteMarkdown(body string, summary *quote.Summary) string {
|
||||
return squad.BackfillCDOQuoteMarkdown(body, summary)
|
||||
}
|
||||
|
||||
// WriteMaterials persists uploaded material text into workspace materials_text/.
|
||||
func WriteMaterials(workDir string, materials []MaterialInput) error {
|
||||
var items []squad.MaterialItem
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"chat-agent-system/internal/materialindex"
|
||||
"chat-agent-system/internal/squad"
|
||||
"chat-agent-system/internal/textextract"
|
||||
"chat-agent-system/internal/storage"
|
||||
)
|
||||
|
||||
@@ -154,7 +155,7 @@ func (s *Service) materialToItems(
|
||||
meta["parse_engine"] = "mineru"
|
||||
}
|
||||
} else {
|
||||
text, err = squad.ExtractTextWithAnchors(fileName, data)
|
||||
text, err = textextract.ExtractTextWithAnchors(fileName, data)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("extract material %q: %w", fileName, err)
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ func TestExecuteTask_Uncontrolled_DoesNotForceAutoRepair(t *testing.T) {
|
||||
dataDir := t.TempDir()
|
||||
svc := MustNewService(t, &config.Config{DataDir: dataDir, AgentBackend: "stub"})
|
||||
res, err := svc.ExecuteTask(context.Background(), TaskRunRequest{
|
||||
TaskKey: "intake",
|
||||
TaskKey: "adhoc_notes",
|
||||
ProjectID: 0,
|
||||
DocID: "",
|
||||
AutoRepair: false,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package squad
|
||||
package bizdev
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -0,0 +1,74 @@
|
||||
package bizdev
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"chat-agent-system/internal/quote"
|
||||
"chat-agent-system/internal/squad"
|
||||
"chat-agent-system/internal/workflowdef"
|
||||
)
|
||||
|
||||
// handleCDOQuotePreparation 为 CDO 报价任务执行 Excel 核算并生成回填内容
|
||||
// (KTD1:报价领域规则、核算与 xlsx 资产归 Core;只计算,不写 squad 工作区,
|
||||
// 文件由 squad 经 TaskPreparation.Backfills 统一写入)。
|
||||
func (s *Service) handleCDOQuotePreparation(workDir string, req squad.SquadRunRequest) (*squad.TaskPreparation, error) {
|
||||
in := quote.Input{
|
||||
CustomerName: req.CustomerName,
|
||||
ProductName: req.ProjectName,
|
||||
Hours: map[string]float64{
|
||||
"结构工程师": 320,
|
||||
"硬件工程师": 320,
|
||||
"软件工程师": 480,
|
||||
"项目经理": 160,
|
||||
},
|
||||
}
|
||||
// 核算写入临时文件后读回字节;工作区文件由 squad 写入。
|
||||
tmp, err := os.CreateTemp("", "cdo-quote-*.xlsx")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tmpPath := tmp.Name()
|
||||
tmp.Close()
|
||||
defer os.Remove(tmpPath)
|
||||
|
||||
summary, err := quote.CalculateAndFill(in, "templates/xlsx/cdo_quote.xlsx", tmpPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := os.ReadFile(tmpPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
backfills := map[string][]byte{
|
||||
"CDO报价核算-V01.xlsx": data,
|
||||
"artifacts/CDO报价核算-V01.xlsx": data,
|
||||
}
|
||||
// 回填 template.md 报价占位(模板存在时);output.md 镜像由 squad 决定
|
||||
tmplBytes, tmplErr := os.ReadFile(filepath.Join(workDir, "template.md"))
|
||||
mirror := false
|
||||
if tmplErr == nil {
|
||||
filled := BackfillCDOQuoteMarkdown(string(tmplBytes), summary)
|
||||
backfills["template.md"] = []byte(filled)
|
||||
mirror = true
|
||||
}
|
||||
|
||||
return &squad.TaskPreparation{
|
||||
Attachment: &squad.Attachment{
|
||||
Filename: "CDO报价核算-V01.xlsx",
|
||||
ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
Data: data,
|
||||
},
|
||||
Backfills: backfills,
|
||||
MirrorTemplateToOutput: mirror,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// squadTaskPreparer 仅对 CDO 报价任务触发领域准备,其余任务快速跳过。
|
||||
func (s *Service) squadTaskPreparer(workDir string, req squad.SquadRunRequest) (*squad.TaskPreparation, error) {
|
||||
if !workflowdef.SameTaskKey(req.TaskKey, "cdo_quote") {
|
||||
return nil, nil
|
||||
}
|
||||
return s.handleCDOQuotePreparation(workDir, req)
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"chat-agent-system/internal/agent"
|
||||
"chat-agent-system/internal/app/uow"
|
||||
"chat-agent-system/internal/authz"
|
||||
"chat-agent-system/internal/config"
|
||||
@@ -192,7 +193,6 @@ func NewService(cfg *config.Config) (*Service, error) {
|
||||
svc := &Service{
|
||||
cfg: cfg,
|
||||
kb: kb.NewClient(cfg),
|
||||
squad: squad.NewService(cfg),
|
||||
ctxMgr: ctxMgr,
|
||||
cas: casStore,
|
||||
materialIndex: matIndex,
|
||||
@@ -203,6 +203,16 @@ func NewService(cfg *config.Config) (*Service, error) {
|
||||
pool: pool,
|
||||
schemaVersion: schemaVersion,
|
||||
}
|
||||
svc.squad = squad.NewService(squad.Options{
|
||||
BackendName: cfg.AgentBackend,
|
||||
BackendTimeout: cfg.AgentTimeout,
|
||||
DataDir: cfg.DataDir,
|
||||
NewBackend: func(name string) agent.Backend {
|
||||
return agent.NewBackend(name, cfg)
|
||||
},
|
||||
TaskPreparer: svc.squadTaskPreparer,
|
||||
Assets: templateAssetProvider{},
|
||||
})
|
||||
svc.parts = Parts{
|
||||
Pool: pool,
|
||||
Gov: docGov,
|
||||
@@ -322,10 +332,10 @@ func (s *Service) ListStages() []StageMeta {
|
||||
}
|
||||
|
||||
func (s *Service) InspectWorkspace(ctx context.Context, workDir string) (*squad.WorkspaceState, error) {
|
||||
return s.squad.InspectWorkspace(ctx, workDir)
|
||||
return s.squad.InspectWorkspace(withSquadProgress(ctx), workDir)
|
||||
}
|
||||
|
||||
// ResumeOrModifyWorkspace 动态调整任务并继续执行
|
||||
func (s *Service) ResumeOrModifyWorkspace(ctx context.Context, req squad.ModifyWorkflowRequest) (*squad.SquadRunResult, error) {
|
||||
return s.squad.ResumeOrModifyWorkspace(ctx, req)
|
||||
return s.squad.ResumeOrModifyWorkspace(withSquadProgress(ctx), req)
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ import (
|
||||
"chat-agent-system/internal/contracts/vocabulary"
|
||||
"chat-agent-system/internal/docduty"
|
||||
"chat-agent-system/internal/docgov"
|
||||
"chat-agent-system/internal/intent"
|
||||
"chat-agent-system/internal/notification"
|
||||
"chat-agent-system/internal/docrevision"
|
||||
"chat-agent-system/internal/intent"
|
||||
"chat-agent-system/internal/kb"
|
||||
"chat-agent-system/internal/notification"
|
||||
"chat-agent-system/internal/projectcontext"
|
||||
"chat-agent-system/internal/projectworkflow"
|
||||
"chat-agent-system/internal/squad"
|
||||
@@ -119,7 +119,32 @@ func (s *Service) hasCompletedMainInstance(ctx context.Context, projectID int64)
|
||||
return ok, err
|
||||
}
|
||||
|
||||
// ErrUnboundControlledProject 表示受控目录任务没有项目绑定,不能静默落到隔离工作区。
|
||||
var ErrUnboundControlledProject = errors.New("会话未绑定项目,受控交付物无法入账")
|
||||
|
||||
// controlledDeliverableRequiresProject 覆盖发布目录里的受控任务(旧键与新键)和研发设计控制 rd_*。
|
||||
// 未知自由任务两边都原样返回,不在此列。
|
||||
func controlledDeliverableRequiresProject(taskKey string) bool {
|
||||
key := strings.TrimSpace(taskKey)
|
||||
if key == "" {
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(key, "rd_") {
|
||||
return true
|
||||
}
|
||||
return workflowdef.CanonicalTaskKey(key) != key || workflowdef.ImplementationTaskKey(key) != key
|
||||
}
|
||||
|
||||
func (s *Service) ExecuteTask(ctx context.Context, req TaskRunRequest) (*TaskRunResult, error) {
|
||||
if controlledDeliverableRequiresProject(req.TaskKey) && req.ProjectID <= 0 {
|
||||
return &TaskRunResult{
|
||||
TaskKey: req.TaskKey,
|
||||
ArtifactTitle: squad.TaskArtifactTitle(req.TaskKey),
|
||||
Success: false,
|
||||
ErrorReason: ErrUnboundControlledProject.Error(),
|
||||
}, ErrUnboundControlledProject
|
||||
}
|
||||
|
||||
if err := s.ensureTaskStageAccess(ctx, req.ProjectID, req.UserID, req.TaskKey); err != nil {
|
||||
return &TaskRunResult{
|
||||
TaskKey: req.TaskKey,
|
||||
@@ -681,7 +706,7 @@ func (s *Service) ExecuteTask(ctx context.Context, req TaskRunRequest) (*TaskRun
|
||||
RunID: req.RunID,
|
||||
}
|
||||
|
||||
res, err := s.squad.ExecuteTask(ctx, squadReq)
|
||||
res, err := s.squad.ExecuteTask(withSquadProgress(ctx), squadReq)
|
||||
if err != nil {
|
||||
if res != nil {
|
||||
out := &TaskRunResult{
|
||||
|
||||
@@ -30,6 +30,7 @@ func TestExecuteTaskIndependent(t *testing.T) {
|
||||
// 测试 1: 单独执行 intake (不依赖任何前序)
|
||||
intakeRes, err := svc.ExecuteTask(context.Background(), bizdev.TaskRunRequest{
|
||||
TaskKey: "intake",
|
||||
ProjectID: 1,
|
||||
ProjectName: "微创吻合器研发",
|
||||
CustomerName: "北京天工",
|
||||
DeviceType: "腔镜微创器械",
|
||||
@@ -58,6 +59,7 @@ func TestExecuteTaskIndependent(t *testing.T) {
|
||||
// 测试 2: 单独执行 cdo_quote (直接输出 Excel 附件)
|
||||
quoteRes, err := svc.ExecuteTask(context.Background(), bizdev.TaskRunRequest{
|
||||
TaskKey: "cdo_quote",
|
||||
ProjectID: 1,
|
||||
ProjectName: "微创吻合器研发",
|
||||
CustomerName: "北京天工",
|
||||
})
|
||||
@@ -118,6 +120,7 @@ func TestExecuteTaskWritesMaterials(t *testing.T) {
|
||||
materialContent := []byte("客户提供的补充需求说明")
|
||||
res, err := svc.ExecuteTask(context.Background(), bizdev.TaskRunRequest{
|
||||
TaskKey: "intake",
|
||||
ProjectID: 1,
|
||||
ProjectName: "测试项目",
|
||||
CustomerName: "测试客户",
|
||||
WorkDir: workDir,
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package bizdev
|
||||
|
||||
import (
|
||||
"chat-agent-system/internal/squad"
|
||||
"chat-agent-system/internal/template"
|
||||
)
|
||||
|
||||
// templateAssetProvider 把内容资产端口映射到产品模板包(KTD1 装配侧):
|
||||
// 模板与指令的内容、版本与磁盘/内嵌来源由产品侧全权决定。
|
||||
type templateAssetProvider struct{}
|
||||
|
||||
func (templateAssetProvider) DocTemplate(taskKey string) (string, error) {
|
||||
return template.GetDocTemplate(taskKey)
|
||||
}
|
||||
|
||||
func (templateAssetProvider) RenderDoc(tmpl string, vars map[string]string) string {
|
||||
return template.RenderDoc(tmpl, vars)
|
||||
}
|
||||
|
||||
func (templateAssetProvider) AgentInstruction(role string) (string, error) {
|
||||
return template.GetAgentInstruction(role)
|
||||
}
|
||||
|
||||
func (templateAssetProvider) AgentInstructionFile(file string) (string, error) {
|
||||
return template.GetAgentInstructionFile(file)
|
||||
}
|
||||
|
||||
// compile-time port conformance
|
||||
var _ squad.AssetProvider = templateAssetProvider{}
|
||||
@@ -0,0 +1,65 @@
|
||||
package bizdev
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"chat-agent-system/internal/squad"
|
||||
"chat-agent-system/internal/stream"
|
||||
)
|
||||
|
||||
// squadProgressAdapter 把小队过程端口映射到产品 SSE 流(KTD1 装配侧)。
|
||||
// 小队不感知 stream 包;SSE 事件形态、持久化与重放由产品侧全权负责。
|
||||
type squadProgressAdapter struct {
|
||||
streamer *stream.Streamer
|
||||
}
|
||||
|
||||
func (a *squadProgressAdapter) EmitPhase(phaseID, message, role string, stepIndex, totalSteps, progress int) {
|
||||
a.streamer.EmitPhase(phaseID, message, role, stepIndex, totalSteps, progress)
|
||||
}
|
||||
|
||||
func (a *squadProgressAdapter) EmitDetail(p squad.DetailEvent) {
|
||||
var tool *stream.ToolCallInfo
|
||||
if p.Tool != nil {
|
||||
tool = &stream.ToolCallInfo{
|
||||
Name: p.Tool.Name,
|
||||
Status: p.Tool.Status,
|
||||
Input: p.Tool.Input,
|
||||
Output: p.Tool.Output,
|
||||
DurationMs: p.Tool.DurationMs,
|
||||
}
|
||||
}
|
||||
a.streamer.Emit(stream.StreamEvent{Type: stream.EventDetail, Detail: &stream.DetailPayload{
|
||||
Category: p.Category,
|
||||
StepID: p.StepID,
|
||||
Role: p.Role,
|
||||
ItemID: p.ItemID,
|
||||
Title: p.Title,
|
||||
Content: p.Content,
|
||||
Collapsible: p.Collapsible,
|
||||
Delta: p.Delta,
|
||||
Done: p.Done,
|
||||
Tool: tool,
|
||||
}})
|
||||
}
|
||||
|
||||
func (a *squadProgressAdapter) EmitDeliverable(taskKey, title, filePath string, wordCount int, score float64) {
|
||||
a.streamer.EmitDeliverable(taskKey, title, filePath, wordCount, score)
|
||||
}
|
||||
|
||||
func (a *squadProgressAdapter) EmitSummary(title, content string, maturityScore int, maturityLevel string) {
|
||||
a.streamer.EmitSummary(title, content, maturityScore, maturityLevel)
|
||||
}
|
||||
|
||||
func (a *squadProgressAdapter) EmitBriefing(brief any) {
|
||||
a.streamer.EmitBriefing(brief)
|
||||
}
|
||||
|
||||
// withSquadProgress 在调用小队前装配过程端口:ctx 已有 Streamer 时注入适配器,
|
||||
// 否则原样返回(小队对缺失端口全程 no-op,行为与旧版 streamer==nil 分支一致)。
|
||||
func withSquadProgress(ctx context.Context) context.Context {
|
||||
st := stream.FromContext(ctx)
|
||||
if st == nil {
|
||||
return ctx
|
||||
}
|
||||
return squad.WithProgress(ctx, &squadProgressAdapter{streamer: st})
|
||||
}
|
||||
@@ -91,6 +91,9 @@ func (r *TaskRunResult) CompletionSummary() string {
|
||||
if r.QualityReport != nil && r.QualityReport.Passed {
|
||||
fmt.Fprintf(&b, "\n质量评分:%.0f/100", r.QualityReport.Score)
|
||||
}
|
||||
if r.DocRecord == nil && controlledDeliverableRequiresProject(r.TaskKey) {
|
||||
b.WriteString("\n交付物已生成于工作区,尚未写入受控文档账本。")
|
||||
}
|
||||
default:
|
||||
fmt.Fprintf(&b, "《%s》未成功落盘。\n\n", title)
|
||||
if r.ErrorReason != "" {
|
||||
|
||||
@@ -64,6 +64,18 @@ func TestTaskRunResult_ProposalIntercept_DistinctFromPendingAgent(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskRunResult_CompletedWithoutLedger_SaysNotProjected(t *testing.T) {
|
||||
res := &bizdev.TaskRunResult{
|
||||
Success: true,
|
||||
ArtifactTitle: "调研报告",
|
||||
TaskKey: "research",
|
||||
}
|
||||
sum := res.CompletionSummary()
|
||||
if !strings.Contains(sum, "尚未写入受控文档账本") {
|
||||
t.Fatalf("summary=%s", sum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskRunResult_CompletedOnlyWhenCommitted(t *testing.T) {
|
||||
res := &bizdev.TaskRunResult{
|
||||
Success: true,
|
||||
|
||||
@@ -29,3 +29,13 @@ func MustNewService(t testing.TB, cfg *config.Config) *Service {
|
||||
}
|
||||
return svc
|
||||
}
|
||||
|
||||
// ClearDocumentGovernanceForTest 去掉账本,让阶段编排测试只覆盖小队产出。
|
||||
// 受控任务仍须带 ProjectID,否则 ExecuteTask 会拒绝未绑定项目。
|
||||
func ClearDocumentGovernanceForTest(s *Service) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.docGov = nil
|
||||
s.docRev = nil
|
||||
}
|
||||
|
||||
@@ -87,10 +87,12 @@ func TestExecuteStageV01AndV05(t *testing.T) {
|
||||
}
|
||||
|
||||
svc := bizdev.MustNewService(t, cfg)
|
||||
bizdev.ClearDocumentGovernanceForTest(svc)
|
||||
|
||||
// 1. 运行 Stage 1 (V0.1 Triage)
|
||||
stage1Res, err := svc.ExecuteStage(context.Background(), bizdev.StageRunRequest{
|
||||
Stage: bizdev.StageV01_Triage,
|
||||
ProjectID: 1,
|
||||
ProjectName: "内窥镜手术动力系统",
|
||||
CustomerName: "北京天衡医疗",
|
||||
DeviceType: "有源手术动力装置",
|
||||
@@ -128,6 +130,7 @@ func TestExecuteStageV01AndV05(t *testing.T) {
|
||||
|
||||
stage2Res, err := svc.ExecuteStage(context.Background(), bizdev.StageRunRequest{
|
||||
Stage: bizdev.StageV05_Feasibility,
|
||||
ProjectID: 1,
|
||||
ProjectName: "内窥镜手术动力系统",
|
||||
CustomerName: "北京天衡医疗",
|
||||
DeviceType: "有源手术动力装置",
|
||||
@@ -163,6 +166,7 @@ func TestExecuteStageV01AndV05(t *testing.T) {
|
||||
|
||||
stage3Res, err := svc.ExecuteStage(context.Background(), bizdev.StageRunRequest{
|
||||
Stage: bizdev.StageV10_Quoting,
|
||||
ProjectID: 1,
|
||||
ProjectName: "内窥镜手术动力系统",
|
||||
CustomerName: "北京天衡医疗",
|
||||
DeviceType: "有源手术动力装置",
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
// Package bootstrap 承担实例启动所有权(KTD17 启动顺序中的
|
||||
// “取得本实例启动所有权”:身份标记、单启锁与可写根校验)。
|
||||
// 失败一律关闭:身份不符、锁被占、目录异常都拒绝启动。
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"chat-agent-system/internal/config"
|
||||
)
|
||||
|
||||
// InstanceMarker 是数据根内的实例身份标记:防止把别的实例的数据目录
|
||||
// 当作自己的空库/新实例使用(KTD18 instance marker 语义)。
|
||||
type InstanceMarker struct {
|
||||
InstanceID string `json:"instance_id"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
Version int `json:"marker_version"`
|
||||
}
|
||||
|
||||
const markerVersion = 1
|
||||
|
||||
// InstanceOwnership 是启动所有权的持有凭据:进程存活期间保持锁。
|
||||
// Release 由调用方在进程退出路径调用(通常无需主动:进程退出自动释放)。
|
||||
type InstanceOwnership struct {
|
||||
InstanceID string
|
||||
DataDir string
|
||||
lockFile *os.File
|
||||
}
|
||||
|
||||
func (o *InstanceOwnership) Release() {
|
||||
if o != nil && o.lockFile != nil {
|
||||
_ = syscall.Flock(int(o.lockFile.Fd()), syscall.LOCK_UN)
|
||||
_ = o.lockFile.Close()
|
||||
o.lockFile = nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureInstance 校验并取得实例启动所有权:
|
||||
// 1. 归一化可写根并校验重叠/合法性;
|
||||
// 2. 数据根 instance-marker.json 建立或核对身份(不同实例拒绝,须显式迁移/收养);
|
||||
// 3. 数据根 instance.lock 非阻塞 flock,防止同机误重复启动(不依赖任何治理工具)。
|
||||
//
|
||||
// 只做本机文件级防护;跨主机共享可写文件树不在支持范围(KTD17)。
|
||||
func EnsureInstance(cfg *config.Config) (*InstanceOwnership, error) {
|
||||
if cfg == nil {
|
||||
return nil, fmt.Errorf("bootstrap: config required")
|
||||
}
|
||||
id := strings.TrimSpace(cfg.InstanceID)
|
||||
if id == "" {
|
||||
return nil, fmt.Errorf("bootstrap: INSTANCE_ID must not be empty")
|
||||
}
|
||||
if strings.ContainsAny(id, `/\:*?"<>| `) || id == "." || id == ".." {
|
||||
return nil, fmt.Errorf("bootstrap: INSTANCE_ID %q must not contain path/space characters", id)
|
||||
}
|
||||
|
||||
dataDir, err := filepath.Abs(cfg.DataDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bootstrap: resolve data dir: %w", err)
|
||||
}
|
||||
if dataDir == string(filepath.Separator) {
|
||||
return nil, fmt.Errorf("bootstrap: data dir must not be filesystem root")
|
||||
}
|
||||
if err := checkWritableRoot(dataDir, "data_dir"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v := strings.TrimSpace(cfg.VaultStateRoot); v != "" {
|
||||
vault, err := filepath.Abs(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bootstrap: resolve vault root: %w", err)
|
||||
}
|
||||
if containsPath(dataDir, vault) || containsPath(vault, dataDir) {
|
||||
return nil, fmt.Errorf("bootstrap: vault root %s and data dir %s must not overlap", vault, dataDir)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(dataDir, 0o755); err != nil {
|
||||
return nil, fmt.Errorf("bootstrap: create data dir: %w", err)
|
||||
}
|
||||
|
||||
if err := ensureMarker(dataDir, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lockPath := filepath.Join(dataDir, "instance.lock")
|
||||
lf, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0o600)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bootstrap: open instance lock: %w", err)
|
||||
}
|
||||
if err := syscall.Flock(int(lf.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil {
|
||||
lf.Close()
|
||||
return nil, fmt.Errorf("bootstrap: instance %q already running on this machine (lock %s held)", id, lockPath)
|
||||
}
|
||||
|
||||
return &InstanceOwnership{InstanceID: id, DataDir: dataDir, lockFile: lf}, nil
|
||||
}
|
||||
|
||||
func checkWritableRoot(abs, name string) error {
|
||||
// 符号链接解析后复核,防止用链接把两个实例指向同一物理根。
|
||||
real, err := filepath.EvalSymlinks(abs)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("bootstrap: eval %s: %w", name, err)
|
||||
}
|
||||
// 目录尚不存在:父目录必须可解析(MkdirAll 随后创建)
|
||||
parent := filepath.Dir(abs)
|
||||
if _, err := os.Stat(parent); err != nil {
|
||||
return fmt.Errorf("bootstrap: %s parent missing: %w", name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
fi, err := os.Stat(real)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bootstrap: stat %s: %w", name, err)
|
||||
}
|
||||
if !fi.IsDir() {
|
||||
return fmt.Errorf("bootstrap: %s %s is not a directory", name, abs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// containsPath 报告 a 是否等于 b 或包含 b(按路径组件)。
|
||||
func containsPath(a, b string) bool {
|
||||
if a == b {
|
||||
return true
|
||||
}
|
||||
return strings.HasPrefix(b, a+string(filepath.Separator))
|
||||
}
|
||||
|
||||
func ensureMarker(dataDir, id string) error {
|
||||
path := filepath.Join(dataDir, "instance-marker.json")
|
||||
raw, err := os.ReadFile(path)
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
m := InstanceMarker{InstanceID: id, CreatedAt: time.Now().UTC().Format(time.RFC3339), Version: markerVersion}
|
||||
b, _ := json.MarshalIndent(m, "", " ")
|
||||
if err := os.WriteFile(path, append(b, '\n'), 0o600); err != nil {
|
||||
return fmt.Errorf("bootstrap: write instance marker: %w", err)
|
||||
}
|
||||
return nil
|
||||
case err != nil:
|
||||
return fmt.Errorf("bootstrap: read instance marker: %w", err)
|
||||
}
|
||||
var m InstanceMarker
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
return fmt.Errorf("bootstrap: parse instance marker %s: %w", path, err)
|
||||
}
|
||||
if m.InstanceID != id {
|
||||
return fmt.Errorf("bootstrap: data dir %s belongs to instance %q, refusing to start as %q (adopt/migrate explicitly)", dataDir, m.InstanceID, id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"chat-agent-system/internal/config"
|
||||
)
|
||||
|
||||
func testCfg(t *testing.T, id, dataDir, vault string) *config.Config {
|
||||
t.Helper()
|
||||
return &config.Config{InstanceID: id, DataDir: dataDir, VaultStateRoot: vault}
|
||||
}
|
||||
|
||||
func TestEnsureInstanceCreatesMarkerAndLock(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cfg := testCfg(t, "alpha", filepath.Join(dir, "alpha"), "")
|
||||
own, err := EnsureInstance(cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("ensure: %v", err)
|
||||
}
|
||||
raw, err := os.ReadFile(filepath.Join(dir, "alpha", "instance-marker.json"))
|
||||
if err != nil {
|
||||
t.Fatalf("marker missing: %v", err)
|
||||
}
|
||||
if !strings.Contains(string(raw), "\"instance_id\": \"alpha\"") {
|
||||
t.Fatalf("marker content: %s", raw)
|
||||
}
|
||||
// 二次取得同实例:同进程内 flock 可重入(同 fd 语义),但模拟误重复
|
||||
// 启动需要另一个进程;此处验证所有权对象可用并释放。
|
||||
own.Release()
|
||||
}
|
||||
|
||||
func TestEnsureInstanceRejectsForeignMarker(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
alpha := filepath.Join(dir, "alpha")
|
||||
if _, err := EnsureInstance(testCfg(t, "alpha", alpha, "")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 复用同一 data dir 但自称另一实例:必须拒绝(KTD18 不自动认领)
|
||||
if _, err := EnsureInstance(testCfg(t, "beta", alpha, "")); err == nil {
|
||||
t.Fatal("foreign instance marker must be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureInstanceRejectsVaultOverlap(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
data := filepath.Join(dir, "data")
|
||||
vault := filepath.Join(data, "vault") // vault 在 data 内:非独立
|
||||
if _, err := EnsureInstance(testCfg(t, "gamma", data, vault)); err == nil {
|
||||
t.Fatal("vault inside data dir must be rejected")
|
||||
}
|
||||
vaultOut := filepath.Join(dir, "vault")
|
||||
if _, err := EnsureInstance(testCfg(t, "gamma", data, vaultOut)); err != nil {
|
||||
t.Fatalf("independent vault should pass: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureInstanceRejectsInvalidID(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
for _, bad := range []string{"", "a/b", "..", "a b", "."} {
|
||||
if _, err := EnsureInstance(testCfg(t, bad, filepath.Join(dir, "x"), "")); err == nil {
|
||||
t.Fatalf("invalid instance id %q must be rejected", bad)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureInstanceRejectsRootAsDataDir(t *testing.T) {
|
||||
if _, err := EnsureInstance(testCfg(t, "root-try", "/", "")); err == nil {
|
||||
t.Fatal("filesystem root as data dir must be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureInstanceDetectsDuplicateStart(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
data := filepath.Join(dir, "dup")
|
||||
own, err := EnsureInstance(testCfg(t, "dup", data, ""))
|
||||
if err != nil {
|
||||
t.Fatalf("first acquire: %v", err)
|
||||
}
|
||||
defer own.Release()
|
||||
// 同机第二次取得(独立 open file description):必须拒绝误重复启动
|
||||
if _, err := EnsureInstance(testCfg(t, "dup", data, "")); err == nil {
|
||||
t.Fatal("duplicate start must be detected and rejected")
|
||||
}
|
||||
}
|
||||
@@ -70,6 +70,7 @@ type IntentStepView struct {
|
||||
// TaskStarter starts a task run without exposing bizdev/stageflow request types to chat.
|
||||
type TaskStarter interface {
|
||||
CreateAndStart(ctx context.Context, runID string, record *taskrun.RunRecord, start TaskStartRequest, resumeDir string) (*taskrun.RunRecord, error)
|
||||
BindAssistantMsgID(ctx context.Context, runID string, assistantMsgID int64) error
|
||||
}
|
||||
|
||||
// TaskStartRequest carries the chat-owned fields needed to start or resume a run.
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -94,11 +95,44 @@ func (s *Session) LastAccessed() time.Time {
|
||||
return s.LastAccessedAt
|
||||
}
|
||||
|
||||
// SetProjectID 设置当前会话绑定的项目 ID。
|
||||
// SetProjectID 设置当前会话绑定的项目 ID,并同步协调器读取的 UserCtx.Project.ID。
|
||||
// 协调器派发任务只解析字符串项目 ID;只改会话字段会让对话注入和阶段门禁看到 0。
|
||||
func (s *Session) SetProjectID(pid int64) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.ProjectID = pid
|
||||
s.syncProjectIDLocked()
|
||||
}
|
||||
|
||||
// bindCoordinatorIdentity 在每轮派发前用已授权的会话身份覆盖协调器上下文。
|
||||
// 会话 ID 供工作区追溯;数字用户 ID 供阶段门禁。阶段键故意不收窄对话采集,
|
||||
// 个人会话与项目成员共享对话都进入任务上下文,只滤掉私密与噪声。
|
||||
func (s *Session) bindCoordinatorIdentity() {
|
||||
if s == nil || s.UserCtx == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.syncProjectIDLocked()
|
||||
if s.UserID > 0 && parseUserID(s.UserCtx.User.ID) <= 0 {
|
||||
s.UserCtx.User.ID = strconv.FormatInt(s.UserID, 10)
|
||||
}
|
||||
if s.ID != "" {
|
||||
s.UserCtx.ChatSessionID = s.ID
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) syncProjectIDLocked() {
|
||||
if s.UserCtx == nil {
|
||||
return
|
||||
}
|
||||
if s.ProjectID > 0 {
|
||||
s.UserCtx.Project.ID = strconv.FormatInt(s.ProjectID, 10)
|
||||
return
|
||||
}
|
||||
if _, err := strconv.ParseInt(strings.TrimSpace(s.UserCtx.Project.ID), 10, 64); err == nil {
|
||||
s.UserCtx.Project.ID = ""
|
||||
}
|
||||
}
|
||||
|
||||
// Project 读取当前会话绑定的项目 ID。
|
||||
@@ -465,6 +499,7 @@ func (s *Session) sendMessageInternal(ctx context.Context, userInput string, str
|
||||
streamer.EmitPhase("dialogue_generating", "正在整理回复...", "assistant", 0, 0, 40)
|
||||
}
|
||||
s.EnsureDocResolver()
|
||||
s.bindCoordinatorIdentity()
|
||||
coordRes, err := s.Coordinator.ProcessIntent(ctx, recRes, s.UserCtx)
|
||||
if err != nil {
|
||||
if streamer != nil {
|
||||
@@ -473,8 +508,12 @@ func (s *Session) sendMessageInternal(ctx context.Context, userInput string, str
|
||||
return nil, fmt.Errorf("coordination execution failed: %w", err)
|
||||
}
|
||||
|
||||
var startedRunID string
|
||||
if coordRes.PendingAction != nil {
|
||||
execResult, execErr := s.executeAction(ctx, coordRes.PendingAction, s.UserCtx)
|
||||
if execResult != nil {
|
||||
startedRunID = execResult.RunID
|
||||
}
|
||||
coordRes.NaturalReply = renderActionReply(coordRes.PendingAction, execResult, execErr)
|
||||
coordRes.PendingAction = nil
|
||||
}
|
||||
@@ -499,6 +538,8 @@ func (s *Session) sendMessageInternal(ctx context.Context, userInput string, str
|
||||
s.UserCtx = coordRes.UpdatedContext
|
||||
}
|
||||
|
||||
s.augmentWorkflowGuidance(ctx, userInput, coordRes)
|
||||
|
||||
if coordRes.ClearExpectation {
|
||||
_ = coordinator.ApplyContextUpdate(s.UserCtx, coordinator.ContextUpdate{
|
||||
ExpectedVersion: s.UserCtx.Version, SetPendingExpectation: true,
|
||||
@@ -534,6 +575,11 @@ func (s *Session) sendMessageInternal(ctx context.Context, userInput string, str
|
||||
asstMeta := map[string]any{
|
||||
"reply_source": replySrc,
|
||||
}
|
||||
if replySrc == coordinator.ReplySourceTemplate {
|
||||
if reason := strings.TrimSpace(coordRes.ReplyFallbackReason); reason != "" {
|
||||
asstMeta["fallback_reason"] = reason
|
||||
}
|
||||
}
|
||||
if s.ProjectID > 0 {
|
||||
asstMeta["project_id"] = s.ProjectID
|
||||
}
|
||||
@@ -592,8 +638,17 @@ func (s *Session) sendMessageInternal(ctx context.Context, userInput string, str
|
||||
asstMeta["client_turn_id"] = clientTurnID
|
||||
asstMeta["client_id"] = clientTurnID + ":assistant"
|
||||
}
|
||||
if startedRunID != "" {
|
||||
asstMeta["run_id"] = startedRunID
|
||||
if asstMeta["task_status"] == nil || asstMeta["task_status"] == "" {
|
||||
asstMeta["task_status"] = "running"
|
||||
}
|
||||
}
|
||||
|
||||
asstMsgID, _ := s.CtxMgr.RecordUserMessageWithID(ctx, s.ID, 0, "assistant", coordRes.NaturalReply, string(coordRes.Intent), string(recRes.Branch), asstMeta)
|
||||
if startedRunID != "" && asstMsgID > 0 && s.Actions != nil && s.Actions.TaskRuns != nil {
|
||||
_ = s.Actions.TaskRuns.BindAssistantMsgID(ctx, startedRunID, asstMsgID)
|
||||
}
|
||||
|
||||
// 异步记录问答切片到向量记忆库 (P2 检索增强,不阻塞流式或同步答复)
|
||||
sessID := s.ID
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user