From 6c3c676ca5518af80b74cc9997456909b57d9a65 Mon Sep 17 00:00:00 2001 From: yourname Date: Thu, 9 Apr 2026 00:43:05 +0000 Subject: [PATCH] =?UTF-8?q?sync:=20d8d-prototype=20=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=BC=BA=E9=99=B7=E9=93=BE=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20=E2=80=94=20step-05/06/07?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 5: 引入 doc-registry.js 单一数据源,消除双写不一致 Step 6: server.js 路由白名单动态覆盖所有文档路径前缀 Step 7: 新增文档内容深度验证,强制遍历每个 view-doc 页面 根因: 旧版 Step 5 双写 DOC_MAP、Step 6 硬编码路由、 Step 7 只截图列表页不深入文档内容,导致文档 404 未被发现 --- .../steps/step-05-generate-docs.md | 112 +++++++++++------ .../steps/step-06-generate-server.md | 67 +++++++++-- .../d8d-prototype/steps/step-07-validate.md | 113 +++++++++++++++--- 3 files changed, 233 insertions(+), 59 deletions(-) diff --git a/.claude/skills/d8d-prototype/steps/step-05-generate-docs.md b/.claude/skills/d8d-prototype/steps/step-05-generate-docs.md index 87078c6..803d32b 100644 --- a/.claude/skills/d8d-prototype/steps/step-05-generate-docs.md +++ b/.claude/skills/d8d-prototype/steps/step-05-generate-docs.md @@ -6,6 +6,17 @@ 生成 `docs-index.html` 文档首页和 `view-doc.html` 文档查看器,使用前端 JavaScript 渲染 Markdown 文件。 +## 铁律:单一数据源(Single Source of Truth) + +**docs-index.html 和 view-doc.html 必须共享同一份文档注册表**,避免双写不一致。 + +架构: +``` +doc-registry.js ← 唯一的文档映射数据源 + ↓ 引用 ↓ 引用 +docs-index.html view-doc.html +``` + ## 执行序列 ### 5.1 扫描项目文档 @@ -15,8 +26,9 @@ - `_bmad-output/planning-artifacts/` — 规划文档(PRD、架构、UX 设计、产品简报等) - `_bmad-output/implementation-artifacts/` — 实现产物(Story 规格、旅程验证、Pipeline 状态等,**可能不存在**) - `_bmad-output/` 根目录下的文件 -- 项目根目录的 README.md、DESIGN.md 等 -- docs/ 目录下的文档 +- 项目根目录的 `TASKS.md`、`CLAUDE.md` 等 +- `database/` 目录下的文档 +- `docs/` 目录下的文档 #### 文档分组策略 @@ -28,50 +40,81 @@ | **Implementation - Story 规格** | `implementation-artifacts/` | `X-Y-title.md` | 蓝色 | | **Implementation - 旅程验证** | `implementation-artifacts/` | `epic-N-journeys.md` | 绿色 | | **Implementation - Pipeline** | `implementation-artifacts/` | `pipeline-*.md`, `pipeline-*.yaml`, `sprint-status.yaml` | 橙色 | -| **Other** | 项目根目录 | README.md 等 | 灰色 | +| **项目文档** | 项目根目录 | TASKS.md, CLAUDE.md, README.md 等 | 灰色 | +| **数据库** | `database/` | SCHEMA_DESIGN.md 等 | 紫色 | -### 5.2 生成 docs-index.html +### 5.2 生成 doc-registry.js(单一数据源) -文档首页功能: -- **文档卡片网格展示**(1-3 列自适应) -- 每张卡片显示文档标题、描述、图标 -- 查看按钮链接到 `view-doc.html?doc=文档路径` -- 使用 Tailwind CSS 响应式布局 +生成独立的 `doc-registry.js` 文件,作为文档映射的唯一数据源: -**文档链接格式**: -```html - - PRD 文档 - +```javascript +// doc-registry.js — 文档注册表(单一数据源) +// docs-index.html 和 view-doc.html 共同引用此文件 +const DOC_REGISTRY = { + // Planning 规划文档 + 'prd': { title: '产品需求文档 (PRD)', desc: '产品愿景、目标用户、功能需求', path: '../_bmad-output/planning-artifacts/prd.md', group: 'planning', icon: 'fa-file-alt', color: 'blue' }, + // ... 其他文档条目 +}; + +// 按分组返回文档列表 +function getDocsByGroup(group) { + return Object.entries(DOC_REGISTRY).filter(([, v]) => v.group === group); +} + +// 获取所有唯一路径前缀(供 server.js 路由使用) +function getPathPrefixes() { + const prefixes = new Set(); + Object.values(DOC_REGISTRY).forEach(doc => { + const match = doc.path.match(/^\.\.\/([^/]+)\//); + if (match) prefixes.add('/' + match[1] + '/'); + else if (doc.path.match(/^\.\.\/[^/]+\.md$/)) { + const fileName = doc.path.replace(/^\.\.\//, '/'); + prefixes.add(fileName); + } + }); + return Array.from(prefixes); +} ``` -### 5.3 生成 view-doc.html +### 5.3 生成 docs-index.html + +文档首页功能: +- **引入 doc-registry.js**:`` +- **文档卡片网格展示**(1-3 列自适应) +- 每张卡片显示文档标题、描述、图标 +- 查看按钮链接到 `view-doc.html?doc=` +- 使用 Tailwind CSS 响应式布局 +- **遍历 DOC_REGISTRY 动态生成卡片**,而非硬编码 + +### 5.4 生成 view-doc.html 文档查看器功能: -1. **顶部导航栏**:返回按钮、文档标题、复制链接、打印功能 -2. **加载状态**:Spinner 动画 + 加载提示 -3. **Markdown 渲染**:使用 markdown-it 库(html: false 防止 XSS) -4. **JavaScript 核心逻辑**: - - 从 URL 参数获取文档名(?doc=xxx.md) - - 验证路径安全性(防止路径遍历) - - 并行尝试多个路径(Promise.all 优化) +1. **引入 doc-registry.js**:`` +2. **顶部导航栏**:返回按钮、文档标题、复制链接、打印功能 +3. **加载状态**:Spinner 动画 + 加载提示 +4. **Markdown 渲染**:使用 markdown-it 库(html: false 防止 XSS) +5. **JavaScript 核心逻辑**: + - 从 URL 参数获取文档 key(?doc=xxx) + - 从 DOC_REGISTRY 查找对应路径 + - fetch 加载 markdown 内容并渲染 + - 支持 YAML 文件的代码块展示 -### 5.4 安全设计 +### 5.5 增量更新时的同步保证 + +当增量更新添加新文档时: + +1. **只修改 doc-registry.js** — 添加新条目 +2. docs-index.html 和 view-doc.html 无需修改(动态读取注册表) +3. **无需同步两个文件**,从根本上消除不一致风险 + +### 5.6 安全设计 - markdown-it 配置 `html: false` 防止 XSS - 验证文档路径,防止路径遍历攻击 - 使用 `textContent` 而不是 `innerHTML` -### 5.5 方案优势 - -| 特性 | 独立页面方案 ⭐ | 页面内展开方案 | -|------|----------------|--------------| -| URL 可分享 | ✅ | ❌ | -| 支持书签 | ✅ | ❌ | -| 代码维护 | ✅ 逻辑集中 | ❌ 每页都要写 | - -### 5.6 更新状态文件 +### 5.7 更新状态文件 - 勾选 docs-index.html 和 view-doc.html - 更新 last_updated @@ -79,8 +122,9 @@ ## 完成标志 -- [ ] docs-index.html 已生成,包含所有文档的卡片 -- [ ] view-doc.html 已生成,支持 Markdown 渲染 +- [ ] doc-registry.js 已生成,包含所有文档映射 +- [ ] docs-index.html 已生成,通过 doc-registry.js 动态渲染 +- [ ] view-doc.html 已生成,通过 doc-registry.js 查找文档路径 - [ ] 文档链接路径正确 - [ ] 安全措施已实施 - [ ] 状态文件已更新 diff --git a/.claude/skills/d8d-prototype/steps/step-06-generate-server.md b/.claude/skills/d8d-prototype/steps/step-06-generate-server.md index a63fe6a..5b13b4b 100644 --- a/.claude/skills/d8d-prototype/steps/step-06-generate-server.md +++ b/.claude/skills/d8d-prototype/steps/step-06-generate-server.md @@ -6,9 +6,24 @@ 生成 `server.js` 文件,提供静态文件服务,支持本地预览原型系统。 +## 铁律:路由白名单必须覆盖所有文档路径 + +server.js 的路由必须覆盖 doc-registry.js 中所有文档的路径前缀,**不能硬编码**只支持 `/_bmad-output/`。 + ## 执行序列 -### 6.1 生成 server.js +### 6.1 扫描 doc-registry.js 提取路径前缀 + +在生成 server.js 之前,先分析 doc-registry.js 中所有文档的路径: + +```javascript +// 从 doc-registry.js 提取唯一路径前缀 +const docPaths = Object.values(DOC_REGISTRY).map(d => d.path); +// 例如: ['../_bmad-output/...', '../TASKS.md', '../database/SCHEMA_DESIGN.md', '../CLAUDE.md'] +// 提取前缀: ['/_bmad-output/', '/TASKS.md', '/CLAUDE.md', '/database/'] +``` + +### 6.2 生成 server.js ```javascript const http = require('http'); @@ -38,6 +53,16 @@ const MIME_TYPES = { '.yml': 'text/yaml; charset=utf-8', }; +// 动态生成父目录路由白名单 — 基于 doc-registry.js 中的路径 +// 自动提取所有 ../xxx/ 前缀和 ../xxx.md 文件名 +const PARENT_ROUTE_WHITELIST = [ + '/_bmad-output/', // BMAD 输出文档(默认) + '/database/', // 数据库文档 + '/TASKS.md', // 任务记录 + '/CLAUDE.md', // 项目说明 + // ⚠️ 增量更新时:如果 doc-registry.js 新增了其他父目录路径,必须在此添加 +]; + const server = http.createServer((req, res) => { let urlPath = req.url.split('?')[0]; if (urlPath === '/') urlPath = '/index.html'; @@ -46,8 +71,11 @@ const server = http.createServer((req, res) => { const decodedPath = decodeURIComponent(urlPath); const normalizedPath = path.normalize(decodedPath).replace(/^(\.\.[\/\\])+/, ''); - // 支持父目录访问(BMAD 输出文档) - const filePath = normalizedPath.startsWith('/_bmad-output/') + // 动态路由:白名单中的前缀/文件映射到父目录 + const useParentDir = PARENT_ROUTE_WHITELIST.some( + prefix => normalizedPath.startsWith(prefix) || normalizedPath === prefix + ); + const filePath = useParentDir ? path.join(PARENT_DIR, normalizedPath) : path.join(PROTOTYPE_DIR, normalizedPath); @@ -73,14 +101,24 @@ const server = http.createServer((req, res) => { }); server.listen(PORT, () => { - console.log(`\n🚀 Prototype server running at:`); + console.log(`\n Prototype server running at:`); console.log(` http://localhost:${PORT}/`); - console.log(`\n📁 Serving from: ${PROTOTYPE_DIR}`); + console.log(`\n Serving from: ${PROTOTYPE_DIR}`); console.log(` Press Ctrl+C to stop\n`); }); ``` -### 6.2 启动服务器 +### 6.3 关键改进说明 + +与旧版相比的变更: + +| 方面 | 旧版(有缺陷) | 新版(已修复) | +|------|--------------|--------------| +| 路由策略 | 硬编码只有 `/_bmad-output/` | 白名单数组,覆盖所有文档路径 | +| 与 Step 5 关系 | 无联动 | 要求扫描 doc-registry.js 提取前缀 | +| 增量更新 | 需手动改 server.js | 白名单有注释提醒,且有数组结构易于扩展 | + +### 6.4 启动服务器 使用 `nohup` 后台启动: @@ -95,7 +133,19 @@ curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/ # 应返回 200 ``` -### 6.3 更新状态文件 +### 6.5 验证路由覆盖 + +启动后,验证白名单中每个路径都能正常访问: + +```bash +# 对 PARENT_ROUTE_WHITELIST 中的每个条目验证 +curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/_bmad-output/planning-artifacts/prd.md +curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/database/SCHEMA_DESIGN.md +curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/TASKS.md +curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/CLAUDE.md +``` + +### 6.6 更新状态文件 - 勾选 server.js - 更新 last_updated @@ -107,7 +157,8 @@ curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/ - [ ] 服务器可正常启动 - [ ] 静态文件可正常访问 - [ ] .md 文件可正常返回 -- [ ] 父目录 BMAD 文档可访问 +- [ ] **PARENT_ROUTE_WHITELIST 覆盖 doc-registry.js 中所有路径前缀** +- [ ] **每个白名单路径 curl 验证返回 200** - [ ] 状态文件已更新 **完成后,加载 `steps/step-07-validate.md` 继续执行。** diff --git a/.claude/skills/d8d-prototype/steps/step-07-validate.md b/.claude/skills/d8d-prototype/steps/step-07-validate.md index 4aef781..2068941 100644 --- a/.claude/skills/d8d-prototype/steps/step-07-validate.md +++ b/.claude/skills/d8d-prototype/steps/step-07-validate.md @@ -13,27 +13,29 @@ - ❌ 只检查文件是否存在 - ❌ 跳过图片分析直接标记完成 - ❌ 自行声称"验证通过"而不提供截图证据 +- ❌ **只验证原型页面,跳过文档内容验证** **必须**按以下顺序执行: 1. Playwright 导航 → 截图(至少:平铺入口页 + 每个独立页面) -2. ZAI analyze_image 分析每张截图 -3. 发现问题 → 修复 → 重新截图验证 -4. 全部通过 → 写入验证报告 → 标记完成 +2. **文档内容深度验证** — 遍历每个 view-doc.html?doc=xxx 页面,确认内容加载成功 +3. ZAI analyze_image 分析每张截图 +4. 发现问题 → 修复 → 重新截图验证 +5. 全部通过 → 写入验证报告 → 标记完成 ## 执行序列 ### 7.1 验证流程 ``` -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ 1. 启动服务器 │ → │ 2. 遍历页面链接 │ → │ 3. 截图保存 │ -│ node server.js │ │ Playwright导航 │ │ 保存到screenshots│ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - ↓ - ┌─────────────────┐ - │ 4. AI分析验证 │ - │ 图片MCP+需求对比│ - └─────────────────┘ +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ +│ 1. 启动服务器 │ → │ 2. 遍历原型页面 │ → │ 3. 截图保存 │ +│ node server.js │ │ Playwright导航 │ │ 保存到screenshots│ +└─────────────────┘ └──────────────────┘ └─────────────────┘ + ↓ +┌─────────────────┐ ┌──────────────────┐ +│ 5. AI分析验证 │ ← │ 4. 文档内容验证 │ ← ⭐ 新增! +│ 图片MCP+需求对比│ │ 遍历每个view-doc │ +└─────────────────┘ └──────────────────┘ ``` ### 7.2 确保 server.js 已启动 @@ -45,7 +47,7 @@ curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/ cd prototype/ && nohup node server.js &>/dev/null & ``` -### 7.3 使用 Playwright MCP 验证 +### 7.3 使用 Playwright MCP 验证原型页面 ```javascript // 1. 启动浏览器,导航到入口 @@ -72,7 +74,68 @@ fullPage: true // - 截图保存 ``` -### 7.4 使用图片 MCP 分析截图 +### 7.4 文档内容深度验证 ⭐(强制步骤) + +**本步骤不可跳过!** 必须验证 docs-index.html 中列出的**每个文档**都能在 view-doc.html 中正常渲染内容。 + +#### 7.4.1 遍历所有文档链接 + +使用 Playwright `browser_run_code` 批量验证: + +```javascript +async (page) => { + // 1. 先导航到 docs-index.html 获取所有文档链接 + await page.goto('http://localhost:8080/docs-index.html'); + const docLinks = await page.evaluate(() => { + return Array.from(document.querySelectorAll('a[href*="view-doc.html"]')) + .map(a => new URL(a.href, 'http://localhost:8080').searchParams.get('doc')); + }); + + // 2. 逐个验证每个文档的内容加载 + const results = []; + for (const doc of docLinks) { + await page.goto(`http://localhost:8080/view-doc.html?doc=${doc}`, { waitUntil: 'networkidle', timeout: 10000 }); + const title = await page.title(); + const bodyText = await page.evaluate(() => { + const content = document.querySelector('.markdown-body') || document.body; + return content ? content.innerText.substring(0, 200) : ''; + }); + const hasError = await page.evaluate(() => !document.getElementById('error').classList.contains('hidden')); + await page.screenshot({ fullPage: true, path: `prototype/screenshots/doc-${doc}.png`, type: 'png', scale: 'css' }); + results.push({ doc, title, hasContent: bodyText.length > 20, hasError, preview: bodyText.substring(0, 80) }); + } + return JSON.stringify(results, null, 2); +} +``` + +#### 7.4.2 检查浏览器控制台错误 + +对每个文档页面检查控制台是否有 404 或加载失败错误: + +```javascript +mcp__playwright__browser_console_messages +level: error +// 检查是否有 "Failed to load resource" 或 "404" 错误 +``` + +#### 7.4.3 修复发现的问题 + +如果发现文档加载失败(hasContent=false 或 hasError=true),**必须立即修复**: + +1. **404 错误** → 检查 server.js 的 `PARENT_ROUTE_WHITELIST` 是否覆盖了该文档路径 +2. **DOC_MAP 缺失** → 检查 view-doc.html(或 doc-registry.js)是否有该文档条目 +3. **源文件不存在** → 从 docs-index.html 中移除该条目 + +修复后**重新验证**,直到所有文档内容加载成功。 + +#### 7.4.4 验证结果汇总格式 + +| 文档 key | 标题 | 内容加载 | 控制台错误 | 截图 | +|----------|------|---------|-----------|------| +| prd | 产品需求文档 | ✅ | 无 | ✅ | +| schema | 数据库 Schema | ❌→✅ | 404→修复 | ✅ | + +### 7.5 使用图片 MCP 分析截图 ```javascript // 分析页面布局和样式 @@ -87,7 +150,7 @@ actual_image_source: prototype/screenshots/actual.png prompt: 对比参考设计与实际实现,指出差异和问题 ``` -### 7.5 验证检查清单 +### 7.6 验证检查清单 #### 链接验证 - [ ] 所有导航链接可点击 @@ -109,6 +172,13 @@ prompt: 对比参考设计与实际实现,指出差异和问题 - [ ] 页面标签清晰可见 - [ ] 网格间距合理,视觉舒适 +#### 文档内容验证 ⭐⭐(新增,不可跳过) +- [ ] docs-index.html 中每个文档链接都已遍历 +- [ ] 每个 view-doc.html?doc=xxx 页面内容加载成功(hasContent=true) +- [ ] 无浏览器控制台 404 或加载失败错误 +- [ ] 每个 doc-registry.js 条目在 server.js 路由白名单中都有覆盖 +- [ ] 所有失败的文档已修复并重新验证通过 + #### 样式验证(通过图片 MCP) - [ ] 配色方案符合设计系统 - [ ] 字体大小合适 @@ -122,7 +192,7 @@ prompt: 对比参考设计与实际实现,指出差异和问题 - [ ] 交互流程正确 - [ ] 与需求文档描述一致 -### 7.6 生成验证报告 +### 7.7 生成验证报告 ```markdown # 原型验证报告 @@ -130,6 +200,7 @@ prompt: 对比参考设计与实际实现,指出差异和问题 ## 测试概览 - 测试时间: {date} - 测试页面: {total} 个 +- 测试文档: {doc_count} 个 - 截图数量: {count} 张 - 验证方式: Playwright MCP + Image MCP @@ -138,6 +209,12 @@ prompt: 对比参考设计与实际实现,指出差异和问题 |------|---------|---------|------| | index.html | ✅/❌ | ✅/❌ | ... | +## 文档内容验证结果 ⭐ +| 文档 | 标题 | 内容加载 | 控制台错误 | 修复状态 | +|------|------|---------|-----------|---------| +| prd | 产品需求文档 | ✅ | 无 | - | +| schema | 数据库 Schema | ✅ | 无 | 已修复路由 | + ## 平铺布局验证结果 ⭐ | 验证项 | 结果 | 说明 | |--------|------|------| @@ -158,7 +235,7 @@ prompt: 对比参考设计与实际实现,指出差异和问题 1. [问题描述] → [改进建议] ``` -### 7.7 最终状态更新 +### 7.8 最终状态更新 - 设置 `status: completed` - 将 step-07-validate 加入 completed_steps @@ -167,9 +244,11 @@ prompt: 对比参考设计与实际实现,指出差异和问题 ## 完成标志 - [ ] 所有页面已截图 +- [ ] **所有文档内容已验证加载成功** - [ ] 链接验证通过 - [ ] 平铺布局验证通过 - [ ] 样式分析完成 +- [ ] **文档内容验证无 404 错误** - [ ] 验证报告已生成 - [ ] 状态文件 status 已设为 completed -- 2.49.1