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