diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9613183..0ca3123 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -116,6 +116,8 @@ jobs:
cache-dependency-path: web/package-lock.json
- name: Install frozen frontend dependencies
run: npm ci
+ - name: Audit production and development dependencies
+ run: npm audit --audit-level=high
- name: Run frontend tests
run: npm run test:run
- name: Build frontend
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7e7907e..95f079f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -84,6 +84,7 @@ data.
```bash
cd web
+npm audit --audit-level=high
npm run test:run
npm run build
```
@@ -136,6 +137,5 @@ Before requesting review, confirm:
## Reporting Security Issues
Do not publish exploitable details, credentials, or sensitive data in a public
-issue. Contact the repository owner privately with a minimal reproduction,
-affected versions, and impact. A dedicated security policy and private
-reporting channel are planned but are not yet configured.
+issue. Follow [SECURITY.md](SECURITY.md) to report a vulnerability without
+disclosing sensitive details.
diff --git a/CONTRIBUTING.zh-CN.md b/CONTRIBUTING.zh-CN.md
index d7bacc6..8c465ca 100644
--- a/CONTRIBUTING.zh-CN.md
+++ b/CONTRIBUTING.zh-CN.md
@@ -78,6 +78,7 @@ uv run python scripts/verify_repository_safety.py
```bash
cd web
+npm audit --audit-level=high
npm run test:run
npm run build
```
@@ -124,6 +125,6 @@ Pull Request 应包含:
## 报告安全问题
-不要在公开 Issue 中发布可利用细节、凭据或敏感数据。请私下联系仓库所有者,
-提供最小复现、受影响版本和影响范围。项目计划增加独立安全策略和私密报告渠道,
-但当前尚未配置。
+不要在公开 Issue 中发布可利用细节、凭据或敏感数据。请按
+[SECURITY.md](SECURITY.md) 的流程提交漏洞,在没有私密入口时只公开提出建立私密联系,
+不要附带复现细节。
diff --git a/DOCUMENT_INDEX.md b/DOCUMENT_INDEX.md
index 1be6969..2cea0e3 100644
--- a/DOCUMENT_INDEX.md
+++ b/DOCUMENT_INDEX.md
@@ -1,12 +1,12 @@
# OperCerta 文档总索引
-本索引完整登记 OperCerta 当前根工作树中的 124 份 Markdown 文档,并保留旧电脑 6 个 `.worktrees/` 的 456 条历史登记。当前根工作树与每个历史 worktree 使用独立六列表格和独立序号;路径均相对于仓库根目录;日期表示文档首次建立日期。历史 worktree 表用于追溯分支资料,不表示对应物理目录仍存在。Git 元数据、依赖目录、虚拟环境和工具缓存不属于项目文档登记范围。
+本索引完整登记 OperCerta 当前根工作树中的 129 份 Markdown 文档,并保留旧电脑 6 个 `.worktrees/` 的 456 条历史登记。当前根工作树与每个历史 worktree 使用独立六列表格和独立序号;路径均相对于仓库根目录;日期表示文档首次建立日期。历史 worktree 表用于追溯分支资料,不表示对应物理目录仍存在。Git 元数据、依赖目录、虚拟环境和工具缓存不属于项目文档登记范围。
Typora 显示:首次运行 `powershell -ExecutionPolicy Bypass -File scripts/install_typora_index_theme.ps1`,重启 Typora 后选择 `主题 → OperCerta Index`。该主题让正文使用 96% 窗口宽度,并统一设置下列全部六列表格的列宽、自动换行和字号。
## 项目核心学习导航
-先按 A1–A9 完成一轮“阅读 → 找到代码 → 手动验证 → 自己复述”。后面的 124 份当前文档与 456 条历史 worktree 登记是排查问题和深入学习时使用的资料库,不需要从头到尾顺序阅读。
+先按 A1–A9 完成一轮“阅读 → 找到代码 → 手动验证 → 自己复述”。后面的 129 份当前文档与 456 条历史 worktree 登记是排查问题和深入学习时使用的资料库,不需要从头到尾顺序阅读。
| 阶段 | 核心主题 | 优先阅读 | 代码与配置入口 | 必做实践 | 掌握标准 |
| ---: | --- | --- | --- | --- | --- |
@@ -18,17 +18,17 @@ Typora 显示:首次运行 `powershell -ExecutionPolicy Bypass -File scripts/i
| A6 | 审批、安全边界与可靠性内核 | [审批原子性](docs/release-evidence/approval-atomicity.md)、[幂等工单](docs/release-evidence/work-order-idempotency.md)、[重启恢复](docs/release-evidence/langgraph-restart-recovery.md)、[批准后 Verifier](docs/release-evidence/agent-verifier-reapproval.md) | [审批仓储](src/opercerta/infrastructure/db/approval_repository.py)、[工单仓储](src/opercerta/infrastructure/db/work_order_repository.py)、[Checkpoint](src/opercerta/infrastructure/checkpoints.py)、[工具策略](src/opercerta/agent/tool_policy.py) | 复现一次重复审批或重启恢复,随后核对数据库事实、审计序列和唯一工单。 | 能用数据库约束、事务、状态机和幂等键解释为什么不会因模型重试产生重复副作用。 |
| A7 | React 前端、FastAPI 与身份权限 | [控制台证据](docs/release-evidence/single-page-console.md)、[JWT/RBAC 证据](docs/release-evidence/demo-jwt-rbac.md) | [React 控制台](web/src/App.tsx)、[API 应用](src/opercerta/api/app.py)、[认证授权](src/opercerta/api/auth.py)、[请求模型](src/opercerta/api/models.py) | 从浏览器完成一个业务闭环,同时在 Network 与后端 Trace 中对应每个请求、角色切换和状态更新。 | 能解释 React 只负责交互状态,FastAPI 负责协议与鉴权,Agent 内核负责决策编排,PostgreSQL 保存权威事实。 |
| A8 | PostgreSQL、Redis、Docker 与可观测性 | [三业务发布证据](docs/release-evidence/three-business-release.md)、[Docker 证据](docs/release-evidence/docker-linux-runtime.md)、[可观测性证据](docs/release-evidence/observability-security-regression.md) | [Compose](compose.yaml)、[Dockerfile](Dockerfile)、[Redis 缓存](src/opercerta/infrastructure/cache.py)、[数据库迁移](migrations)、[Tracing](src/opercerta/observability/tracing.py) | 执行健康检查、查看容器状态、重启 API/MCP,并确认 checkpoint、业务事实和工单没有丢失或重复。 | 能解释容器与 Compose 的区别、Redis 为什么不是权威存储、PostgreSQL/pgvector 的双重职责及日志如何安全关联。 |
-| A9 | 本人掌握、故障复盘与演示 | [项目所有者掌握验收](docs/learning/opercerta-ownership-acceptance.md)、[工程案例集](docs/development-log/interview-casebook.md)、[最新开发日志](docs/development-log/daily/2026-07-31.md) | 选择一次真实业务闭环和一个真实故障对应的代码、日志、数据库事实与修复提交。 | 独立执行完整库存闭环、重启/幂等实验和 30 秒/3 分钟/10 分钟讲解,保存 operation/work order/Trace/审计证据并录制 3–5 分钟视频。 | 不看稿也能操作、定位代码、解释取舍和失败收口;验收不得由 Codex 自动代签。 |
+| A9 | 本人掌握、故障复盘与演示 | [项目所有者掌握验收](docs/learning/opercerta-ownership-acceptance.md)、[工程案例集](docs/development-log/interview-casebook.md)、[最新开发日志](docs/development-log/daily/2026-08-11.md) | 选择一次真实业务闭环和一个真实故障对应的代码、日志、数据库事实与修复提交。 | 独立执行完整库存闭环、重启/幂等实验和 30 秒/3 分钟/10 分钟讲解,保存 operation/work order/Trace/审计证据并录制 3–5 分钟视频。 | 不看稿也能操作、定位代码、解释取舍和失败收口;验收不得由 Codex 自动代签。 |
-## 根工作树(124 份)
+## 根工作树(129 份)
显示说明:本表及后续各 worktree 表均保持“序号、文件名、路径、用途、状态、日期”六列完整字段。
| 序号 | 文件名 | 路径 | 用途(详细) | 状态 | 日期 |
| ---: | --- | --- | --- | --- | --- |
| 1 | `README.md` | `README.md` | 英文项目总入口,使用顶部 `English|简体中文` 标准链接切换语言,说明业务背景、三业务功能、Agent 闭环、完整技术栈、快速启动、使用流程、验证结果、可靠性边界和路线图。 | 每页只显示一种语言;不使用折叠框 | 2026-07-30 |
-| 2 | `IMPLEMENTATION_HANDOFF.md` | `IMPLEMENTATION_HANDOFF.md` | 跨对话和上下文压缩后的实施交接文件,记录当前分支、已验证事实、未完成事项、下一步动作及禁止越过的发布边界。 | PR #23/main 五项门禁已收口;Showcase 待本人验收,Product gate 关闭 | 2026-07-30 |
-| 3 | `DOCUMENT_INDEX.md` | `DOCUMENT_INDEX.md` | OperCerta 全部项目文档的唯一总登记表,用于按文件名、路径、用途、状态和日期统一检索、复查与交接。 | 当前根工作树 124 份文档已完整登记;另保留 6 个旧 worktree 的 456 条历史记录 | 2026-08-02 |
+| 2 | `IMPLEMENTATION_HANDOFF.md` | `IMPLEMENTATION_HANDOFF.md` | 跨对话和上下文压缩后的实施交接文件,记录当前分支、已验证事实、未完成事项、下一步动作及禁止越过的发布边界。 | Draft PR #26 四项通过;等待批准合并与 main Compose,Showcase 待本人验收,Product gate 关闭 | 2026-08-11 |
+| 3 | `DOCUMENT_INDEX.md` | `DOCUMENT_INDEX.md` | OperCerta 全部项目文档的唯一总登记表,用于按文件名、路径、用途、状态和日期统一检索、复查与交接。 | 当前根工作树 129 份文档已完整登记;另保留 6 个旧 worktree 的 456 条历史记录 | 2026-08-11 |
| 4 | `2026-07-14-agent-project-naming-design.md` | `docs/specs/2026-07-14-agent-project-naming-design.md` | 定义 OperCerta、ForenTrail、SiteVerum、Federune 四个项目的命名原则、语义边界与品牌一致性,防止项目职责和名称漂移。 | 已冻结为命名基线 | 2026-07-14 |
| 5 | `ai-agent-portfolio-overall-design.md` | `docs/specs/ai-agent-portfolio-overall-design.md` | 规定四个 AI Agent 项目的整体定位、差异化业务范围、技术能力组合、实施顺序和共同约束,是项目组合的最高层设计依据。 | 已冻结为总体设计基线;文件名已统一为英文路径 | 2026-07-14 |
| 6 | `2026-07-14-agent-portfolio-design.md` | `docs/specs/2026-07-14-agent-portfolio-design.md` | 设计四项目如何组合成求职作品集,包括能力覆盖、展示顺序、共享基础设施边界和避免重复建设的原则。 | 已冻结为组合设计基线 | 2026-07-14 |
@@ -68,7 +68,7 @@ Typora 显示:首次运行 `powershell -ExecutionPolicy Bypass -File scripts/i
| 40 | `2026-07-20-opercerta-three-business-release.md` | `docs/superpowers/plans/2026-07-20-opercerta-three-business-release.md` | 将三业务适配、六个 MCP 工具、评测、Redis、OpenTelemetry、真实模型代表验证、本地发布和中文学习包拆成主线任务。 | 本地任务已执行;公网交互、用户掌握与最终门禁待完成 | 2026-07-20 |
| 41 | `2026-07-20-opercerta-zero-cost-showcase-engineering-walkthrough.md` | `docs/superpowers/plans/2026-07-20-opercerta-zero-cost-showcase-engineering-walkthrough.md` | 规划统一事实清单、招聘专题、本地工程详解、控制台共存、响应式测试、静态导出和 Netlify 发布验证。 | 计划已创建;根分支尚未归档完成证据 | 2026-07-20 |
| 42 | `README.md` | `docs/development-log/README.md` | 说明开发日志目录结构、各类日志职责、记录规范、敏感信息禁区和上下文恢复阅读顺序。 | 已建立并持续使用 | 2026-07-15 |
-| 43 | `current-state.md` | `docs/development-log/current-state.md` | 保存最新可验证项目状态、测试证据、运行环境、发布边界、未完成事项和下一步,作为压缩上下文后的唯一当前事实入口;历史状态只保留在 daily 与 release evidence。 | main `7bb9ecd` 五项门禁通过;Showcase 待本人验收;Product gate 关闭 | 2026-07-30 |
+| 43 | `current-state.md` | `docs/development-log/current-state.md` | 保存最新可验证项目状态、测试证据、运行环境、发布边界、未完成事项和下一步,作为压缩上下文后的唯一当前事实入口;历史状态只保留在 daily 与 release evidence。 | main `c4257ac` Compose 构建失败已在收口分支修复;工程门禁复验中,Showcase 待本人验收;Product gate 关闭 | 2026-08-10 |
| 44 | `2026-07-15.md` | `docs/development-log/daily/2026-07-15.md` | 记录日志体系、Windows PostgreSQL、审批领域契约及初始可靠性实施过程与命令证据。 | 当日记录已归档 | 2026-07-15 |
| 45 | `2026-07-16.md` | `docs/development-log/daily/2026-07-16.md` | 记录库存补货 Task 1–9、幂等工单、LangGraph 恢复、调试过程和本地验证结果。 | 当日记录已归档 | 2026-07-16 |
| 46 | `2026-07-17.md` | `docs/development-log/daily/2026-07-17.md` | 记录 WSL2、Docker Linux 运行时、JWT/RBAC 和 Compose 环境迁移中的问题、修复与验证。 | 当日记录已归档 | 2026-07-17 |
@@ -82,7 +82,7 @@ Typora 显示:首次运行 `powershell -ExecutionPolicy Bypass -File scripts/i
| 54 | `2026-07-16-risk-based-review-and-progress-control.md` | `docs/development-log/decisions/2026-07-16-risk-based-review-and-progress-control.md` | 规定风险分级复核、进度百分比口径、关键节点审批和优先完成纵向业务闭环的项目控制方法。 | 已采用 | 2026-07-16 |
| 55 | `interview-casebook.md` | `docs/development-log/interview-casebook.md` | 将真实实施问题整理为面试案例,按现象、根因、诊断、修复、验证和经验总结记录可讨论的工程话题;覆盖 Agent 契约、数据库可靠性、跨平台环境、供应链和发布证据边界。 | 已新增 `.gitattributes` 与既有 CRLF 的差异、冷构建和候选镜像分层举证案例 | 2026-07-17 |
| 56 | `learning-method.md` | `docs/development-log/learning-method.md` | 规定源码阅读、手动命令、单变量实验、复述、故障演练和面试问答结合的项目掌握方法。 | 已加入三业务学习与手动闭环建议;需持续实践 | 2026-07-17 |
-| 57 | `opercerta-core-technical-guide.md` | `docs/learning/opercerta-core-technical-guide.md` | 从一次业务请求出发解释 FastAPI、LangGraph、MCP、PostgreSQL、审批、幂等、恢复、容器和可观测性的代码链路与底层思想。 | 本地学习材料已完成;文件名已统一为英文路径,需结合源码实践 | 2026-07-20 |
+| 57 | `opercerta-core-technical-guide.md` | `docs/learning/opercerta-core-technical-guide.md` | 从一次业务请求出发解释 FastAPI、LangGraph、MCP、PostgreSQL、审批、幂等、恢复、容器和可观测性的代码链路与底层思想;集中说明 Prompt、最小上下文、受控工具调用、单根任务编排、四类记忆、Eval、约束与失败恢复。 | 已同步当前有界 ReAct 风格 Agent Harness、9/9 Kimi 评测和已知预算/并行留白;仍需本人源码实践 | 2026-08-10 |
| 58 | `opercerta-manual-experiment-guide.md` | `docs/learning/opercerta-manual-experiment-guide.md` | 提供 WSL2/Compose 启停、三业务操作、数据库核对、MCP 调用、故障注入和恢复验证的可复制命令与观察点。 | 已同步 signal → case → Agent 调查主流程和根工作树/web 路径;用户掌握检查待继续 | 2026-07-20 |
| 59 | `opercerta-interview-guide.md` | `docs/learning/opercerta-interview-guide.md` | 提供 30 秒、3 分钟、10 分钟项目介绍、架构解释、技术取舍、可靠性证据和常见追问回答框架。 | 已统一最终 CI、Real Kimi、Showcase 版本和简历表述;需用户口述演练和源码掌握 | 2026-07-20 |
| 60 | `demo-script.md` | `docs/demo-script.md` | 规定面向 HR 与面试官的展示顺序、演示前检查、业务闭环操作、异常备用方案和诚实边界。 | 已同步扫描异常、case 调查、审批、Verifier 与幂等工单流程;待用户实演 | 2026-07-18 |
@@ -90,7 +90,7 @@ Typora 显示:首次运行 `powershell -ExecutionPolicy Bypass -File scripts/i
| 62 | `cache-tracing-model-adapter.md` | `docs/release-evidence/cache-tracing-model-adapter.md` | 保存 Redis 只读缓存、审批后绕过、OpenTelemetry 脱敏关联、严格真实模型适配器及未验证边界的阶段证据。 | 阶段证据已归档;真实模型另有独立证据 | 2026-07-20 |
| 63 | `demo-jwt-rbac.md` | `docs/release-evidence/demo-jwt-rbac.md` | 保存本地 JWT 签发、四角色权限矩阵、审批身份绑定、安全拒绝和 Compose 回归结果。 | 本地验证通过;不代表生产身份系统 | 2026-07-18 |
| 64 | `docker-linux-runtime.md` | `docs/release-evidence/docker-linux-runtime.md` | 保存 WSL2 Ubuntu 下 Compose 构建、服务健康、数据库初始化、业务 smoke 和重启恢复的实际输出。 | 单节点本地验证通过;发布门禁仍关闭 | 2026-07-17 |
-| 65 | `github-actions-ci.md` | `docs/release-evidence/github-actions-ci.md` | 保存 GitHub PR/main Actions、后端与前端测试、PostgreSQL/Compose smoke、仓库可见性变化和分支保护能力核验。 | 已追加 PR #23、main run 30629194460、671/60/9-of-9、uv 0.11.28 镜像构建与 Compose 重启恢复;main 保护尚未配置 | 2026-07-30 |
+| 65 | `github-actions-ci.md` | `docs/release-evidence/github-actions-ci.md` | 保存 GitHub PR/main Actions、后端与前端测试、PostgreSQL/Compose smoke、仓库可见性变化和分支保护能力核验。 | 已追加 PR #25/main run 30744845403 的四项通过、Compose clean-build 失败及收口分支本地修复;等待新 main CI | 2026-08-10 |
| 66 | `inventory-replenishment-vertical-slice.md` | `docs/release-evidence/inventory-replenishment-vertical-slice.md` | 汇总库存查询、规则判断、审批、执行、唯一工单、审计和 API 的首条端到端后端闭环证据。 | Windows 本地后端闭环已验证;发布门禁仍关闭 | 2026-07-16 |
| 67 | `langgraph-restart-recovery.md` | `docs/release-evidence/langgraph-restart-recovery.md` | 保存 LangGraph 四点 A/B 重启测试、checkpointer 状态、审批中断恢复和副作用不重复的数据库断言。 | 本地恢复证据已归档;不代表生产高可用 | 2026-07-16 |
| 68 | `native-postgres-environment.md` | `docs/release-evidence/native-postgres-environment.md` | 保存 Windows 原生 PostgreSQL 版本、服务、数据库、角色、认证规则和连接验证结果。 | 历史环境证据已归档;当前主运行时已迁移 | 2026-07-15 |
@@ -150,6 +150,11 @@ Typora 显示:首次运行 `powershell -ExecutionPolicy Bypass -File scripts/i
| 122 | `opercerta-ownership-acceptance.md` | `docs/learning/opercerta-ownership-acceptance.md` | 规定项目所有者必须亲自完成的环境检查、库存完整闭环、源码链路、重启/幂等实验及 30 秒/3 分钟/10 分钟讲解,并记录 operation、work order、Trace、审计和数据库事实。 | `AWAITING_OWNER_VALIDATION`;不得由 Codex 自动代签 | 2026-07-31 |
| 123 | `real-model-quality-evaluation.md` | `docs/release-evidence/real-model-quality-evaluation.md` | 保存 Kimi K2.6 三业务 9 条冻结真实模型路径的 Goal、工具选择、证据、citation、提示注入、审批、幂等写入、数据库副作用和端到端延迟实测,并记录知识前置、确定性证据绑定、信号恢复与 Docker 构建问题的 TDD 收口过程。 | 本地固定小样本 9/9;不代表生产准确率、真实流量、成本或 SLA | 2026-08-02 |
| 124 | `2026-08-02.md` | `docs/development-log/daily/2026-08-02.md` | 记录真实模型评测数据集、运行器、指标、RED/GREEN 修复、Docker 网络恢复、最终 9/9 结果和下一步个人掌握验收,用于后续复盘和面试交流。 | 当日评测工作已归档;Showcase 仍待本人验收与录屏 | 2026-08-02 |
+| 125 | `SECURITY.md` | `SECURITY.md` | 定义 pre-1.0 支持范围、GitHub 私密漏洞报告优先流程、无私密入口时的安全联系方法,以及静态公开页面、本地 demo JWT 和生产安全评审的边界。 | 已建立可执行安全报告策略;不承诺非生产项目响应 SLA | 2026-08-10 |
+| 126 | `2026-08-10-final-closeout-audit.md` | `docs/development-log/audits/2026-08-10-final-closeout-audit.md` | 对照四份基础设计和全部已批准修订,建立规格—实现—证据矩阵,记录 main CI 漂移、Agent 术语、前端数字、供应链和开源安全问题及最终三类门禁。 | 自动可修缺口已处理;等待完整复验、远程 CI 和本人验收 | 2026-08-10 |
+| 127 | `2026-08-10.md` | `docs/development-log/daily/2026-08-10.md` | 记录最新 main 盘点、独立工作树保护、Compose CI 根因、Dockerfile RED/GREEN、传递依赖升级、Harness 文档和发布边界,供后续故障复盘。 | 当日工程修改已记录;最终测试结果另见 release evidence | 2026-08-10 |
+| 128 | `final-closeout-2026-08-10.md` | `docs/release-evidence/final-closeout-2026-08-10.md` | 汇总最终收口分支的静态检查、后端、评测、前端依赖审计/测试/构建、Docker/Compose 业务副作用与恢复结果,并区分本地证据和待补的远程 main 证据。 | 本地门禁全绿;远程 main CI、本人验收和最终 tag 待完成 | 2026-08-10 |
+| 129 | `2026-08-11.md` | `docs/development-log/daily/2026-08-11.md` | 记录获批推送、Draft PR #26 创建、GitHub 连接器 403 后的 CLI 后备路径、四项 PR 门禁结果以及 main-only Compose 和后续人工审批边界。 | PR 四项通过;保持 Draft,等待批准合并和 main Compose | 2026-08-11 |
## 历史 Worktree:agent-core-architecture(82 条记录)
diff --git a/Dockerfile b/Dockerfile
index 804af07..a87b92d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -9,7 +9,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \
COPY src ./src
RUN --mount=type=cache,target=/root/.cache/uv \
- uv sync --frozen --no-dev --offline
+ uv sync --frozen --no-dev
COPY migrations ./migrations
COPY data ./data
diff --git a/IMPLEMENTATION_HANDOFF.md b/IMPLEMENTATION_HANDOFF.md
index bfb67e8..9cfb03a 100644
--- a/IMPLEMENTATION_HANDOFF.md
+++ b/IMPLEMENTATION_HANDOFF.md
@@ -1,10 +1,10 @@
# OperCerta 实施交接
-> 更新时间:2026-07-31。先读 `DOCUMENT_INDEX.md` 的核心阅读表,再读本文件和 `docs/development-log/current-state.md`。
+> 更新时间:2026-08-10。先读 `DOCUMENT_INDEX.md` 的核心阅读表,再读本文件和 `docs/development-log/current-state.md`。
## 当前交接结论
-OperCerta 的代码与自动化主线已经完成。双门禁与本人掌握收口经 PR #23 合入 main `7bb9ecda8170ed8752049331f5597ea2368d77b1`;main Actions run `30629194460` 五项全绿,包含后端 `671 passed`、前端 19 文件/60 条、三业务固定契约 42/42、Agent 安全恢复 9/9,以及干净构建 uv `0.11.28` 镜像后的真实 Compose 三业务数据库副作用与 API/MCP 重启恢复。
+OperCerta 的三业务、Agent 主线、真实模型固定评测和本地运行能力已经合入 main `c4257ac10dec470bbb9ca1076c4cfacfb21a80de`(PR #25)。该 main run `30744845403` 中仓库安全、Python 质量、后端 682 条及两套评测、前端 19 文件/62 条均通过;`compose-smoke` 因 Dockerfile 在全新 runner 上离线安装项目时缺少 PEP 517 `hatchling` 缓存而失败。修复分支已推送并创建 Draft PR #26;初始 PR run `31479724666` 四项全部通过,Compose 按 PR 事件规则跳过。本地 clean Docker build、三业务副作用和 API/MCP 重启恢复也已通过;仍需合并后的 main-only Compose run 才能称为总门禁重新全绿。
本版本发布定义已经正式修订为:
@@ -27,17 +27,19 @@ OperCerta 的代码与自动化主线已经完成。双门禁与本人掌握收
## 本轮收口修改
-- 新增 Apache-2.0 `LICENSE`。
-- Dockerfile 的 uv 固定为本地/CI 同版 `0.11.28`。
-- 仓库安全扫描覆盖双语 README、双语 CONTRIBUTING、handoff 和文档索引。
-- 新增双门禁修订规格、实施计划和本人掌握验收表。
-- 当前状态不再堆叠历史快照;历史仍保存在 daily 与 release evidence。
+- 修复 clean-runner Docker build:保持运行依赖 `--frozen`,但不再假设构建后端已经存在于 uv cache。
+- 升级前端锁文件中的受影响传递依赖,并在 CI/贡献指南加入 `npm audit --audit-level=high`。
+- 新增 `SECURITY.md`,明确安全报告入口和非生产边界。
+- 统一当前实现为“有界 ReAct 风格 Tool Loop”,不再误报为严格 Plan-and-Execute。
+- 同步 682 条后端、当前收口分支 63 条前端、42/42、9/9 Agent、9/9 Kimi 真实模型固定路径等已验证数字及其样本边界。
+- 补充完整 Agent Harness 技术说明和 2026-08-10 规格/发布审计。
## 下一执行顺序
-1. 由项目所有者亲自执行 `docs/learning/opercerta-ownership-acceptance.md`;必须记录 `operation_id`、`work_order_id`、Trace、审计序列和数据库事实,**不得由 Codex 自动代签**。
-2. 完成 3–5 分钟录屏与口述复盘。
-3. 人工证据通过后,把 Showcase 门禁改为 `PASSED`,创建最终 tag 并记录回滚提交。
+1. 项目所有者复核 Draft PR #26 后批准转为 Ready 并合并;取得新的 main 五项全绿证据。
+2. 由项目所有者亲自执行 `docs/learning/opercerta-ownership-acceptance.md`;必须记录 `operation_id`、`work_order_id`、Trace、审计序列和数据库事实,**不得由 Codex 自动代签**。
+3. 完成 3–5 分钟录屏与口述复盘。
+4. 人工证据通过后,把 Showcase 门禁改为 `PASSED`,创建最终 tag 并记录回滚提交。
## 本地运行入口
@@ -66,6 +68,7 @@ uv run python scripts/run_agent_evaluation.py
cd web
npm ci
+npm audit --audit-level=high
npm run test:run
npm run build
```
diff --git a/README.md b/README.md
index da57d7d..e9c9bc9 100644
--- a/README.md
+++ b/README.md
@@ -74,7 +74,7 @@ flowchart LR
UI["React console"] --> API["FastAPI boundary"]
API --> SIGNAL["Deterministic signal scan"]
SIGNAL --> GOAL["Typed goal encoding"]
- GOAL --> GRAPH["LangGraph plan-and-execute loop"]
+ GOAL --> GRAPH["LangGraph bounded ReAct-style tool loop"]
GRAPH --> LLM["LLM reasoning"]
LLM --> POLICY["Tool policy and harness"]
POLICY --> MCP["FastMCP read tools"]
@@ -180,11 +180,11 @@ provider; it is not a production-accuracy, traffic, cost, or SLA claim.
| Gate | Current verified result |
| --- | ---: |
| Backend suite | 682 tests passed |
-| Frontend suite | 19 test files, 60 tests passed |
+| Frontend suite | 19 test files, 63 tests passed |
| Three-business fixed contracts | 42/42 passed |
| Frozen Agent safety and recovery evaluation | 9/9 passed |
| Kimi K2.6 real-model quality evaluation | 9/9 passed; injection 3/3; zero unauthorized calls, approval bypasses, or duplicate work orders |
-| Main Compose smoke | Build, business database effects, API/MCP restart, recovery, and cleanup passed |
+| Closeout-branch local Compose smoke | Clean build, business database effects, API/MCP restart, recovery, and cleanup passed; new main CI pending |
Run the local gates:
@@ -200,6 +200,7 @@ bash scripts/run_real_model_quality_evaluation.sh
cd web
npm ci
+npm audit --audit-level=high
npm run test:run
npm run build
```
@@ -253,12 +254,14 @@ Completed locally:
- three bounded business workflows and the shared Agent loop;
- approval binding, revalidation, idempotent writes, and restart recovery;
- real PostgreSQL/pgvector, Redis, FastMCP, FastEmbed retrieval, and React console;
-- fixed contract/evaluation suites and main-branch Compose recovery evidence;
+- fixed contract/evaluation suites and closeout-branch local Compose recovery evidence;
- read-only public project page and a reproducible pre-release.
Current gate state:
- engineering and local automated gate: `PASSED`;
+- remote CI gate: `PR_CHECKS_PASSED_MAIN_PENDING`; PR #26 passed all four PR
+ jobs, while the main-only Compose smoke still requires a post-merge run;
- Showcase Release gate: `AWAITING_OWNER_VALIDATION` until the owner completes
the manual walkthrough, source explanation, recovery/idempotency experiment,
and 3–5 minute recording;
diff --git a/README.zh-CN.md b/README.zh-CN.md
index b0207e7..2434c4d 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -68,7 +68,7 @@ flowchart LR
UI["React 控制台"] --> API["FastAPI 边界"]
API --> SIGNAL["确定性异常扫描"]
SIGNAL --> GOAL["类型化目标编码"]
- GOAL --> GRAPH["LangGraph 规划与执行循环"]
+ GOAL --> GRAPH["LangGraph 有界 ReAct 风格工具循环"]
GRAPH --> LLM["LLM 推理"]
LLM --> POLICY["工具策略与 Harness"]
POLICY --> MCP["FastMCP 只读工具"]
@@ -167,11 +167,11 @@ Goal、工具、证据、审批和数据库副作用契约,不代表生产准
| 门禁 | 当前已验证结果 |
| --- | ---: |
| 后端测试 | 682 条通过 |
-| 前端测试 | 19 个测试文件、60 条用例通过 |
+| 前端测试 | 19 个测试文件、63 条用例通过 |
| 三业务固定契约 | 42/42 通过 |
| 冻结 Agent 安全与恢复评测 | 9/9 通过 |
| Kimi K2.6 真实模型质量评测 | 9/9 通过;提示注入 3/3;未授权调用、审批绕过、重复工单均为 0 |
-| main Compose smoke | 构建、业务数据库副作用、API/MCP 重启、恢复和清理通过 |
+| 收口分支本地 Compose smoke | 干净构建、业务数据库副作用、API/MCP 重启、恢复和清理通过;等待新 main CI |
运行本地门禁:
@@ -187,6 +187,7 @@ bash scripts/run_real_model_quality_evaluation.sh
cd web
npm ci
+npm audit --audit-level=high
npm run test:run
npm run build
```
@@ -239,12 +240,13 @@ docs/ 技术指南、开发记录和发布证据
- 三条受控业务流程和共享 Agent Loop;
- 审批绑定、重新取证、幂等写入和重启恢复;
- 真实 PostgreSQL/pgvector、Redis、FastMCP、FastEmbed 检索和 React 控制台;
-- 固定契约/评测以及 main 分支 Compose 恢复证据;
+- 固定契约/评测以及收口分支本地 Compose 恢复证据;
- 只读公开项目页面和可复现预发布版本。
当前门禁状态:
- 工程与本地自动化门禁:`PASSED`;
+- 远程 CI 门禁:`PR_CHECKS_PASSED_MAIN_PENDING`;PR #26 四项 PR job 已通过,main-only Compose smoke 仍需合并后运行;
- Showcase Release gate:`AWAITING_OWNER_VALIDATION`,必须由项目所有者亲自完成
业务实演、源码讲解、恢复/幂等实验和 3–5 分钟录屏;
- Product Release gate:`CLOSED`。
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..bbefe17
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,33 @@
+# Security Policy
+
+## Supported Versions
+
+OperCerta is a pre-1.0 project. Security fixes are applied to the latest `main`
+branch only. Historical tags and preview deployments are not maintained as
+supported production releases.
+
+## Reporting a Vulnerability
+
+Do not disclose credentials, exploitable details, private endpoints, customer
+data, or a working exploit in a public issue or pull request.
+
+1. Prefer GitHub's **Security → Report a vulnerability** flow when it is
+ available for this repository.
+2. Include the affected revision, impact, prerequisites, and the smallest safe
+ reproduction. Redact secrets and personal or business data.
+3. If private vulnerability reporting is not available, open a public issue
+ that contains only the title `Security contact requested`. Do not include
+ technical details; wait for the maintainer to establish a private channel.
+
+The maintainer will acknowledge a valid private report, assess severity, and
+coordinate a fix and disclosure. No response-time SLA is promised for this
+non-production project.
+
+## Security Boundary
+
+The public Netlify site is a read-only static showcase. The complete Agent MVP
+runs locally and uses demo JWT identities and synthetic data. It must not be
+treated as a production identity, public API, or repository for confidential
+information. Production deployment requires a separate security review for
+identity, secrets, ingress, rate limiting, backups, monitoring, and incident
+response.
diff --git a/docs/development-log/audits/2026-08-10-final-closeout-audit.md b/docs/development-log/audits/2026-08-10-final-closeout-audit.md
new file mode 100644
index 0000000..4e16abb
--- /dev/null
+++ b/docs/development-log/audits/2026-08-10-final-closeout-audit.md
@@ -0,0 +1,54 @@
+# OperCerta 最终收口审计(2026-08-10)
+
+## 审计范围与优先级
+
+本次审计覆盖四份 `docs/specs/` 基线设计、后续已批准的 `docs/superpowers/specs/` 修订、对应实施计划、当前源码、测试、评测、Compose、公开静态专题和发布证据。发生冲突时按“后批准、范围更具体的修订规格优先”解释;历史设计原文不回写,以保留决策演进。
+
+当前版本仍定义为:
+
+> **公开静态展示 + 本地可复现完整 Agent MVP + 3–5 分钟录屏。**
+
+## 规格—实现—证据矩阵
+
+| 规格目标 | 当前实现 | 自动化或文档证据 | 结论 |
+| --- | --- | --- | --- |
+| 只实施 OperCerta,不复用旧公司材料 | 仓库只包含 OperCerta 源码与合成业务数据 | `verify_repository_safety.py`、四份基础设计 | 符合 |
+| 库存、设备、任务三业务闭环 | signal → case → Agent 调查 → 审批 → 复核 → 幂等工单 | 42/42 三业务契约、Compose 数据库断言 | 符合 |
+| 异常先被合理发现,再启动 Agent | 确定性 signal scanner 读取事实与规则,创建/对账 signal;用户从 case 启动调查 | signal inbox、reconciliation 和 successor 测试/证据 | 符合 |
+| 有限业务输入,不开放无边界聊天 | React 表单与 FastAPI/Pydantic 形成可信 `IntentEnvelope` | API、前端与非法输入测试 | 符合 |
+| LLM 是认知层但不是写入权威 | Goal 编码、有界 ReAct 风格 Tool Loop、FinalAnalysis、批准后 Verifier | Agent Trace、9 条冻结轨迹、9 条 Kimi 固定路径 | 符合;已纠正旧“严格 Plan-and-Execute”表述 |
+| LangGraph 承担 Agent 编排 | 单一 `ControlledAgentRootGraph` 负责循环、interrupt、checkpoint、resume 和 recovery | checkpoint/重启恢复测试与 Compose recovery-only | 符合 |
+| MCP/Skill/Function Calling | 模型经 LangChain 原生 tool calling 选择 5 个只读 MCP 工具;写工具只由代码节点调用 | ToolPolicy、ToolExecutor、FastMCP 集成测试 | 符合 |
+| RAG 与长期知识 | FastEmbed + pgvector 检索按场景、版本和有效状态过滤的合成 SOP,并返回 citation | RAG 隔离测试、真实模型 citation 指标 | 符合;SOP 不覆盖业务事实和审批规则 |
+| 记忆与状态 | Graph State、PostgreSQL checkpoint、pgvector SOP、Trace/Audit 四类记忆 | 核心技术手册、数据库和恢复测试 | 符合;无用户画像或跨任务聊天记忆 |
+| 高风险写入受控 | JWT/RBAC、审批绑定、批准后绕过 Redis 重取事实、Verifier、确定性 Binding | 审批、事实漂移、缓存绕过与权限测试 | 符合 |
+| 审批竞态与幂等副作用 | PostgreSQL 行锁、审批周期唯一语义、operation 派生幂等键、唯一约束、写后读 | 并发审批、重复写入、数据库断言 | 符合 |
+| 可恢复、可审计、可观测 | 业务表主导恢复 + checkpoint;Trace、Audit、SSE、低基数指标 | 9/9 安全恢复评测、Compose restart | 符合 |
+| 前端可展示技术链路 | Case 隔离、角色切换、业务事实、审批、Agent Trace、架构与 Eval 专题 | 收口分支 19 个前端测试文件/63 条用例、Vite build | 符合 |
+| 评测不虚构 | 682 条后端、63 条前端、42/42、9/9 Agent、9/9 Kimi 固定本地小样本 | JSON 报告与 release evidence | 符合;不等于生产准确率或 SLA |
+| 静态公开展示与本地 MVP 分离 | Netlify 只读页面;可写 API、数据库和 demo JWT 仅在本地 | Showcase 门禁修订、README、Netlify 证据 | 符合 |
+
+## 本次发现并处理的偏差
+
+1. **主线 CI 状态漂移:** main `c4257ac` 的 Actions run `30744845403` 并非五项全绿。四项通过,`compose-smoke` 在 clean runner 构建镜像时因 `uv sync --offline` 缺少 `hatchling` 缓存而失败。收口分支已用 RED/GREEN 测试移除错误的离线假设;必须以新远程 main run 关闭此项。
+2. **Agent 模式术语漂移:** README 和讲解文档沿用 Plan-and-Execute,但当前实现是模型根据 Observation 逐轮选择工具的有界 ReAct 风格循环。已统一表述,避免面试或开源审查时与代码不符。
+3. **前端数字漂移:** 已合并 main 实际为 62 条前端用例,部分 README、专题和状态文档仍显示 60。同步后又增加源码入口回归测试,因此收口分支最终为 63 条。
+4. **前端传递依赖告警:** `npm ci` 后发现 nanoid、postcss、undici 的传递依赖漏洞。锁文件已升级到修复版本,CI 新增 high 级依赖审计。
+5. **开源安全报告缺口:** CONTRIBUTING 曾写“计划增加安全策略”。已新增 `SECURITY.md` 并改为可执行的私密报告优先流程。
+6. **Harness 学习材料分散:** Prompt、上下文、工具、编排、记忆、Eval、约束与恢复已集中补入核心技术手册,并明确 Mock/Real、token budget 和通用并行调度边界。
+
+## 仍需项目所有者完成的关键节点
+
+- 复核 Draft PR #26,批准转为 Ready 并合并;以新的 main CI 五项全绿作为远程工程门禁证据。
+- 独立完成 `opercerta-ownership-acceptance.md` 的业务实演、源码讲解、重启/幂等实验和记录;Codex 不得代签。
+- 录制 3–5 分钟演示并核对静态专题内容。
+- 验收通过后创建最终 Showcase tag;不得提前把 Product Release gate 打开。
+
+## 发布判定
+
+- 工程与本地自动化门禁:`PASSED`,本分支完整本地结果已通过。
+- 远程 CI 门禁:`PR_CHECKS_PASSED_MAIN_PENDING`;PR 四项通过,等待合并后的 main Compose。
+- Showcase Release gate:`AWAITING_OWNER_VALIDATION`。
+- Product Release gate:`CLOSED`。
+
+公网 FastAPI、生产 IAM、限流、防滥用、托管密钥、备份恢复、高可用和线上告警不属于当前 Showcase 版本,不应为了“看起来完整”而伪装为已实现。
diff --git a/docs/development-log/current-state.md b/docs/development-log/current-state.md
index 517649e..58241a5 100644
--- a/docs/development-log/current-state.md
+++ b/docs/development-log/current-state.md
@@ -1,6 +1,6 @@
# OperCerta 当前状态
-> 更新时间:2026-08-02。本文件只保存当前权威状态;历史过程见 `docs/development-log/daily/` 与 `docs/release-evidence/`。
+> 更新时间:2026-08-10。本文件只保存当前权威状态;历史过程见 `docs/development-log/daily/` 与 `docs/release-evidence/`。
## 当前结论
@@ -10,11 +10,12 @@ OperCerta 的三业务共享 Agent 主线、可靠性内核、前后端、本地
公开站点 只承载静态项目展示,不连接可写 API 或数据库。真实 FastAPI、LangGraph、FastMCP、PostgreSQL、Redis 与 React 控制台在本地 Docker Compose 环境运行。当前版本不是公网交互产品,也不是企业生产系统。
-## 三类门禁
+## 当前门禁
| 门禁 | 状态 | 判定依据 |
| --- | --- | --- |
-| 工程与本地自动化门禁 | `PASSED` | main `7bb9ecd` 的 671 条后端基线与五项 CI 已通过;当前真实模型评测候选分支后端 682/682、Kimi 9/9,前端仍为 60,三业务 42/42、Agent 9/9、Compose 重启恢复通过 |
+| 工程与本地自动化门禁 | `PASSED` | 收口分支锁文件、Ruff/format/mypy、682 条后端、42/42、9/9、63 条前端、0 high audit、clean Docker build 与 Compose 三业务/重启恢复均通过 |
+| 远程 CI 门禁 | `PR_CHECKS_PASSED_MAIN_PENDING` | Draft PR #26 初始 run `31479724666` 四项通过;Compose 按 PR 规则跳过,等待批准合并和新 main run |
| Showcase Release gate | `AWAITING_OWNER_VALIDATION` | 静态站点和本地 MVP 已具备;仍需项目所有者独立完成业务实演、源码讲解、恢复实验和录屏 |
| Product Release gate | `CLOSED` | 未部署公网可写后端、生产身份、限流、防滥用、托管数据库备份、高可用与线上告警 |
@@ -22,7 +23,7 @@ OperCerta 的三业务共享 Agent 主线、可靠性内核、前后端、本地
`Product Release gate: CLOSED`
-main 发布/安全基线曾在一次性 pgvector 测试库得到 `671 passed in 112.07s` 并由 backend job 复核;当前真实模型评测候选分支新增 11 条契约后得到 `682 passed in 96.49s`,尚待合并后取得新鲜 main CI。
+PR #25 已把真实模型质量评测合入 main。main run `30744845403` 的后端、前端、Python 与仓库安全 job 通过,但 Compose 构建失败,因此不能继续写成“五项全绿”。失败点是 Dockerfile 第二次 `uv sync --offline` 无法在全新缓存中取得 `hatchling`,不是业务、Agent 或数据库断言失败。
## 已实现范围
@@ -38,17 +39,19 @@ main 发布/安全基线曾在一次性 pgvector 测试库得到 `671 passed in
## 当前运行与发布证据
-- GitHub main:`7bb9ecda8170ed8752049331f5597ea2368d77b1`;PR #23;Actions run `30629194460` 五项全绿。
-- 自动化基线:main 后端 `671 passed`,当前候选分支后端 `682 passed`;前端 19 文件/60 条;三业务固定契约 42/42;冻结 Agent 安全恢复 9/9;main Compose smoke 通过。
+- GitHub main:`c4257ac10dec470bbb9ca1076c4cfacfb21a80de`;PR #25;Actions run `30744845403` 四项成功、`compose-smoke` 失败。
+- 收口 Draft PR:[PR #26](https://github.com/KXHXK/opercerta/pull/26);初始 head `63692be`;PR run `31479724666` 的 repository-safety、python-quality、backend-tests、frontend 全部成功,compose-smoke 按事件规则跳过。
+- 已合并自动化基线:后端 `682 passed`;前端 19 文件/62 条;三业务固定契约 42/42;冻结 Agent 安全恢复 9/9。最近一次成功 main Compose 证据属于 `4c696d2`,不能替代 `c4257ac` 的失败结果。
+- 收口分支本地新鲜证据:后端 421 单元 + 261 集成 = 682;前端 19 文件/63 条;依赖审计 0;clean Docker build 成功;隔离 Compose 完成三业务数据库副作用、API/MCP 重启、recovery-only 和资源清理。
- 真实模型质量小样本:9/9;Goal、工具 precision/recall、证据完整率、citation 可解析率与数据库副作用匹配率均为 100%;端到端 P50/P95 为 19.722/31.333 秒。该结果不是生产准确率或 SLA。
-- main Compose 在干净 GitHub Linux 环境构建 uv `0.11.28` 镜像,验证三业务数据库副作用、API/MCP 重启、恢复和隔离卷清理,关闭了本机 GHCR 拉取超时留下的容器证据缺口。
-- 本机 WSL2 Ubuntu 26.04 的 `opercerta-demo` PostgreSQL、Redis、MCP、API 四服务 healthy;readiness 中 database、checkpoint、MCP 均 ready。
+- 最近一次成功的历史 main Compose 在 `4c696d2` 验证了三业务数据库副作用与恢复;当前 main `c4257ac` 的 clean build 失败事实已单列,不能混写。
+- 本轮 WSL2 Ubuntu 26.04 隔离 Compose 的 PostgreSQL、Redis、MCP、API 四服务均达到 healthy,验证完成后已删除容器、网络和测试卷,不保留污染状态。
- Netlify 为静态站点;`/console` 与 `/api/*` 在公网均是 SPA 静态回退,不是可写后端。
- 旧预发布 `v0.1.0-showcase.1` 指向较早提交,只保留历史;最终 Showcase tag 必须在本轮合并、本人验收和录屏后创建。
## 当前唯一主线
-1. 合并本轮双门禁、许可证、工具链和文档防漂移修订,并取得新鲜 main CI/Compose 证据。
+1. 项目所有者复核 Draft PR #26 后批准转为 Ready 并合并,取得新的 main 五项全绿证据,把远程 CI 门禁改为 `PASSED`。
2. 项目所有者按 `docs/learning/opercerta-ownership-acceptance.md` 亲自完成环境、完整库存闭环、代码链路、重启/幂等和分层讲解验收。
3. 录制 3–5 分钟演示,保存操作编号、工单编号、Trace/审计和数据库后置事实。
4. 验收通过后创建最终 Showcase tag,并以“公开静态展示 + 本地可复现完整 Agent MVP”对外表述。
diff --git a/docs/development-log/daily/2026-08-10.md b/docs/development-log/daily/2026-08-10.md
new file mode 100644
index 0000000..9a6401b
--- /dev/null
+++ b/docs/development-log/daily/2026-08-10.md
@@ -0,0 +1,56 @@
+# OperCerta 开发日志:2026-08-10
+
+## 今日目标
+
+在不修改根工作树未提交内容的前提下,从最新 `origin/main` 创建独立收口工作树,重新核对规格、主线事实、依赖、CI 和发布边界,关闭能够自动完成的工程问题,把人工掌握、录屏和最终发布留给项目所有者。
+
+## 基线保护
+
+- 根工作树分支存在文档和专题导航的未提交修改,未覆盖、未清理。
+- 新建 `.worktrees/final-closeout-20260810`,分支 `release/final-closeout-20260810`,基于 main `c4257ac`。
+- `origin/main` 已包含 PR #25 的真实模型评测与 Agent 专题。
+
+## 发现的问题
+
+1. 权威状态文档仍停留在 PR #23、671 条后端和 60 条前端,未反映 PR #25。
+2. main Actions run `30744845403` 实际失败;只有 `compose-smoke` 失败,其余四项通过。
+3. 失败日志显示 Dockerfile 在第二次离线 `uv sync` 中无法取得 PEP 517 `hatchling` 构建后端。
+4. README 与讲解材料把当前循环误称为严格 Plan-and-Execute;源码实际为有预算的 Model→Tool→Observation 循环。
+5. 前端锁文件存在 3 个可修复的传递依赖告警;开源安全报告流程尚未落地。
+6. 根工作树新增的 Harness 学习总结尚未进入最新 main 基线。
+
+## TDD 与修复
+
+- 先修改容器资产测试,要求项目安装不得依赖 clean runner 中不存在的离线构建后端缓存;测试按预期 RED。
+- Dockerfile 第二次安装仍使用 `--frozen --no-dev`,但移除错误的 `--offline` 假设;定向测试转 GREEN。
+- `npm audit fix` 只更新锁文件内 nanoid、postcss、undici 的修复版本,随后在 CI 加入 `npm audit --audit-level=high`。
+- 专题先把已合并主线数字由 60 更新为 62;新增 GitHub 源码入口回归后,收口分支最终为 63 条。保留非固定导航并增加源码入口,移动端隐藏内部锚点,遵守“无 fixed/sticky 模块”的已批准视觉约束。
+- README、handoff、current-state、核心技术手册和讲解文档统一实现事实与诚实边界。
+- 新增 `SECURITY.md` 与最终规格收口审计。
+
+## 可复盘的工程经验
+
+### 1. “冻结依赖”不等于“任意阶段都能离线”
+
+`uv.lock` 冻结项目运行依赖,但 PEP 517 build-system 解析还需要 `pyproject.toml` 中的 Hatchling。第一层 `--no-install-project` 不会保证这个构建后端进入缓存,因此第二层在全新 runner 上强制 offline 会失败。本机缓存命中不能代替 clean CI 证明。
+
+### 2. 必须区分局部门禁和总门禁
+
+PR #25 的后端 682、前端 62、Python 和仓库安全都通过,但 main 总 run 因 Compose 失败仍是红色。不能把“核心测试通过”写成“工程门禁全部通过”。
+
+### 3. 架构术语必须能够映射到源码
+
+当前没有独立生成完整计划再执行的严格 Plan-and-Execute 阶段。模型根据 Observation 逐轮选择只读工具,更准确的说法是“有界 ReAct 风格 Tool Loop”;工具/模型预算、ToolPolicy、确定性规则和审批形成安全边界。
+
+### 4. 评测数字要带样本边界
+
+9/9 Kimi 结果可以证明固定本地路径中的 Goal、工具、证据、审批和副作用契约,但不能外推生产准确率、成本或 SLA。端到端 P50/P95 也不能冒充模型单次推理延迟。
+
+## 当日未越过的边界
+
+- 未 push、未创建 PR、未合并、未创建最终 tag。
+- 未代替项目所有者签署掌握验收或录屏验收。
+- 未把静态 Netlify 站点改写成公网可写 Agent 产品。
+- 未启动 ForenTrail 或把 FieldPilot 工作并入本仓库。
+
+完整门禁结果将在 `docs/release-evidence/final-closeout-2026-08-10.md` 中记录。
diff --git a/docs/development-log/daily/2026-08-11.md b/docs/development-log/daily/2026-08-11.md
new file mode 100644
index 0000000..64adb7b
--- /dev/null
+++ b/docs/development-log/daily/2026-08-11.md
@@ -0,0 +1,33 @@
+# OperCerta 开发日志:2026-08-11
+
+## 今日目标
+
+在项目所有者明确批准后,把 2026-08-10 已完成本地验证的收口提交推送至 GitHub,创建 Draft PR,并核对远程 PR 门禁;不自动合并、不创建最终 tag、不代签本人掌握验收。
+
+## 推送与 Pull Request
+
+- 分支:`release/final-closeout-20260810`。
+- 初始提交:`63692beb44ccb1de42695574c2c9bfd1aa755a3d`。
+- Draft PR:[PR #26](https://github.com/KXHXK/opercerta/pull/26),目标分支 `main`。
+- GitHub 连接器创建 PR 时返回 HTTP 403 `Resource not accessible by integration`;按既定发布流程使用已认证且具有 `repo`/`workflow` scope 的 GitHub CLI 创建成功,没有修改提交内容或扩大权限。
+
+## 远程 PR 门禁
+
+初始 [Actions run 31479724666](https://github.com/KXHXK/opercerta/actions/runs/31479724666) 结果:
+
+| Job | 结果 |
+| --- | --- |
+| `repository-safety` | `success` |
+| `python-quality` | `success` |
+| `backend-tests` | `success` |
+| `frontend` | `success`,包含新增 `npm audit --audit-level=high` |
+| `compose-smoke` | `skipped`,符合仅 main push / workflow_dispatch 执行的条件 |
+
+PR 可合并且无代码冲突,但仍保持 Draft。只有合并后的 main run 才能验证此前失败的 clean-runner Compose 构建、三业务数据库副作用和重启恢复,因此当前远程状态是 `PR_CHECKS_PASSED_MAIN_PENDING`,不是总门禁 `PASSED`。
+
+## 下一关键审批
+
+1. 项目所有者复核 PR #26 的范围和证据。
+2. 明确批准转为 Ready 并合并。
+3. 监控合并后的 main 五项门禁,重点确认 `compose-smoke`。
+4. main 全绿后再同步文档和静态专题;本人掌握、录屏和最终 tag 仍单独验收。
diff --git a/docs/learning/opercerta-core-technical-guide.md b/docs/learning/opercerta-core-technical-guide.md
index e9b1def..ee03d47 100644
--- a/docs/learning/opercerta-core-technical-guide.md
+++ b/docs/learning/opercerta-core-technical-guide.md
@@ -182,7 +182,7 @@ RAG 回答“适用 SOP 怎么描述”,返回带 `document_id/chunk_id/versio
2. `operational_signals` 保存来源、时间、事实哈希和严重级别;`dedup_key` 唯一约束使并发扫描不会制造重复待办。
3. operator 从信号箱点击“启动 Agent 调查”;`trigger_signal_id`、对象类型和对象 ID 在同一 PostgreSQL 事务中校验并绑定 operation。
4. 同一信号只有一个认领胜者;重复点击返回 `signal_already_claimed`,不能创建第二个处置。
-5. 进入 operation 后,Agent 才使用 Goal、Plan-and-Execute、Tool Calling、RAG 和模型解释完成深入调查;审批、复核和幂等写入边界保持不变。
+5. 进入 operation 后,Agent 才使用 Goal、有界 ReAct 风格 Tool Loop、Tool Calling、RAG 和模型解释完成深入调查;审批、复核和幂等写入边界保持不变。
为什么不让用户先点“查询状态”再点“创建处置”:写路径本来就必须重新取证,拆成两次人工操作既重复调用,又会让第一次查询结果在第二次提交前过期,形成 TOCTOU。为什么不让 LLM 负责发现 `12 < 15`:这是稳定、可审计、可单测的数值规则,用模型只会增加成本、延迟和不确定性。真正适合 Agent 的部分是多来源取证、SOP 检索、受控规划、解释、人机协作和恢复。
@@ -200,7 +200,7 @@ RAG 回答“适用 SOP 怎么描述”,返回带 `document_id/chunk_id/versio
`facts_hash` 把本次判定事实固定下来,`dedup_key = signal 类型 + 对象 + facts_hash`。相同事实再次扫描会复用 PostgreSQL 中的旧 signal,所以已经被认领的信号仍显示 `investigating`;这表示“本次重新取证命中了同一异常”,不是本次扫描偷偷创建了新处置。前端现在显示本次扫描时间、对象数、命中数、规则事实和来源,角色切换不再自动加载数据库信号。
-operator 点击“启动 Agent 调查”后才进入第二阶段:FastAPI 在同一事务认领 signal,LangGraph 编码 Goal,Plan-and-Execute 通过受控 Tool Calling 再取业务事实并检索对应 SOP,模型只负责综合解释,确定性场景规则生成候选计划,然后停在人工审批。批准后 Verifier 绕过缓存再次取证,绑定一致才允许幂等写工单。因此整个链路是“规则发现 → Agent 深入调查 → 人工批准 → 重新验证 → 受控执行”,而不是让模型猜测是否异常。
+operator 点击“启动 Agent 调查”后才进入第二阶段:FastAPI 在同一事务认领 signal,LangGraph 编码 Goal,有界 ReAct 风格 Tool Loop 通过受控 Tool Calling 再取业务事实并检索对应 SOP,模型只负责选择只读工具和综合解释,确定性场景规则生成候选计划,然后停在人工审批。批准后 Verifier 绕过缓存再次取证,绑定一致才允许幂等写工单。因此整个链路是“规则发现 → Agent 深入调查 → 人工批准 → 重新验证 → 受控执行”,而不是让模型猜测是否异常。
## 21. 历史对账和后继调查为什么不能直接“重开”
@@ -276,3 +276,132 @@ flowchart TD
真实模型验证暴露了两个 Mock 无法发现的问题。第一,生产工厂曾把 `OPERCERTA_MCP_TIMEOUT_SECONDS=2` 同时传给 LLM;Kimi 正常工具调用约需数秒,导致统一 503。现已拆分 `OPERCERTA_MODEL_TIMEOUT_SECONDS=90`。第二,Kimi K2.6 的强制工具调用在当前 Moonshot 配置下需要 `OPERCERTA_MODEL_THINKING_MODE=disabled`;否则首个 `model_decide` 返回供应商 BadRequest。最终分析和审批 Verifier 也改为单一内部提交工具的原生 tool calling,避免供应商 structured-output 解析波动。
这不是把供应商特例散落进业务代码:通用 gateway 默认仍为 `thinking=default`,Moonshot 部署配置显式选择 `disabled`;业务图只依赖严格 `AgentLoopModelGateway` 契约。三个真实只读场景、一个库存批准写入和一个不可用端点闭锁均已有独立脱敏证据。发布门禁仍为 `CLOSED`,因为尚未 commit/push/merge、复跑远程 CI、公开交互后端和完成人工掌握验收。
+
+## 25. Agent Harness:让模型有判断空间,但没有越权空间
+
+### 25.1 Harness 不是单个类
+
+OperCerta 的 Agent Harness 是包围 LLM 的整套运行约束:
+
+> LLM 负责理解目标、选择只读工具、综合证据和提出建议;确定性代码负责权限、业务规则、审批、参数计算、写入授权、幂等和恢复。
+
+`AgentHarness` 类只实现可信 Goal 和调用预算契约。完整 Harness 还包括 Prompt Registry、Pydantic Context、ToolPolicy、ToolExecutor、LangGraph、PostgreSQL Checkpointer、TraceRecorder、冻结 Eval 和确定性 Policy Guard。
+
+```mermaid
+flowchart LR
+ UI["React 有限表单"] --> API["FastAPI 认证与请求校验"]
+ API --> INTENT["可信 IntentEnvelope"]
+ INTENT --> LLM["LLM Goal / Tool Calling / FinalAnalysis"]
+ LLM --> POLICY["ToolPolicy 白名单·对象绑定·预算"]
+ POLICY --> MCP["FastMCP 只读工具"]
+ MCP --> OBS["事实·规则·RAG Observation"]
+ OBS --> LLM
+ LLM --> GUARD["确定性 Policy Guard"]
+ GUARD --> HUMAN["人工审批 interrupt"]
+ HUMAN --> FRESH["绕过 Redis 重新取证"]
+ FRESH --> VERIFY["LLM Verifier + 确定性 Binding"]
+ VERIFY --> WRITE["幂等工单写入与回读"]
+ WRITE --> TRACE["Trace / Audit / Feedback"]
+ TRACE -. "状态与反馈" .-> LLM
+ PG[("PostgreSQL\n事实·Checkpoint·Trace·pgvector")] --- OBS
+```
+
+### 25.2 七个工程组成
+
+| 组成 | 本项目实现 | 约束与价值 |
+| --- | --- | --- |
+| Prompt | `PromptRegistry` 加载并哈希 `planner-v1`、`tool-loop-v1`、`verifier-v1` | Prompt 约束模型职责,但不能代替代码安全边界 |
+| 上下文 | `IntentEnvelope`、`GoalContext`、`AgentDecisionContext`、`VerificationContext` | 只给模型阶段性最小结构化上下文,不暴露凭据、SQL 或无限聊天历史 |
+| 工具 | `ToolPolicy → ToolExecutor → CachedReadToolGateway → McpToolGateway → FastMCP` | 模型只能看到白名单只读工具,写工具不进入模型目录 |
+| 编排 | 三业务共享 `ControlledAgentRootGraph` | 有界 Model→Tool→Observation 循环,人工审批使用 LangGraph `interrupt` |
+| 记忆 | Graph State、PostgreSQL checkpoint、pgvector SOP、Trace/Audit | Redis 只是可删除的只读缓存,不是业务事实或长期记忆 |
+| Eval | 42 条三业务契约、9 条安全恢复轨迹、9 条 Kimi 固定真实模型路径 | 证明明确契约,不等于生产准确率、真实流量或 SLA |
+| 约束与恢复 | RBAC、Schema、对象绑定、审批周期、批准后重取证、Binding、幂等键、写后读、RecoveryCoordinator | 任一关键证据、权限或状态不一致都失败关闭 |
+
+### 25.3 Prompt 与上下文的真实生产边界
+
+当前生产单根图实际使用:
+
+- `planner-v1`:把可信 `IntentEnvelope` 编码为 `GoalEncoding`;
+- `tool-loop-v1`:根据允许工具和已有 Observation 返回 `ToolDecision` 或 `FinalAnalysis`;
+- `verifier-v1`:批准后比较原事实和刷新事实,返回 `proceed`、`abort` 或 `escalate`。
+
+`analyst-v1` 和 `reporter-v1` 存在于模型适配器,但当前根图不单独调用。最终分析来自 `tool-loop-v1`,终态结果主要由确定性代码生成。不能因为 Prompt 文件存在就声称它们全部位于当前生产路径。
+
+系统不保存或展示隐藏思维链。Trace 只保存 Prompt 引用、结构化决定、工具与证据引用、安全摘要和错误码。`AgentHarness.validate_goal()` 还要求模型输出的 goal、scenario、object_id 与可信 Intent 完全一致;当前根图最多允许 6 次模型调用和 4 次工具调用,证据仍不完整就返回 `required_evidence_incomplete`。
+
+### 25.4 受控 Tool Calling 与写权限隔离
+
+模型可见工具只有:
+
+- `inventory.get_snapshot`;
+- `equipment.get_status`;
+- `task.get_status`;
+- `policy.list_constraints`;
+- `knowledge.search_sop`。
+
+`ToolPolicy` 检查工具白名单、严格参数、对象绑定、重复调用和预算;`ToolExecutor` 将结果转换成带 evidence id、参数哈希、状态、安全摘要和 cache status 的 `ToolObservation`。
+
+FastMCP 还实现 `work_order.create` 与 `work_order.get`,但它们不对模型开放。只有人工批准、重新取证、LLM Verifier 和确定性 Binding 全部通过后,LangGraph 的代码节点才可调用写工具。模型不能用一句“已创建”替代数据库写入和回读事实。
+
+### 25.5 有界 ReAct 风格生命周期
+
+当前根图不是严格 Plan-and-Execute,也不是无限开放 ReAct,而是有预算和确定性出口的 ReAct 风格 Tool Loop:
+
+```text
+encode_goal
+→ model_decide
+→ authorize_tools
+→ execute_read_tools
+→ model_decide(有界循环)
+→ validate_final_analysis
+→ calculate_policy
+→ persist_policy_result
+→ approval_interrupt
+→ refresh_and_verify
+→ execute_work_order
+→ verify_work_order
+→ mark_completed
+```
+
+查询路径在确定性规则评估后直接完成;写入路径必须等待人工审批。库存、设备和任务只替换事实 Schema、场景规则、计划参数和工单 payload,不复制可靠性生命周期。
+
+### 25.6 四种记忆与 Redis 边界
+
+1. **工作记忆:** LangGraph State 保存本 operation 的 Intent、Goal、Observation、审批、刷新事实、Verifier、工单和调用计数。
+2. **可恢复状态:** PostgreSQL `langgraph` schema 保存 checkpoint,`operation_id` 同时作为 `thread_id`。
+3. **长期语义知识:** FastEmbed 将合成 SOP 编码为 512 维向量,pgvector 按 scenario、active、version 过滤并执行 cosine 检索。
+4. **事件记忆:** Agent Trace 和业务 Audit 保存脱敏的工具、审批、执行、反馈与错误码。
+
+Redis 是 cache-aside 读优化,不是业务真相或长期 Memory。Redis 删除或不可用时回退 MCP 权威读取;批准后复核强制 `bypass_cache=True`。当前没有用户画像、跨 operation 聊天历史或自动改规则的自学习记忆。
+
+### 25.7 Eval 与量化边界
+
+当前证据包括 42 条三业务固定契约、9 条 Agent 安全/恢复轨迹和 9 条 Kimi K2.6 固定真实模型路径。真实模型小样本中,Goal exact、工具 precision/recall、证据完整率、citation 可解析率和数据库副作用匹配率均为 100%,提示注入 3/3,未授权调用、审批绕过和重复工单均为 0;端到端 P50/P95 为 19.722/31.333 秒。
+
+这些是开发者维护的固定本地小样本,用于验证 Goal、Tool、Evidence、Approval 和 Side Effect 契约;不是独立第三方基准,也不能表述为生产准确率、供应商性能、成本或 SLA。
+
+### 25.8 失败恢复链
+
+- FastAPI/Pydantic 拒绝非法 Schema;JWT/RBAC 分离 operator、approver 和 auditor。
+- operation 必须来自真实、未认领的 operational signal。
+- `AgentHarness` 防 Goal 漂移和预算越界;`ToolPolicy` 拒绝未知工具、写工具、参数漂移和重复调用。
+- PostgreSQL 行锁与 `(operation_id, approval_cycle)` 让并发审批只有一个原子胜者。
+- 批准后绕过 Redis 重取事实;Verifier 给认知建议,确定性 `binding_facts` 拥有最终执行门禁。
+- 工单使用 operation 派生幂等键与数据库唯一约束;相同请求回放原工单,冲突 payload 失败。
+- 写入后调用 `work_order.get` 核对 ID、operation、payload 和 hash。
+- `ControlledAgentRootRecoveryCoordinator` 比较业务状态和 checkpoint,选择等待、重建、恢复审批或不操作;冲突时抛出 `RecoveryStateConflict`。
+- MCP 与模型均有显式超时;provider 失败时 operation 安全收口,不静默回退 Mock。
+
+### 25.9 当前诚实边界
+
+默认 Compose 使用 `MockAgentModelGateway`,用于稳定自动化和可复现工程契约;真实模型模式必须显式配置兼容 endpoint,并在 Trace 标识真实 provider。Mock 与 Real 证据不能混写。
+
+仍有两个不能夸大的留白:
+
+1. `AgentBudget.max_input_tokens` 和 Harness `timeout_seconds` 已建模,但尚无统一 token 计数器和全链路 Deadline;实际超时由模型与 MCP 客户端分别执行。
+2. `OPERCERTA_TOOL_MODE=parallel` 已配置,但生产单根 Tool Loop 仍顺序执行模型提出的只读调用;只有 signal 扫描中的独立事实/规则读取使用并发。不能声称 Agent 工具循环已实现通用并行调度。
+
+对外准确表达是:
+
+> OperCerta 将 LLM 放在认知与工具选择层,把业务规则、审批授权和写入权限留在确定性控制层;通过 LangGraph 保存可恢复状态,借助 FastMCP 获取类型化事实,使用 PostgreSQL/pgvector 管理事实与知识,并以审批后重新取证、绑定比较和幂等写入解决传统工单系统中的事实漂移、竞态和重复副作用问题。
diff --git a/docs/learning/opercerta-interview-guide.md b/docs/learning/opercerta-interview-guide.md
index ec3900d..75040af 100644
--- a/docs/learning/opercerta-interview-guide.md
+++ b/docs/learning/opercerta-interview-guide.md
@@ -2,7 +2,7 @@
## 30 秒版本
-“OperCerta 是我用 FastAPI、LangGraph、最小 LangChain、FastMCP、PostgreSQL/pgvector、Redis 和 React 实现的可恢复运营处置 Agent。它跑通库存补货、设备维修、作业异常恢复三条闭环:确定性检测先发现异常,Plan-and-Execute Agent 受控取证,人工审批绑定快照,批准后重新复核,再幂等写工单。当前候选分支已验证 682 条后端测试、19 个前端测试文件/60 条用例、42/42 三业务固定契约、9/9 Agent 冻结安全恢复评测和真实 Compose 重启;Kimi K2.6 的 9 条冻结本地路径也全部通过,覆盖三业务查询、提示注入和审批写入。公网可写后端仍未上线。”
+“OperCerta 是我用 FastAPI、LangGraph、最小 LangChain、FastMCP、PostgreSQL/pgvector、Redis 和 React 实现的可恢复运营处置 Agent。它跑通库存补货、设备维修、作业异常恢复三条闭环:确定性检测先发现异常,有界 ReAct 风格 Agent 受控取证,人工审批绑定快照,批准后重新复核,再幂等写工单。当前收口分支已验证 682 条后端测试、19 个前端测试文件/63 条用例、42/42 三业务固定契约、9/9 Agent 冻结安全恢复评测;Kimi K2.6 的 9 条冻结本地路径也全部通过,覆盖三业务查询、提示注入和审批写入。公网可写后端仍未上线,最新 Compose 修复还要取得新 main CI 后才恢复总门禁。”
## 3 分钟版本
@@ -59,9 +59,9 @@ request ID 和 W3C trace context 关联 API/MCP;span 跨 LangGraph、Redis、S
还可以讲 WSL 生命周期使 Docker 正常退出、Netlify worktree 部署错产物、失败测试污染共享数据库等案例。
-### 11. 为什么是 Plan-and-Execute,而不是开放 ReAct
+### 11. 为什么是有界 ReAct 风格工具循环,而不是开放式 ReAct 或严格 Plan-and-Execute
-业务动作只有查询和创建三类受控工单,开放聊天会放大提示注入、对象漂移和越权工具风险。模型先把固定表单编码成 Goal,再提出只读工具计划;ToolPolicy/Harness 校验后执行,确定性规则和人工审批才允许进入写路径。需要重规划时最多一次,不允许无限自治循环。
+业务动作只有调查和创建三类受控工单,开放聊天会放大提示注入、对象漂移和越权工具风险。模型先把固定表单编码成 Goal,再根据 Observation 逐轮选择只读工具;ToolPolicy/Harness 校验后执行,证据不足时继续循环,满足证据契约后结束。它没有严格 Plan-and-Execute 的独立完整计划阶段,也不是无限自治的开放 ReAct:模型和工具调用都有预算,确定性规则和人工审批才允许进入写路径。
### 12. 真实 Kimi Tool Calling 兼容问题怎么讲
diff --git a/docs/release-evidence/final-closeout-2026-08-10.md b/docs/release-evidence/final-closeout-2026-08-10.md
new file mode 100644
index 0000000..8a84ea2
--- /dev/null
+++ b/docs/release-evidence/final-closeout-2026-08-10.md
@@ -0,0 +1,61 @@
+# OperCerta 最终收口验证证据(2026-08-10)
+
+## 验证对象
+
+- 基线:`origin/main` `c4257ac10dec470bbb9ca1076c4cfacfb21a80de`。
+- 分支:`release/final-closeout-20260810`。
+- 环境:Windows 10 + WSL2 Ubuntu 26.04 + Docker 29.1.3;源代码测试使用 uv 0.11.28 / Python 3.12,前端使用 Node.js 24。
+- 数据:隔离的临时 PostgreSQL/pgvector 与合成业务数据;未使用客户或原单位数据。
+
+## 上游 CI 事实
+
+main Actions run [`30744845403`](https://github.com/KXHXK/opercerta/actions/runs/30744845403) 的 `repository-safety`、`python-quality`、`backend-tests` 和 `frontend` 成功;`compose-smoke` 在镜像构建时失败。失败日志为第二次 `uv sync --frozen --no-dev --offline` 无法在 clean runner cache 中解析 `hatchling>=1.27,<2`。
+
+本分支先增加容器资产断言并得到 RED,再移除第二次项目安装的错误 `--offline` 假设。运行依赖继续由 `uv.lock` 与 `--frozen` 约束。
+
+## 本地验证结果
+
+| 门禁 | 命令 | 结果 |
+| --- | --- | --- |
+| uv 锁文件 | `uv lock --check` | 通过 |
+| Ruff | `uv run ruff check .` | 通过 |
+| 格式 | `uv run ruff format --check .` | 220 个文件通过 |
+| mypy | `uv run mypy src` | 86 个源文件通过 |
+| 后端单元测试 | `uv run pytest -q tests/unit` | 421 条通过 |
+| 后端集成测试 | `uv run pytest -q tests/integration` | 261 条通过,239.50 秒 |
+| 后端合计 | 单元 + 集成 | 682 条通过 |
+| 三业务固定契约 | `uv run python scripts/run_opercerta_evaluation.py` | 42/42 通过(运行器的 pytest 包装为 1 条通过) |
+| Agent 安全恢复评测 | `uv run python scripts/run_agent_evaluation.py` | 9/9 通过 |
+| 前端依赖审计 | `npm audit --audit-level=high` | 0 个漏洞 |
+| 前端测试 | `npm run test:run` | 19 文件/63 条通过 |
+| 前端构建 | `npm run build` | 通过;JS `index-XIL8-04O.js`,CSS `index-BKDs2e3h.css` |
+| 文档索引 | `uv run python scripts/verify_document_index.py` | 128 份当前 Markdown 完整登记 |
+| 仓库安全 | `uv run python scripts/verify_repository_safety.py` | 通过 |
+| Docker clean build | `docker build --progress=plain` | 通过;原失败的项目安装层成功取得构建后端并完成 wheel 构建 |
+| Compose 完整验证 | `COMPOSE_PROJECT_NAME=opercerta-final-closeout python3 scripts/verify_agent_compose.py` | 三业务轨迹、citation、审批和 PostgreSQL 副作用通过 |
+| Compose 重启恢复 | restart API/MCP 后执行 `--recovery-only` | 通过;四个长期服务均 healthy |
+| 隔离资源清理 | `docker compose down -v --remove-orphans` | 测试容器、网络与 volume 删除完成 |
+
+## 供应链修复
+
+- `web/package-lock.json` 将 nanoid、postcss、undici 升级到修复版本,未改变顶层依赖范围。
+- GitHub Actions frontend job 在 `npm ci` 后执行 `npm audit --audit-level=high`,高风险告警将失败关闭。
+- 新增 `SECURITY.md`,CONTRIBUTING 改为引用真实安全报告流程。
+
+## 判定
+
+- 工程与本地自动化门禁:`PASSED`。
+- 远程 CI 门禁:`PR_CHECKS_PASSED_MAIN_PENDING`。Draft PR #26 初始 run `31479724666` 四项通过;Compose 按 PR 事件规则跳过。
+- Showcase Release gate:`AWAITING_OWNER_VALIDATION`。
+- Product Release gate:`CLOSED`。
+
+本轮没有重新调用真实 Kimi;9/9 真实模型指标来自已合入 main 的 2026-08-02 脱敏报告。本轮复验的是 Mock 冻结契约、数据库/MCP/Compose 和构建/供应链门禁,不能把两次证据混成一次运行。
+
+## 尚不能写成通过的事项
+
+- 收口分支已推送并创建 Draft PR #26;尚无合并后的新 main clean-runner Compose 证据。
+- 项目所有者掌握验收与 3–5 分钟录屏尚未完成。
+- 最终 Showcase tag 尚未创建。
+- Product Release gate 保持 `CLOSED`;本证据不覆盖公网 IAM、Ingress、限流、备份、高可用或 SLA。
+
+所有本地命令均已取得终态;上游 main 的失败历史继续保留,不能用本地结果冒充远程 clean runner 已修复。
diff --git a/docs/release-evidence/github-actions-ci.md b/docs/release-evidence/github-actions-ci.md
index 473f93b..e1f23d5 100644
--- a/docs/release-evidence/github-actions-ci.md
+++ b/docs/release-evidence/github-actions-ci.md
@@ -1,5 +1,15 @@
# GitHub Actions 分层 CI:远程验证证据
+## 2026-08-10 PR #25 main 失败复核与本地修复
+
+- PR #25 已合入 main `c4257ac10dec470bbb9ca1076c4cfacfb21a80de`。
+- [main run 30744845403](https://github.com/KXHXK/opercerta/actions/runs/30744845403) 的 `repository-safety`、`python-quality`、`backend-tests`、`frontend` 成功;后端为 682 条,前端为 19 文件/62 条,42/42 与 9/9 评测均通过。
+- `compose-smoke` 在 `Build and start Compose services` 失败:Dockerfile 第二次 `uv sync --frozen --no-dev --offline` 在全新 runner cache 中找不到 PEP 517 `hatchling>=1.27,<2`。因此该 main run 总结论为 `failure`,不得继续写成五项全绿。
+- `release/final-closeout-20260810` 先增加容器资产 RED 断言,再移除错误的项目安装离线假设;运行依赖继续使用 `uv.lock` 和 `--frozen`。
+- 本地 WSL2 clean Docker build、隔离 Compose 三业务数据库副作用、API/MCP 重启和 recovery-only 已通过;收口分支前端新增源码入口测试后为 63 条,`npm audit --audit-level=high` 为 0 个漏洞。
+- 修复提交 `63692be` 已推送并创建 [Draft PR #26](https://github.com/KXHXK/opercerta/pull/26)。初始 [PR run 31479724666](https://github.com/KXHXK/opercerta/actions/runs/31479724666) 的 `repository-safety`、`python-quality`、`backend-tests`、`frontend` 全部成功;`compose-smoke` 按 PR 事件规则跳过。
+- PR 结果证明修复提交的四项快速门禁通过;必须取得合并后的 main GitHub Actions 五项成功,远程 CI 门禁才能改为 `PASSED`。
+
## 2026-07-31 双门禁收口 PR #23 与 main 总门禁
- [PR #23](https://github.com/KXHXK/opercerta/pull/23) 的 `repository-safety`、`python-quality`、`backend-tests`、`frontend` 全部成功,`compose-smoke` 按 PR 事件规则跳过;随后以普通 merge commit `7bb9ecda8170ed8752049331f5597ea2368d77b1` 合入 main。
diff --git a/tests/unit/runtime/test_container_assets.py b/tests/unit/runtime/test_container_assets.py
index 2ef39db..31f65ac 100644
--- a/tests/unit/runtime/test_container_assets.py
+++ b/tests/unit/runtime/test_container_assets.py
@@ -69,10 +69,13 @@ def test_image_caches_locked_dependencies_before_copying_project_source() -> Non
dockerfile = Path("Dockerfile").read_text(encoding="utf-8")
dependency_sync = dockerfile.index("uv sync --frozen --no-dev --no-install-project")
source_copy = dockerfile.index("COPY src ./src")
- project_sync = dockerfile.index("uv sync --frozen --no-dev --offline")
+ project_sync = dockerfile.index("uv sync --frozen --no-dev", source_copy)
assert dockerfile.index("README.md") < dependency_sync
assert dependency_sync < source_copy < project_sync
+ # A clean runner has no cached PEP 517 build backend. Keeping the project
+ # install online lets uv fetch hatchling while the runtime lock stays frozen.
+ assert "uv sync --frozen --no-dev --offline" not in dockerfile
def test_compose_example_is_tracked_and_real_file_is_ignored() -> None:
diff --git a/tests/unit/runtime/test_release_assets.py b/tests/unit/runtime/test_release_assets.py
index 88f00ad..4ae4b3f 100644
--- a/tests/unit/runtime/test_release_assets.py
+++ b/tests/unit/runtime/test_release_assets.py
@@ -283,9 +283,11 @@ def test_showcase_release_gate_and_owner_acceptance_are_explicit() -> None:
assert phrase in amendment
assert "Showcase Release gate: AWAITING_OWNER_VALIDATION" in state
assert "Product Release gate: CLOSED" in state
- assert "PR #23" in handoff
- assert "30629194460" in handoff
- assert "671 passed" in handoff
+ assert "PR #25" in handoff
+ assert "30744845403" in handoff
+ assert "compose-smoke" in handoff
+ assert "后端 682" in handoff
+ assert "前端 19 文件/62 条" in handoff
assert "不得由 Codex 自动代签" in ownership
assert "operation_id" in ownership
assert "work_order_id" in ownership
diff --git a/web/package-lock.json b/web/package-lock.json
index db56021..2c4e062 100644
--- a/web/package-lock.json
+++ b/web/package-lock.json
@@ -1973,9 +1973,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
- "version": "3.3.16",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
- "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
+ "version": "3.3.18",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
+ "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
"dev": true,
"funding": [
{
@@ -2046,9 +2046,9 @@
}
},
"node_modules/postcss": {
- "version": "8.5.19",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz",
- "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==",
+ "version": "8.5.26",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
+ "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
"dev": true,
"funding": [
{
@@ -2066,7 +2066,7 @@
],
"license": "MIT",
"dependencies": {
- "nanoid": "^3.3.12",
+ "nanoid": "^3.3.17",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@@ -2391,9 +2391,9 @@
}
},
"node_modules/undici": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
- "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
+ "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
"dev": true,
"license": "MIT",
"engines": {
diff --git a/web/src/showcase/SectionNav.tsx b/web/src/showcase/SectionNav.tsx
index 8ec1604..f17cc0d 100644
--- a/web/src/showcase/SectionNav.tsx
+++ b/web/src/showcase/SectionNav.tsx
@@ -15,12 +15,22 @@ export function SectionNav() {
OPERCERTA
-
- {ITEMS.map(([id, label]) => (
-
- ))}
+
+
+ {ITEMS.map(([id, label]) => (
+
+ ))}
+
+
+ GitHub ↗
+
);
diff --git a/web/src/showcase/ShowcasePage.test.tsx b/web/src/showcase/ShowcasePage.test.tsx
index 0a44096..39b24f7 100644
--- a/web/src/showcase/ShowcasePage.test.tsx
+++ b/web/src/showcase/ShowcasePage.test.tsx
@@ -49,7 +49,7 @@ it("publishes exact reproducible evaluation results and honest limits", () => {
for (const result of [
"682 / 682",
- "60 / 60",
+ "63 / 63",
"42 / 42",
"3 / 3",
"19.722 s",
@@ -95,6 +95,15 @@ it("moves within the page without adding a location hash", () => {
expect(window.location.hash).toBe("");
});
+it("links directly to the public source repository", () => {
+ render(
);
+
+ expect(screen.getByRole("link", { name: /GitHub/ })).toHaveAttribute(
+ "href",
+ "https://github.com/KXHXK/opercerta",
+ );
+});
+
it("labels both screenshots as local synthetic-data evidence", () => {
render(
);
diff --git a/web/src/showcase/project-facts.test.ts b/web/src/showcase/project-facts.test.ts
index 6ae34fe..46282ca 100644
--- a/web/src/showcase/project-facts.test.ts
+++ b/web/src/showcase/project-facts.test.ts
@@ -8,7 +8,7 @@ it("keeps evidence-backed release facts and three typed scenarios", () => {
frozenEvaluations: 42,
realModelPaths: 9,
backendTests: 682,
- frontendTests: 60,
+ frontendTests: 63,
agentSafetyEvaluations: 9,
promptInjectionPasses: 3,
endToEndP50Seconds: 19.722,
diff --git a/web/src/showcase/project-facts.ts b/web/src/showcase/project-facts.ts
index 1853233..35973b3 100644
--- a/web/src/showcase/project-facts.ts
+++ b/web/src/showcase/project-facts.ts
@@ -13,7 +13,7 @@ export const PROJECT_FACTS = {
frozenEvaluations: 42,
realModelPaths: 9,
backendTests: 682,
- frontendTests: 60,
+ frontendTests: 63,
agentSafetyEvaluations: 9,
promptInjectionPasses: 3,
endToEndP50Seconds: 19.722,
diff --git a/web/src/styles.css b/web/src/styles.css
index 3e02564..727e8c7 100644
--- a/web/src/styles.css
+++ b/web/src/styles.css
@@ -60,12 +60,21 @@ a {
display: flex;
align-items: center;
justify-content: space-between;
- padding: 22px 0;
+ padding: 16px 0;
border-bottom: 1px solid var(--line);
}
-.section-nav div {
+.section-nav-actions,
+.section-nav-links {
display: flex;
+ align-items: center;
+}
+
+.section-nav-actions {
+ gap: 12px;
+}
+
+.section-nav-links {
flex-wrap: wrap;
gap: 6px;
}
@@ -92,6 +101,29 @@ a {
transform: translateY(-1px);
}
+.section-source {
+ display: inline-flex;
+ min-height: 36px;
+ align-items: center;
+ padding: 6px 12px;
+ border: 1px solid var(--line);
+ border-radius: 10px;
+ color: var(--accent);
+ background: rgba(255, 255, 255, 0.7);
+ font-weight: 750;
+ text-decoration: none;
+ transition:
+ color 180ms ease,
+ border-color 180ms ease,
+ background 180ms ease;
+}
+
+.section-source:hover {
+ color: var(--ink);
+ border-color: var(--accent);
+ background: var(--accent-soft);
+}
+
.showcase-logo {
color: var(--ink);
font: 800 0.82rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
@@ -1457,9 +1489,16 @@ code {
}
.section-nav {
- flex-direction: column;
- gap: 14px;
- align-items: flex-start;
+ gap: 12px;
+ }
+
+ .section-nav-links {
+ display: none;
+ }
+
+ .section-source {
+ min-height: 34px;
+ padding-inline: 10px;
}
.showcase-hero,