From ed4a49bfa680004bc9304826d45a4e5839693b10 Mon Sep 17 00:00:00 2001 From: weichen Date: Thu, 17 Sep 2026 06:55:32 +0000 Subject: [PATCH] docs(engineering-dashboard): add personal control center design plan Freeze the independent local readonly engineering-dashboard design, short-term task entry, and system-design index before implementation. Co-authored-by: Cursor --- .../2026-09-17-engineering-dashboard.md | 60 ++ .doc/system-design/README.md | 12 + ...ersonal-engineering-control-center-plan.md | 607 ++++++++++++++++++ 3 files changed, 679 insertions(+) create mode 100644 .doc/ops/short-term/2026-09-17-engineering-dashboard.md create mode 100644 .doc/system-design/project-control-center/01-personal-engineering-control-center-plan.md diff --git a/.doc/ops/short-term/2026-09-17-engineering-dashboard.md b/.doc/ops/short-term/2026-09-17-engineering-dashboard.md new file mode 100644 index 00000000..64acea38 --- /dev/null +++ b/.doc/ops/short-term/2026-09-17-engineering-dashboard.md @@ -0,0 +1,60 @@ +--- +title: "个人工程中控台:实时项目看板与离线 HTML 快照" +status: planned +created: 2026-09-17 +updated: 2026-09-17 +owner: unassigned +branch: feature/engineering-dashboard +worktree: "-" +mutex: engineering-dashboard +--- + +# 个人工程中控台:实时项目看板与离线 HTML 快照 + +## 目标 + +实现一个独立于产品后台的本地只读中控台,以同一不可变快照驱动实时页面、机器可读 JSON 与单文件 HTML 导出。 + +## 范围 + +- 聚合 `.doc`、Git/worktree、Docker/Compose、`/ready` 和晋升凭证。 +- 展示架构、模块、功能、任务、分支、验证和环境实装证据。 +- 提供 Markdown 搜索、阅读、正反向链接和证据链导航。 +- 显示来源、新鲜度、degraded 与 drift,不把声明状态当作观测状态。 + +## 非目标 + +- 不接入产品后台、产品数据库或产品权限体系。 +- 不写文档,不操作 Git、Docker、部署或 Herdr。 +- 不隐式联网或执行 `git fetch`。 + +## 步骤 + +1. [ ] U1:冻结快照、事实与四轴状态契约。 +2. [ ] U2:实现文档图谱与领域解析器。 +3. [ ] U3:实现 Git 与 worktree 采集。 +4. [ ] U4:实现运行态与部署证据采集。 +5. [ ] U5:实现对账、派生视图与新鲜度。 +6. [ ] U6:实现本地服务、总览与文档导航。 +7. [ ] U7:实现离线导出、脚本、浏览器验收和治理文档同步。 + +## 验收标准 + +- [ ] 实时 HTML、离线 HTML 和 JSON 引用同一 `generation_id` 与 `revision_id`。 +- [ ] 计划、实施、验证、实装四轴独立展示。 +- [ ] rdev、dev ahead、main events 分叉三个真实样本按方案输出。 +- [ ] 单源失败时页面仍可用,且显示 stale/error/last-success。 +- [ ] 默认仅监听 loopback,无写操作、凭证暴露、路径越界和远程资源执行。 +- [ ] 导出 HTML 离开仓库后仍可阅读 Markdown,且不嵌入 assets。 + +## 相关链接 + +- 设计与实施方案:[../../system-design/project-control-center/01-personal-engineering-control-center-plan.md](../../system-design/project-control-center/01-personal-engineering-control-center-plan.md) +- Checklist: 待实施阶段创建 +- Handoff: 待需要时创建 +- Test report: 待实施阶段创建 + +## 证据 / 备注 + +- 本任务当前仅完成方案设计,尚未认领、未实现、未验证、未部署。 +- 实施开始前必须在 CURRENT 登记 owner、真实 branch、worktree 与 mutex;本文件中的 branch 是计划分支名。 diff --git a/.doc/system-design/README.md b/.doc/system-design/README.md index 4b91de9b..b74e53df 100644 --- a/.doc/system-design/README.md +++ b/.doc/system-design/README.md @@ -138,6 +138,18 @@ --- +### 8. 个人工程中控台 + +| 项 | 链接 | +|-----|------| +| 设计与实施方案 | [project-control-center/01-personal-engineering-control-center-plan.md](project-control-center/01-personal-engineering-control-center-plan.md) | +| 任务入口 | [../ops/short-term/2026-09-17-engineering-dashboard.md](../ops/short-term/2026-09-17-engineering-dashboard.md) | +| 一句话总结 | 独立于产品后台的本地只读中控台,以同一不可变快照驱动实时项目看板、Agent JSON 和离线 HTML | + +**核心边界**:计划、Git 实施、验证、环境实装四轴独立;冲突保留来源证据;不写文档、不操作 Git/Docker/部署/Herdr,不读取凭证。 + +--- + ## 🏗️ 在整体架构中的位置 ``` diff --git a/.doc/system-design/project-control-center/01-personal-engineering-control-center-plan.md b/.doc/system-design/project-control-center/01-personal-engineering-control-center-plan.md new file mode 100644 index 00000000..a39f8b18 --- /dev/null +++ b/.doc/system-design/project-control-center/01-personal-engineering-control-center-plan.md @@ -0,0 +1,607 @@ +--- +title: "个人工程中控台:实时项目看板与离线 HTML 快照" +type: feat +date: 2026-09-17 +artifact_contract: ce-unified-plan/v1 +product_contract_source: ce-plan-bootstrap +execution: code +--- + +# 个人工程中控台:实时项目看板与离线 HTML 快照 + +## Goal Capsule + +为仓库所有者提供一个独立于产品后台的本地只读中控台。打开一个页面即可判断当前系统由什么组成、哪些任务正在计划或实施、真实分支和 worktree 在哪里、各环境实际运行了什么,并能在两次点击内到达对应的设计、审计、整改、任务和测试材料。 + +中控台同时提供两种输出: + +1. `serve`:持续采集本地仓库、Git 和运行环境,提供自动刷新的 HTML 页面与机器可读 JSON; +2. `export`:基于同一份不可变快照生成单文件 HTML,便于归档、离线查看和交接。 + +这不是产品功能,不接入产品后台、产品数据库或产品权限体系;它也不是发布系统,不执行 Git、Docker、部署或文档写操作。 + +### 成功标准 + +- 在当前仓库规模下,首次可用页面目标为 2 秒内出现;慢数据源可随后标记为 `degraded`,不得拖垮整页。 +- 正常情况下,本地文件、Git 或运行态变化在 5 秒内反映到新快照。 +- 页面分别展示“文档声明、Git 实施、验证证据、环境实装”,不把任务文档的 `done` 翻译为已合并或已部署。 +- 每个关键结论均显示来源、采集时间、新鲜度和可信类型;冲突保留双方证据,不静默选边。 +- 在线模式检索 allowlist 内未截断的 Markdown 正文;离线导出同时给出 included、truncated 和 metadata-only 清单,二进制附件只索引路径、类型、大小和归属。 +- 单文件 HTML 默认权限为 `0600`,可脱离仓库阅读已纳入 inclusion manifest 的 Markdown 正文,并醒目标出截断或仅元数据条目;不打包 `.doc/**/assets`。 +- 任一采集源失败时仍可查看最近成功数据,并明确显示 `stale/error/last_success_at`。 + +--- + +## Product Contract + +### 用户与使用场景 + +唯一主要用户是仓库所有者。典型使用包括: + +- 每天开始工作时查看焦点任务、阻塞、集成槽和环境异常; +- 从模块进入设计、任务、测试和部署证据链; +- 判断某项工作究竟只是有计划、正在实现、已经验证,还是已在某环境实装; +- 查看本地分支、远端跟踪引用、ahead/behind、worktree 和未提交变更; +- 发现 `CURRENT`、功能矩阵、发布门禁、Git 与 Docker 之间的漂移; +- 导出一个自包含 HTML,作为某一时点的项目快照。 + +### 已确定的产品决策 + +#### PKD-1:独立本地工具 + +采用独立 Go CLI 和本地 Web 页面,不加入现有产品后台。 + +(session-settled: user-approved — chosen over product-admin integration: the console is for the repository owner’s local engineering management and the production image excludes `.doc` and `.git`) + +#### PKD-2:只读实时页与离线快照共用数据模型 + +实时服务和导出文件必须由同一份版本化快照生成,避免在线页面与离线报告出现两套口径。 + +(session-settled: user-approved — chosen over a static-only report: the requested console must follow agent tasks and project state as they change) + +### 核心需求 + +| ID | 需求 | +|---|---| +| R1 | CLI 默认解析当前 Git 仓库根,也接受显式 `--repo`;页面始终显示绝对根路径、当前分支、HEAD 和采集时间。 | +| R2 | 仅监听 `127.0.0.1`,不依赖产品容器、产品数据库或产品登录。 | +| R3 | 架构与模块页聚合 `module-boundaries.yaml`、系统设计索引和 `FEATURE-MATRIX`,展示模块、能力、依赖和证据链。 | +| R4 | 任务状态至少拆为 `doc_status`、Git facets、`verification_result`/`verification_freshness`、deployment facets;可并存事实不得压成单值枚举。 | +| R5 | Git 页展示本地/跟踪分支、ahead/behind、最近提交、worktree、dirty/untracked 和任务声明匹配情况;不得隐式 `fetch`。 | +| R6 | 部署页按 shared/dev/test/main 分别展示容器状态、应用 readiness、服务完整性、schema、镜像与 promotion receipt;不得计算未经定义的综合绿灯。 | +| R7 | 文档页支持按 taxonomy、状态、模块和关键词筛选,展示正文、正向链接、反向链接、broken、orphan 与 unindexed 状态;查询状态可通过 URL 恢复。 | +| R8 | 一个不可变快照包含 `schema_version`、`generation_id`、`revision_id`、`generated_at`、仓库/提交身份、来源状态、事实与冲突。 | +| R9 | 自动刷新采用完整快照原子切换;每个来源独立超时,失败时保留最近成功数据并标注过期。 | +| R10 | 每条关键事实标注 `declared`、`observed`、`verified` 或 `inferred`,并保留来源路径和采集时间。 | +| R11 | 单 HTML 内嵌结构化模型、页面资源和允许导出的 Markdown 正文;附件只保留元数据及相对链接,并显示内容覆盖清单。 | +| R12 | 对 Agent 暴露稳定的只读 JSON,而不是让 Agent 解析 HTML;JSON 与页面引用同一 `generation_id` 和 `revision_id`。 | +| R13 | TaskState 显示声明的 owner/Agent 类型/认领/mutex 与基于 Git 的最近活动证据;Herdr 工程 Agent 和产品 PI Agent Runner 使用不同类型。没有运行态来源时必须显示 unknown,不得把“已认领”写成“正在执行”。 | +| R14 | 文件、命令和 HTTP 均采用 allowlist;拒绝路径穿越、symlink 越界、原始 HTML 执行、远程资源加载和凭证读取。 | + +### 事实源与权威边界 + +| 领域 | 权威来源 | 中控台处理方式 | +|---|---|---| +| 发布门禁 | `.doc/remediation/gap-matrix.md` | 只解释发布 blocker,不替代能力完成度。 | +| 能力成熟度 | `.doc/status/FEATURE-MATRIX.md` | 显示功能完成度,不据此推断是否部署。 | +| 当前焦点、认领、集成槽 | `.doc/ops/CURRENT.md` | 作为团队声明,与 Git 和运行态对账。 | +| 任务计划 | `ops/short-term`、`ops/long-term`、backlog front matter | 作为计划状态,不据此推断实现。 | +| 分支与 worktree | Git plumbing | 作为本地观测;远端状态注明“基于本地 refs,未联网刷新”。 | +| 环境运行态 | Docker/Compose 元数据与 `/ready` | 分开展示容器、应用与服务完整性。 | +| 验收证据 | `.doc/test-reports` 及 promotion receipts | 显示证据存在性与身份,不以文件名替代内容校验。 | + +来源之间不设全局覆盖顺序。各来源回答自己的问题;值不一致时生成 drift,并并列展示原值、时间和来源。 + +### 用户流程 + +```mermaid +flowchart LR + Start[启动 serve 或 export] --> Root[解析显式 repo root] + Root --> Collect[并行采集文档 Git 运行态] + Collect --> Snapshot[构建不可变快照] + Snapshot --> Reconcile[按领域对账] + Reconcile --> Live[本地实时页面和 JSON] + Reconcile --> Export[单文件 HTML] + Live --> Refresh[定时生成下一快照] + Refresh --> Snapshot +``` + +总览默认优先显示 `in_progress`、`blocked`、`planned`、`partial/deferred`、drift 和 degraded。`done` 与 archive 默认收起,避免历史材料淹没当前工作。 + +任务详情按照以下证据链导航: + +```mermaid +flowchart LR + Audit[audit 问题] --> Remediation[remediation 方案] + Remediation --> Task[任务声明] + Task --> Git[分支 worktree commit merge] + Git --> Test[测试与验收报告] + Test --> Deploy[环境 artifact 与 readiness] +``` + +显式 Markdown 链接和既定 taxonomy 属于强关系;仅由文件名或 slug 推断的关系标为“候选”,不得参与完成度计算。 + +### 状态模型 + +```mermaid +flowchart TB + D[doc_status: planned/in_progress/blocked/done] --> View[并列状态视图] + G[Git facets: branch/worktree/merge/cleanliness/ahead-behind] --> View + V[Verification: result + freshness + bound identity] --> View + P[Deployment facets: container/readiness/services/artifact identity] --> View + View --> Drift{来源是否一致} + Drift -->|是| Consistent[consistent] + Drift -->|否| Conflict[drift + 双方证据] +``` + +“实装”定义为存在可核对的环境观测证据,而不是任务文档为 `done`。容器可达但 artifact identity 无法核对时显示“运行可见、版本未验证”,不得提升为 verified deployment。当文档声明 healthy、Docker 显示异常而 `/ready` 为 ok 时,页面保留多值并标记 drift,不输出统一绿灯。 + +### 真实验收样本 + +1. rdev:存在调研、设计、任务与 backlog,但若没有 CURRENT 认领和真实分支,应显示“已计划、未认领、未实施、未实装”。 +2. main 环境:若 CURRENT 声明整体 healthy,而 events 容器实际 restarting、应用 `/ready` 仍为 ok,应同时显示三项证据与 drift。 +3. 本地 dev:若分支领先 `origin/dev`,必须显示“本地已具备、远端未确认可用”,不得写成已远程发布。 +4. `system-admin-console`:作为产品管理后台能力,与本计划的个人工程中控台分开展示,名称和边界不得混淆。 + +### 安全与非目标 + +一期明确不做: + +- 不写文档,不修改任务状态,不占用集成槽; +- 不创建、切换、合并、删除或推送 Git 分支; +- 不执行 `git fetch`,不调用远端托管平台 API; +- 不启动、停止、重建或部署容器; +- 不控制 Herdr 或向 Agent 派发任务; +- 不读取 `.env`、`.secrets`、Docker `Config.Env`、数据库数据或 `.git` 内部文件正文; +- 不加入产品 API、产品前端、产品数据库或生产镜像; +- 不执行 Markdown 原始 HTML、脚本、Mermaid 或远程资源。 + +后续候选包括跨快照历史、远端 CI/Gitea 聚合、受限 Markdown/Mermaid 渲染、Agent 查询 API 和修复建议。任何写操作继续保持在本工具之外。 + +--- + +## Planning Contract + +### High-Level Technical Design + +以下图示冻结边界和协议,不规定具体函数签名。 + +```mermaid +flowchart LR + Docs[.doc sources] --> DC[Document collector] + Git[Git/worktrees] --> GC[Git collector] + Run[Docker/ready/receipts] --> RC[Runtime collector] + DC --> Build[Snapshot builder] + GC --> Build + RC --> Build + Build --> Rec[Reconcile facts and conflicts] + Rec --> JSON[Read-only JSON] + Rec --> Web[Local HTML] + Rec --> File[Offline HTML] +``` + +```mermaid +sequenceDiagram + participant UI as Browser + participant C as Collection coordinator + participant S as Sources + participant P as Snapshot publisher + UI->>C: poll with revision_id + C-->>UI: current snapshot immediately + C->>S: singleflight collection with deadlines + S-->>C: facts or source error + C->>P: publish complete generation atomically + P-->>UI: new revision or not-modified +``` + +```mermaid +stateDiagram-v2 + [*] --> Loading + Loading --> Fresh: first collection succeeds + Loading --> ErrorUnknown: first collection fails + Fresh --> Refreshing: TTL expires + Refreshing --> Fresh: new generation succeeds + Refreshing --> Stale: source fails and last-known exists + Stale --> Fresh: source recovers + ErrorUnknown --> Fresh: source first succeeds +``` + +### 技术决策 + +#### KTD-1:采用仓库内独立 Go CLI + +新增 `cmd/engineering-dashboard` 与 `internal/engineeringdashboard`,复用仓库 Go 工具链但不导入产品业务包。Go 标准库足以提供本地 HTTP、资源嵌入、JSON、并发和原子文件输出;现有 `yaml.v3` 可用于已定义的 YAML 契约。 + +#### KTD-2:快照是唯一交付模型 + +采集器只生产事实,reconcile 层只生产派生状态和冲突,HTTP/API/export 只消费已完成的不可变快照。快照至少包含: + +```text +schema_version, generation_id, revision_id, generated_at, collection_duration +repository { root, branch, head, dirty } +sources[] { name, status, observed_at, last_success_at, error } +facts[] { subject, field, value, provenance, source_ref, observed_at, confidence } +conflicts[] { subject, field, kind, fact_refs[] } +documents[], modules[], tasks[], git[], environments[] +``` + +`generation_id` 唯一标识一次采集,包含采集批次语义;`revision_id` 只由 schema、仓库身份和规范化的非易变事实、冲突、来源状态确定,明确排除 `generated_at`、`observed_at` 和采集耗时。HTML 与 JSON 必须展示同一对标识,HTTP ETag 和前端是否重绘使用 `revision_id`。一期不读取旧 schema 的外部 JSON;离线 HTML 自带对应 renderer,因此不依赖未来二进制迁移。 + +#### KTD-3:专用解析器与容错索引并存 + +对 CURRENT、FEATURE-MATRIX、gap-matrix、任务 front matter 和 module boundaries 编写契约化解析器。文档层只索引 promotion receipt 的路径与链接,结构化部署凭证由运行态采集器唯一负责。对其余 Markdown 只提取安全元数据、标题、正文与链接。专用解析器失败时保留原文入口并标记 degraded,不凭模糊规则编造状态。 + +#### KTD-4:命令执行采用固定 argv 和超时 + +Git 与 Docker 采集通过注入式 command runner 执行固定程序和分离 argv,不通过 shell 拼接。`--repo` 先转为绝对、symlink-resolved 的 Git 根并作为单个参数传递。每个来源默认 2 秒超时;错误输出做长度限制和敏感字段清理。Git 使用 porcelain/plumbing 输出,例如 `status --porcelain=v2`、`worktree list --porcelain`、`for-each-ref`、`rev-list`、`merge-base`。 + +#### KTD-5:一期采用短轮询,不引入持久数据库 + +服务端由单一采集协调器按 TTL 触发完整重采集,2–5 秒刷新;同一仓库最多一轮采集在途,请求只读取当前快照,不直接叠加扫描。每轮带单调 generation 序号和整轮 deadline,只有当前有效轮次能原子发布。页面使用 ETag/`revision_id` 避免重复传输。last-known 只在同一 `serve` 进程内有效;冷启动或单次 export 首次采集失败时显示 unknown/error,不伪造历史值。文件监听、SSE 通知、持久缓存和历史数据库均后置。 + +#### KTD-6:安全阅读优先于富渲染 + +在线详情页一期将 Markdown 全部作为纯文本安全转义,并保留换行和空白;标题、列表、链接、代码、图片和 Mermaid 均显示源码,不生成 HTML 节点或发起资源请求。静态导出内嵌同一正文和内部导航。所有文档 ID 都是规范化仓库相对路径,只允许 `.doc/**/*.md` 及明确列入 allowlist 的根 Markdown;读取前后都校验解析路径,直接拒绝 symlink,并对 `.git`、`.env*`、`.secrets`、`data` 等敏感路径执行最终目标 denylist。 + +#### KTD-7:导出为自包含、有限尺寸的证据快照 + +导出将模型、CSS、JS 和允许的 Markdown 正文写入单 HTML,采用临时文件加 rename 原子落盘,默认 `0600`。数据使用 HTML-safe JSON 或等价编码封装,正文只通过 `textContent`/DOM API 写入;CSP 禁止网络、外部资源和内联事件执行。默认只嵌入 Git tracked、allowlist 内的 Markdown;untracked Markdown 只列元数据,除非用户显式选择。导出前输出 inclusion manifest 与敏感模式告警;`.doc/**/assets`、数据库、构建产物和二进制报告不嵌入。 + +### 建议目录 + +```text +cmd/engineering-dashboard/ + main.go +internal/engineeringdashboard/ + model.go + collector.go + source_docs.go + source_git.go + source_runtime.go + reconcile.go + server.go + export.go + assets/ + index.html + app.js + app.css + testdata/ + docs/ + git/ + runtime/ +scripts/ + engineering-dashboard.sh +.doc/test-reports/ + YYYY-MM-DD-engineering-dashboard-test-report.md +``` + +### 风险与缓解 + +| 风险 | 缓解 | +|---|---| +| 文档结构并不完全统一 | 只对 SSOT 和任务 front matter 使用严格契约;其余文档容错索引并暴露解析告警。 | +| 看板制造“假完成” | 多轴 facets、事实 provenance、验证身份绑定、冲突不覆盖、真实反例测试。 | +| Docker/Git 卡住或不可用 | 独立超时、并行采集、last-known 状态、degraded 页面。 | +| 文档 XSS 或路径穿越 | 纯文本渲染、HTML-safe 数据封装、CSP、路径 allowlist、拒绝 symlink、最终目标敏感路径 denylist。 | +| `.doc` 附件导致扫描/导出失控 | 默认跳过 assets 内容,只采集元数据;设置单文档和总正文上限并报告截断。 | +| 导出副本离开本机后 `0600` 不再提供保护 | 页面首部标明敏感快照、仓库身份和生成时间;输出 inclusion manifest,交接者自行控制接收者、保留期与删除。 | +| worktree 与主 checkout 混淆 | 只管理 `--repo` 指定/解析出的根,页面常驻显示根与 HEAD;其他 worktree 仅作为 Git 元数据。 | +| 本地 remote refs 被误认为远端实时状态 | 显示 refs 观测时间和“未联网刷新”,不自动 fetch。 | +| 与产品 `system-admin-console` 命名冲突 | CLI、包和文档统一使用 `engineering-dashboard` / “个人工程中控台”。 | + +--- + +## Implementation Units + +### U1:冻结快照、事实与状态契约 + +**目标**:先建立不会把计划、实现、验证、部署混为一谈的模型与测试夹具。 + +**覆盖需求**:R4、R8、R10、R12、R13;承接 PKD-2 的同源快照决策。 + +**依赖**:无。 + +**文件**: + +- `internal/engineeringdashboard/model.go` +- `internal/engineeringdashboard/model_test.go` +- `internal/engineeringdashboard/testdata/**` + +**实施要点**: + +1. 定义版本化 `Snapshot`、`SourceState`、`Fact`、`Conflict`、`TaskState`、`AgentClaim`、`EnvironmentState`。 +2. provenance 固定为 `declared|observed|verified|inferred`;freshness 固定为 `fresh|stale|error|unknown`。 +3. 状态使用可组合 facets:Git 分为 branch/worktree/merge/cleanliness/ahead-behind;验证分为 result/freshness/subject identity;部署分为 container/readiness/services/artifact identity。 +4. TaskState 保留 owner、agent kind、claim、mutex、声明分支、观测 worktree、最近活动时间及证据;声明与观测不可互相提升。 +5. 建立 rdev、dev ahead、main events restarting、CURRENT/worktree 不匹配等夹具。 + +**测试场景**: + +- 文档 `done` 但无 merge/deploy 时,Git 与部署 facets 不改变; +- `claimed` 但无可观测运行信号时,Agent 活动保持 unknown;Herdr 与产品 PI Runner 不共享类型; +- 同字段多来源不一致时生成 conflict 并保留所有 fact 引用; +- JSON round-trip 保留 schema 与 snapshot identity; +- 枚举未知值失败且错误可定位。 + +**完成证据**:模型测试通过,schema 示例落入 Go fixture,不存在页面专用的第二套状态类型。 + +### U2:实现文档图谱与领域解析器 + +**目标**:将 `.doc` taxonomy、模块、任务、测试和链接转换为可追溯文档图谱。 + +**覆盖需求**:R3、R7、R10、R11、R14。 + +**依赖**:U1。 + +**文件**: + +- `internal/engineeringdashboard/source_docs.go` +- `internal/engineeringdashboard/docs_test.go` + +**实施要点**: + +1. 扫描 allowlist 内 Markdown,默认跳过任何 `assets` 目录正文;记录附件元数据。 +2. 解析 front matter、标题、显式 Markdown 链接和反向链接。 +3. 为 CURRENT、FEATURE-MATRIX、gap-matrix、任务文、module boundaries 和 test report 建立专用适配器;receipt 只在文档图谱中作为文档与链接,结构化解析归 U4。 +4. 区分 `broken`、`orphan`、`unindexed`;slug 相似只形成候选关系。 +5. 规范化路径并阻止 `..`、绝对路径和任何 symlink;最终目标再次应用扩展名 allowlist 与敏感路径 denylist。 + +**测试场景**: + +- taxonomy 内文档可检索且反向链接正确; +- broken link 不导致整个索引失败; +- front matter 缺失或非标准时产生告警并仍可阅读; +- assets 不读正文,越界 symlink 被拒绝; +- rdev 文档链被发现,但没有被推断为已实现。 + +**完成证据**:文档索引能从 fixture 生成稳定 JSON,路径安全负例通过。 + +### U3:实现 Git 与 worktree 采集 + +**目标**:以只读 Git plumbing 形成可复现的分支、提交和 worktree 观测。 + +**覆盖需求**:R1、R4、R5、R10、R14。 + +**依赖**:U1。 + +**文件**: + +- `internal/engineeringdashboard/source_git.go` +- `internal/engineeringdashboard/git_test.go` + +**实施要点**: + +1. 抽象 command runner,固定 argv、工作目录、超时和输出上限。 +2. 收集当前分支/HEAD、dirty/untracked、local/remote-tracking refs、ahead/behind、merge-base 和 worktree。 +3. 不执行 fetch;remote tracking ref 始终带本地观测时间与免责声明。 +4. 对账任务声明的 branch/worktree,但不因路径名相似而认定一致。 + +**测试场景**: + +- 本地分支领先 origin 时显示 ahead 且不显示“远端可用”; +- worktree 目录名与真实检出分支不一致时生成 drift; +- 命令超时、Git 不存在、非 Git 目录时单源 degraded; +- runner 记录中不存在 shell、fetch、push 或写命令。 + +**完成证据**:临时 Git 仓库集成测试和 fake runner 单元测试通过。 + +### U4:实现运行态与部署证据采集 + +**目标**:按环境独立呈现容器、应用 readiness、服务完整性、schema、镜像和晋升凭证。 + +**覆盖需求**:R4、R6、R10、R14。 + +**依赖**:U1。 + +**文件**: + +- `internal/engineeringdashboard/source_runtime.go` +- `internal/engineeringdashboard/runtime_test.go` + +**实施要点**: + +1. 从仓库内批准的 Compose 静态配置和 allowlist labels 映射环境与服务;无法明确映射时显示 unknown,不用名称猜测。容器 JSON 只提取容器名、image/digest、状态、restart count、health,以及 allowlist 内的 Compose project/service labels;不复制 Env、Mounts、完整 NetworkSettings 或任意 labels。 +2. 对 allowlist 中的本地 `/ready` 端点执行短超时、响应大小受限的请求;禁用重定向,或对每一跳重新校验 scheme、解析后 IP、端口和 host。 +3. 解析 promotion receipt/candidate image 等仓库证据,保留 artifact identity。 +4. 将 container health、app readiness、required-service completeness 分开建模。 + +**测试场景**: + +- app ready 且 events restarting 时同时保留两项状态; +- Docker 不可用或 HTTP 超时时,其他数据源仍可输出; +- receipt 与容器 image 不一致时生成 deployment drift;容器可达但 identity 缺失时标记“运行可见、版本未验证”; +- readiness 重定向到外网或非 allowlist 本地端口时拒绝访问; +- 输出中不存在容器环境变量或凭证。 + +**完成证据**:fixture 和 fake HTTP/Docker runner 测试通过,main 分叉场景被覆盖。 + +### U5:实现对账、派生视图与新鲜度 + +**目标**:把来源事实组合成用户可判断的视图,但不替来源做未经定义的裁决。 + +**覆盖需求**:R3、R4、R6、R8、R9、R10、R13。 + +**依赖**:U2、U3、U4。 + +**文件**: + +- `internal/engineeringdashboard/reconcile.go` +- `internal/engineeringdashboard/reconcile_test.go` + +**实施要点**: + +1. 按领域 SSOT 规则构建 module、task、environment 和 evidence-chain 视图。 +2. 实现 conflict 分类:declared-vs-observed、task-vs-git、test-vs-subject、receipt-vs-runtime、stale-source、missing-evidence。 +3. 快照构建期间每个来源独立记录 `observed_at`、持续时间和错误;成功后一次性发布。 +4. 失败源只可复用同一 serve 进程的 last-known,并标注 stale、age 与 last-success;冷启动无缓存时为 unknown/error。过期数据不隐藏,但按年龄提升告警级别。 +5. 测试报告只有在 commit/artifact identity 与当前 subject 对齐时才能派生 pass;身份缺失为 unbound,身份不匹配为 stale/drift。 + +**测试场景**: + +- 三个真实样本得到预期多轴状态; +- 同一组输入顺序变化不改变规范化快照; +- 单源失败不会删除其他源,也不会重写 last-known 的观测时间;冷启动失败没有伪造 last-known; +- 旧测试报告、新 HEAD 与不同 image 不会派生当前验证通过; +- 恢复后 stale 自动清除并产生新 snapshot ID。 + +**完成证据**:reconcile table tests 覆盖一致、漂移、缺失、过期和恢复。 + +### U6:实现本地服务、总览与文档导航 + +**目标**:提供能快速判断异常并下钻证据的本地只读页面和 JSON。 + +**覆盖需求**:R1、R2、R3、R7–R10、R12–R14;承接 PKD-1 和 PKD-2。 + +**依赖**:U5。 + +**文件**: + +- `internal/engineeringdashboard/server.go` +- `internal/engineeringdashboard/server_test.go` +- `internal/engineeringdashboard/assets/index.html` +- `internal/engineeringdashboard/assets/app.js` +- `internal/engineeringdashboard/assets/app.css` +- `cmd/engineering-dashboard/main.go` + +**实施要点**: + +1. `serve --repo --addr 127.0.0.1:`;一期无条件拒绝所有非 loopback 地址,包括通配、IPv6 非 loopback 和显式传入值。 +2. 暴露 `/api/v1/snapshot`、`/api/v1/doc/`、`/healthz`;所有接口只读。严格校验 Host/Origin/Fetch Metadata,不信任代理转发头;页面和 API 使用每次启动生成的不可预测访问令牌,降低 DNS rebinding 与跨站读取风险。 +3. 页面分为总览、架构/模块、任务、Git、部署、文档和证据链。 +4. 自动刷新完整快照,保留当前筛选、滚动位置、焦点和选中文档;ETag 未变化时不重绘。 +5. 明确一级视图状态:initial loading、fresh empty、fresh success、partial/degraded with last-known、hard error without cache、refreshing、recovered。刷新中继续展示上一完整快照,直到下一版本原子替换。 +6. 搜索覆盖标题、仓库相对路径、front matter 和未截断正文;不同维度用 AND、同一维度多选用 OR,默认先排当前任务和异常证据,再按匹配度与路径稳定排序。筛选、查询和选中文档写入 URL 参数,支持浏览器前进/后退与深链。 +7. 桌面优先;窄屏下侧栏变为可关闭抽屉,宽表格保留可横向滚动的语义表头,详情区单列。所有功能可键盘完成,使用 landmark/标题/表格语义,drift/degraded 不只靠颜色表达,自动刷新通过非打断式 live region 报告且不抢焦点。 +8. Markdown 正文只进入文本节点;在线页与导出页均设置限制性 CSP,不执行 HTML、脚本、SVG、远程 URL 或事件属性。 + +**测试场景**: + +- 只监听 loopback,方法 allowlist 拒绝写请求;Host/Origin/token、DNS rebinding 和跨 Origin 负例被拒绝; +- HTML 与 JSON 的 generation/revision identity 一致; +- 文档 ID 越界、symlink 越界、未知 ID 返回安全错误; +- degraded source 在总览可见且页面仍可导航; +- 任务状态四轴、drift 和来源时间在浏览器中可见。 +- 首次加载、空数据、无缓存错误、刷新、恢复状态可区分;键盘、焦点、语义结构、窄屏和非颜色告警验收通过。 +- 搜索组合逻辑、排序、无结果、URL 深链和刷新后的视图保持通过。 + +**完成证据**:HTTP tests、基本无障碍检查和真实浏览器验收通过。 + +### U7:实现离线导出、启动脚本与治理文档 + +**目标**:生成安全、可携带、可追踪的单文件快照,并完成仓库治理接线。 + +**覆盖需求**:R1、R8、R9、R11、R14;承接 PKD-1 和 PKD-2。 + +**依赖**:U6。 + +**文件**: + +- `internal/engineeringdashboard/export.go` +- `internal/engineeringdashboard/export_test.go` +- `scripts/engineering-dashboard.sh` +- `.doc/status/FEATURE-MATRIX.md` +- `.doc/ops/CURRENT.md` +- `.doc/test-reports/YYYY-MM-DD-engineering-dashboard-test-report.md` + +**实施要点**: + +1. `export --repo --output ` 构建一次不可变快照并原子写入;首次采集失败的来源显示 unknown/error。 +2. 以 HTML-safe JSON/等价编码内嵌允许的 Markdown 正文、索引和页面资源;附件保持元数据与相对链接,所有正文只写入文本节点。 +3. 默认权限 `0600`,输出页显示生成时间、repo、HEAD、snapshot ID 和 degraded/drift。 +4. 薄脚本只负责定位仓库与调用 Go CLI,不隐藏参数或执行安装/部署副作用。 +5. 实施期间按仓库规范更新 CURRENT、FEATURE-MATRIX 和 test report;完成后再迁移任务生命周期状态。 + +**测试场景**: + +- 导出文件脱离仓库后仍能搜索和阅读 Markdown; +- 导出中不含附件字节、绝对凭证路径、env 值或远程请求;``、事件属性、SVG、危险 URL scheme 和 Unicode 边界载荷不能逃逸数据上下文; +- 默认 tracked/untracked 与敏感模式策略、inclusion manifest 和覆盖清单符合契约; +- 采集部分失败时仍能导出并保留错误; +- 输出中 JSON/HTML snapshot identity 一致,权限符合预期; +- 大文档和总正文上限触发时明确标记截断。 + +**完成证据**:export tests、离线浏览器验收和治理文档回链完整。 + +--- + +## Verification Contract + +### 自动验证 + +实施阶段至少运行: + +```bash +go test ./internal/engineeringdashboard/... +go test ./cmd/engineering-dashboard/... +go vet ./... +go test -short ./... +git diff --check +``` + +涉及共享容器的只读验收也必须遵守 CURRENT 集成槽规则;中控台本身不得为测试启动、停止或重建共享容器。 + +### 契约验证矩阵 + +| 领域 | 必须证明 | +|---|---| +| 模型 | 计划、Git facets、验证、部署 facets 互不覆盖;Agent 声明和运行观测分离;所有派生结论可追溯到 facts。 | +| 文档 | taxonomy、全文检索、反链、broken/unindexed、路径安全与 assets 排除。 | +| Git | ahead/behind、worktree、dirty、local refs 新鲜度和无隐式 fetch。 | +| 部署 | container/app/service 三类信号分离,receipt/artifact 身份可核对。 | +| 降级 | 每个源超时、错误、stale、恢复都不会让整页不可用。 | +| 实时 | 变化在目标时间内形成完整新快照,不出现半新半旧卡片。 | +| 导出 | 单文件离线可读、默认 `0600`、不嵌 assets、不产生网络请求。 | +| 安全 | loopback、Host/Origin/startup token、read-only methods、命令 allowlist、路径边界、Docker 字段 allowlist、CSP 与输出脱敏。 | + +### 浏览器验收 + +1. 从总览进入 rdev,确认“计划/实施/验证/实装”四轴与源文档链接均可见。 +2. 在仓库外复制导出 HTML,断网打开并全文搜索、阅读至少一份设计与一份测试报告。 +3. 制造 fixture 中的 CURRENT/Docker/readiness 冲突,确认页面保留多值且不出现综合绿灯。 +4. 切换 fixture 快照,确认页面刷新后筛选条件和当前文档保持不变。 +5. 尝试路径穿越、未知文档、POST、DNS rebinding、跨 Origin、readiness 重定向和含原始 HTML/脚本逃逸载荷的 Markdown,确认均不能越界、外联或执行。 + +### 性能与容量验证 + +- 记录当前 `.doc` 规模下的冷启动、增量刷新、JSON 大小和 HTML 导出大小。 +- 建议一期门槛:UI shell 与来源状态在 2 秒内可交互,P95 完整快照重建不超过 5 秒,单源超时不超过 2 秒;基线不达标时必须记录实测并拆分后台正文索引,不能伪造 PASS。 +- 导出达到配置上限时必须显式列出被截断文档,不允许静默丢失。 + +--- + +## Definition of Done + +- [ ] `serve` 和 `export` 在显式仓库根下可重复运行,且没有产品后台依赖。 +- [ ] 实时 HTML、离线 HTML 与 JSON 使用同一版本化快照契约。 +- [ ] `generation_id` 标识采集实例,`revision_id` 标识内容版本并驱动 ETag;无内容变化不会触发重绘。 +- [ ] 架构、模块、功能、任务、Git、部署、文档和证据链均可从页面访问。 +- [ ] 计划、实施、验证、实装四轴独立,rdev、dev ahead、main events 分叉样本通过。 +- [ ] Agent 认领与可观测活动分开,Herdr 工程 Agent 与产品 PI Agent Runner 不混称。 +- [ ] 所有 drift、degraded、stale 都带来源和时间,不存在未经定义的综合绿灯。 +- [ ] 默认 loopback、Host/Origin/startup token、只读命令、路径边界、脱敏、CSP 和 Markdown 安全测试通过。 +- [ ] 首次加载、空数据、部分失败、无缓存失败、恢复、键盘、窄屏和 URL 深链验收通过。 +- [ ] 导出 HTML 脱离仓库可读 Markdown,且不包含二进制 assets 或凭证数据。 +- [ ] 单元、集成、全仓短测、浏览器验收和 `git diff --check` 均有真实输出。 +- [ ] `.doc/test-reports` 报告回链任务文,FEATURE-MATRIX 与 CURRENT 按实际状态更新。 +- [ ] 没有遗留调试入口、写操作按钮、未使用的替代模型或与产品后台重名的组件。 + +--- + +## Appendix + +### 一期交付边界 + +一期交付:本地只读服务、版本化快照 JSON、架构/任务/Git/部署/文档视图、四轴状态、drift/degraded、全文搜索、安全文档阅读和单 HTML 导出。 + +后续评估:跨快照历史趋势、远端托管/CI 聚合、受限富文本与 Mermaid、跨仓库聚合、面向 Agent 的筛选查询 API。 + +永久排除:在页面中修改文档、切换或推送分支、控制 Agent、执行部署、读取密钥、成为产品后台模块。 + +### 方案依据 + +- `.doc/README.md` 定义的文档 taxonomy 与生命周期; +- `.doc/ops/CURRENT.md` 的焦点、认领、集成槽与环境声明; +- `.doc/status/FEATURE-MATRIX.md` 的模块完成度口径; +- `.doc/remediation/gap-matrix.md` 的发布门禁口径; +- `.doc/system-design/README.md` 与模块边界清单; +- Git/worktree、Docker/Compose、`/ready` 和 promotion receipts 的当前可观测能力。