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,