【本次核心 · S6-P21】 - 族层数据:families.json + 44 份契约注入 family/familyRole/familyParams; data.json / data.js / site/details 同步。13 族 / 44 成员 / 35 独立 → 概念组件 79→48。 - 79 个 slug 全保留、集合逐一不变(铁律 5 对外承诺未破);frameworks 仍 395 文件、薄壳仍 79。 - 归族判据为契约中可核对字段(semanticTypeCandidates 重叠 / anatomy 为同一骨架子集 / 变体维度同构 / doNotInvent 显式从属声明),每族 mergeBasis 写明依据,不按名字猜。 - 实现层合并(导航族端到端切片):tools/gen-family-impl.mjs 从 5 端模板生成 TopMenu / SideMenu / MixedNavigation 共 15 文件,参数 direction=top|side|mixed; 三份 CSS md5 完全相同 = 一份样式表服务三个组件。 - 新增 tools/gen-families.mjs、tools/gen-family-impl.mjs、tools/verify-families.mjs、 tools/lib/family-model.mjs、tools/lib/family-impl/nav-menu/*.tpl。 【同时清掉此前已完成但未提交的批次】 生成物(data.json / data.js / site/sources / site/components 薄壳 / sitemap.xml / tests 报告) 跨阶段交织,无法拆成互相自洽的多个提交,故按既有批量风格合并提交: - Package:三端可 import(S5-P18)+ 发布到私有 npm 源 - Docs site:导航语言改下拉(S5-P19)、详情页代码块默认展开、中英切换完整性 - Security:生产部署链审计修复(2026-09-19)+ 线上部署 - Theme modes 日间/夜间/自动;S1-P4 data.js 瘦身;S2-P5 暗色;S2-P6 跨端一致性; S2-P7 行为断言;S2-P9 FAQ;S3-P8 RTL;S3-P9 契约缺口解释层;S4-P12 发布流程 - 补入 tools/pack-deploy.mjs、run-site-smoke.mjs、verify-*.mjs,.dockerignore、 安全审计修复与待决策项.md 【验收】 - node tools/verify-families.mjs → OK: 族层端到端一致(13 族 / 44 成员 / 79 组件不变 / 395 文件不变) - node tools/verify-cross-platform.mjs → 79/79 identical(HEAD 基线 high 44) - node tools/run-regression.mjs → 100%(79/79 页,1017/1017 断言,N/A 34),连跑 8 次一致,0 超时 - 逐页实测:topmenu / sidemenu / mixednavigation 各 13/13,帧内 direction 参数正确,0 JS 错误 - 零运行时依赖 OK;build-site.ps1 ASCII-only OK 【未纳入】site/components/<slug>/ 平台薄壳 316 个 —— 历史从未跟踪且属构建产物,保持现状。
68 KiB
Aurora Admin · 长期路线图与任务包
文档性质:交给执行模型的工程规划。每个任务包自带验收命令与失败判据,拿到即可开工,不需要再问。 基线版本:v1.4.1(2026-09-11) 规划人:海鸥(规划师角色) 更新约定:任务完成时在本文件对应任务包下追加
✅ 完成于 <commit>,不要删原计划。
〇 · 交接约定(执行模型必读)
0.1 铁律(违反即返工)
- 零运行时依赖不可破:不引入 npm 运行时依赖。构建/测试脚本用 Node 内置能力或 PowerShell。
package.json的dependencies必须为空(devDependencies可用)。 build-site.ps1必须 ASCII-only:PS5.1 按 ANSI 读无 BOM 文件,脚本里出现非 ASCII 字面量会被损坏。中文文案放 UTF-8 模板。- 改结构要改模板:
tests/<slug>.html是生成物 —— 改它没用。改tests/_template.html/tests/_index_template.html后重跑run-tests.ps1。 - 改样式要改内嵌层:演示页的内嵌
<style>才是实际生效的样式(v1.4.0 教训:只改frameworks/*.css可能不生效,因为演示页不一定 link 它)。 data.json是 For Agents 的对外承诺:可以拆data.js,但data.json必须保持完整(一次请求拿到全部)。破坏它是 breaking change。- 单次改动后必须跑回归:
tests/_collect.html八次连跑,100% / 0 失败 / 0 超时才算过。
0.2 禁止事项(做过即需回滚)
| 禁止 | 原因 | 正确做法 |
|---|---|---|
在 frameworks/ 里直接手工改出"更好看"的样式 |
frameworks/ 是规范原文的忠实实现,改动会破坏契约一致性 |
要改样式先改令牌,或走任务包流程 |
用 git reset --hard / git checkout . 清理 |
会丢用户改动 | 用 git stash 或定向还原 |
删除或改写 CHANGELOG.md 的历史条目 |
那是变更事实记录 | 只追加 [Unreleased] 或新版本段 |
修改 tests/report.json 的数值来"通过"验收 |
报告是生成物,改它等于伪造证据 | 修实际问题后重跑生成 |
| 为了通过验收而放宽断言判据 | v1.3.1 曾出现"调松断言刷分"的诱惑 | 断言判据的修正必须附理由 + 反例 |
| 在规划未涉及的领域顺手重构 | 范围蔓延会让验收失焦 | 写成新任务包追加到本文件 |
0.3 环境与工具
| 项 | 说明 |
|---|---|
| 操作系统 | Windows(开发)/ Linux(CI)。跨平台写法见下方「跨平台注意」 |
| Shell | Git Bash(开发)/ bash(CI) |
| Node | ≥ 18(用到 node:sqlite 的任务需 ≥ 22) |
| 构建 | powershell -NoProfile -ExecutionPolicy Bypass -File build-site.ps1(Windows 专用) |
| 回归 | 浏览器打开 tests/_collect.html,或 node tools/run-regression.mjs(需 playwright) |
| 本地服务 | node site/dev-server.js(端口 3311) |
跨平台注意:
- 后台启动服务:Windows Git Bash 用
node site/dev-server.js &,CI 用node site/dev-server.js & sleep 3。不要用start或nohup。 - 路径分隔符:脚本里统一用 POSIX 风格(
/),Node 的path模块会处理。 - PowerShell 脚本只能在 Windows 跑。若 CI 需要构建,改写为 Node 脚本或加
runs-on: windows-latest。
0.4 失败升级路径
执行中遇到阻塞时,按这个顺序处理,不要停下来等:
| 情况 | 处理 |
|---|---|
| 验收命令本身有错(路径不对、语法不兼容) | 修正命令使其能真实反映目标,在交付说明里写明修正了什么、为什么 |
| 目标不可达(如依赖的服务/网络不可用) | 交付最强替代物 + 写明缺什么。例:无法 npm pack 则做本地 npm pack --dry-run 的等价检查 |
| 发现规划有事实错误(如"某字段占 89%"实际不是) | 以实测为准,修正规划并在本文件标注「⚠️ 规划修正」 |
| 发现规划未覆盖的新缺口 | 写成新任务包追加到本文件,不在原任务里顺手修 |
| 连续两次尝试同一路径都失败 | 换策略而非重试。在交付说明里记录两次失败的原因 |
0.5 交付说明模板(每个任务包完成时提交)
## <任务 ID> 完成说明
**验收输出**(原样粘贴命令输出,不要转述):
<命令> <输出>
**改动文件**:
- <路径> — <做了什么>
**规划偏差**(若有):
- <规划里写的> → <实际的>,原因:<...>
**发现的新问题**(若有):
- <描述> → 已追加为任务包 <ID>
**回归**:100%(<总断言>/<通过>)/ 八次连跑一致 / 0 超时
一 · 现状基线(全部为实测数据)
1.1 资产规模
| 项 | 数量 | 测量方式 |
|---|---|---|
| 组件 | 79 | components/index.json |
| 5 端实现文件 | 395 | ls frameworks/*.{html,css,jsx,vue2.vue,vue3.vue} 各 79 |
| 契约 JSON | 79(全量) | ls .design_library/aurora-admin/components/*.json |
| 契约 usageHints | 358 条 | 遍历契约累加 |
| 契约 unknowns | 139 条 | 同上(现成的 FAQ 素材) |
| 契约 doNotInvent | 101 条 | 同上 |
| 设计令牌 | 75 个 | site/data.json → tokens.length |
| i18n 字典 | 330 条 | site/i18n.js |
| 测试断言 | 961 条(12.2/页) | tests/report.json |
| 文档站路由 | 9 个 | home/overview/guide/design/changelog/agents/component/scenario/tests |
1.2 质量指标
| 项 | 值 | 说明 |
|---|---|---|
| 回归通过率 | 100% | 八次连跑一致,0 失败 0 超时 |
| N/A 断言 | 36(3.7%) | 静态组件无交互面、单实例组件无多变体,已逐条核实 |
| 契约保真度 | 93.9% 逐字命中规格 | 余 22 条为同义改写,非发明 |
| 令牌语义错配 | 0 / 1313 处 | 属性与令牌角色全部匹配 |
| i18n 质量 | 0 残留中文、0 未翻译 | 326 条字典 |
| 组件可换肤 | 79/79 | 逐组件改令牌比对样式签名 |
1.3 性能基线
| 指标 | 值 | 说明 |
|---|---|---|
| 文档站传输量 | 1247 KB | 7 个资源 |
其中 data.js |
1058 KB(85%) | ← 最大优化目标 |
app.js |
101 KB | 未压缩 |
style.css |
40 KB | 未压缩 |
| DOMContentLoaded | 345 ms | 本地 dev-server,线上更慢 |
| 每详情页额外 | iframe 1 个 | 加载对应演示页 |
1.4 实测缺口(按严重度)
| # | 缺口 | 实测证据 | 影响 |
|---|---|---|---|
| G1 | 无 LICENSE | ls LICENSE → 不存在 |
法律上不可用,对外交付的硬阻塞 |
| G2 | 无分发机制 | 无 package.json / 无 CDN / 无发布流程 |
用户拿不到,只能 clone 仓库 |
| G3 | CI 无测试 | .github/workflows/ 只有 deploy-pages.yml |
回归靠手动,质量无持续保障 |
| G4 | 暗色模式是假的 | 令牌层 aa-dark 规则 0 条、组件层 0 条、仅站点骨架 7 条 |
CHANGELOG 曾称"补全",实为第 6 次声称≠实现 |
| G5 | 跨端一致性无验证 | 5 端 395 文件,无任何自动比对 | H5/React/Vue2/Vue3 视觉是否一致无从知晓 |
| G6 | data.js 1MB |
占传输量 85% | 首屏慢,移动端更差 |
| G7 | RTL 未支持 | 逻辑属性 0 处 / 物理属性 22 文件 | 阿拉伯语等市场不可用 |
| G8 | 断言偏结构 | 12 项/页,多为"元素存在",无"点击后发生什么" | 行为回归无覆盖 |
二 · 长期愿景与阶段划分
2.1 目标状态(12 个月)
一句话:从「一个做得很完整的组件库仓库」,变成「一个能被外部团队直接采用的设计系统产品」。
拆成四个可验证的终态:
- 可交付 —— 有许可证、有分发渠道、有 CI 保障,外部团队 5 分钟内能用上。
- 可信 —— 关键声称(暗色、跨端一致、无障碍)全部有自动化证据,不靠文档承诺。
- 完整 —— 覆盖国际化(含 RTL)、无障碍实测、性能预算。
- 生态 —— 设计工具链打通(Figma)、典型页面可直接复用、有版本发布节奏。
2.2 阶段划分与决策依据
| 阶段 | 主题 | 解决 | 决策依据 |
|---|---|---|---|
| S1 | 可交付 | G1 G2 G3 G6 | 前三个是对外交付的硬阻塞:没许可证不能商用,没分发拿不到,没 CI 质量会退化。G6 顺手做(data.js 占 85% 传输量,收益最大) |
| S2 | 可信 | G4 G5 G8 | 都是「声称了但没证据」。G4 是第 6 次声称≠实现,必须先止血 |
| S3 | 完整 | G7 + 无障碍实测 | RTL 与无障碍是竞品 7 家里的分水岭(3 家有 RTL) |
| S4 | 生态 | Figma/模板/发布 | 前两阶段完成后,生态建设才有意义 |
为什么这个顺序:S1 是"能不能用",S2 是"能不能信",S3 是"够不够全",S4 是"好不好用"。顺序颠倒会做出没人敢用的产品。
三 · 任务包
格式:目标 → 依据 → 依赖 → 路径 → 步骤 → 验收 → 失败判据 每个任务包独立可执行,除标注依赖外互不阻塞。
S1-P1 · LICENSE 许可证 | 预算 ~10 min | 优先级 P0
目标:仓库根目录存在 LICENSE,明确授权范围,移除对外交付的法律阻塞。
依据:G1。CONTRIBUTING.md 与 README.md 都在讲如何贡献与使用,但没有许可证意味着默认保留所有权利,外部团队法务不会放行。
依赖:无。需用户确认许可证类型(见下方决策点)。
路径:LICENSE(新建)
步骤:
- 确认许可证类型。若用户未指定,默认 MIT(与本项目"零依赖、鼓励复用"的定位一致),并在交付说明里标注"如需变更请告知"。
- 写入标准 MIT 全文,版权行为
Copyright (c) 2026 Aurora Admin。 - 在
README.md增加## 许可证段,链接到LICENSE。 - 在
CHANGELOG.md的[Unreleased]下追加### Added — LICENSE。
验收:
# 文件存在且包含关键条款
test -f LICENSE && grep -q "MIT License" LICENSE && grep -q "WITHOUT WARRANTY" LICENSE && echo "LICENSE OK"
# README 有链接
grep -q "LICENSE" README.md && echo "README OK"
预期输出:
LICENSE OK
README OK
失败判据:任一条命令无输出,或 LICENSE 里出现非标准条款(自行增删免责声明)。
决策点(需用户输入):许可证类型。MIT 是默认推荐;若项目未来要商业化或要求衍生作品开源,应改 Apache-2.0 或 MPL-2.0。
✅ 完成(MIT,采用默认选择) — 验收输出:
LICENSE OK
README OK
同步改动:LICENSE(MIT 全文)、README.md(新增「许可证」段 + 版本号更新为 v1.4.1 + 指向本路线图)。
如需改为 Apache-2.0 / MPL-2.0:替换 LICENSE 全文,同步 README.md 与未来 package.json 的 license 字段即可,无其他联动。
S1-P2 · npm 分发 | 预算 ~2 h | 优先级 P0
目标:npm install aurora-admin-design 能拿到令牌 + 组件样式;<link> 可直接引 CDN。
依据:G2。当前用户只能 clone 整个仓库(5.1MB git + 2.6MB site),对"只想用几个组件"的人成本过高。
前置验证已完成(规划阶段实测):
| 假设 | 结果 |
|---|---|
| npm 可用 | ✅ v11.17.0 |
包名 aurora-admin-design 是否被占用 |
✅ 未被占用(registry 返回 {"error":"Not found"}) |
colors_and_type.css 可否直接作 dist 令牌源 |
✅ 含 :root、75 个令牌,可直接用 |
| 零依赖约束下能否构建 dist | ✅ Node 内置能力足够(读写文件 + JSON) |
依赖:S1-P1(package.json 需声明 license 字段)。
路径:
package.json(新建).npmignore(新建)tools/build-dist.mjs(新建,产出分发产物)dist/(构建产物,加入.gitignore)
步骤:
- 建
package.json,关键字段:注意:不声明任何{ "name": "aurora-admin-design", "version": "1.4.1", "description": "B 端中后台设计系统 · 79 组件 × 5 端 · 零运行时依赖", "license": "MIT", "main": "dist/tokens/tokens.css", "files": ["dist/", "README.md", "LICENSE"], "scripts": { "build": "node tools/build-dist.mjs", "test": "node tools/verify-dist.mjs" }, "keywords": ["design-system", "admin", "css-variables", "design-tokens"], "sideEffects": ["*.css"] }dependencies—— 这是硬约束。 - 写
tools/build-dist.mjs(Node ESM,零依赖),产出:dist/tokens/tokens.css、tokens.json(复用build-site.ps1的产出)dist/components/<slug>.css× 79 +dist/components/index.css(聚合)dist/components/<slug>.html× 79(静态演示)dist/README.md(用法速查,从主 README 节选)dist/manifest.json(组件清单 + 版本 + 文件校验和)
- 写
.npmignore排除site/、tests/、frameworks/*.jsx|vue*、.github/、*.png。 - 在
README.md增加「安装」段,给出 npm / CDN / 直接下载三种方式。
验收:
# 1) 构建产物齐全
node tools/build-dist.mjs
test -f dist/tokens/tokens.css && echo "tokens OK"
test -f dist/components/index.css && echo "aggregate OK"
test $(ls dist/components/*.css | wc -l) -ge 80 && echo "79 components + index OK"
# 2) 无运行时依赖(硬约束)
node -e "const p=require('./package.json'); if(p.dependencies) { console.error('FAIL: 不允许 dependencies'); process.exit(1) } console.log('zero-dep OK')"
# 3) 分发内容不含开发资产
# 注意:不能用 `npm pack | grep -q site/ && echo FAIL || echo OK` —— npm 不可用时 grep 也失败,会误报 OK
if npm pack --dry-run >/tmp/pack.txt 2>&1; then
if grep -qE "site/|tests/" /tmp/pack.txt; then
echo "FAIL: 打包含开发资产"; grep -E "site/|tests/" /tmp/pack.txt | head -3; exit 1
fi
echo "pack contents OK"
else
echo "SKIP: npm 不可用(网络/未安装),此项需在有 npm 的环境补验"
fi
# 4) 产物可用(起本地服务,浏览器打开验证)
node -e "const fs=require('fs');const c=fs.readFileSync('dist/components/index.css','utf8');if(!c.includes('--au-color-brand'))process.exit(1);console.log('css usable OK')"
预期输出:
tokens OK
aggregate OK
79 components + index OK
zero-dep OK
pack contents OK
css usable OK
失败判据:
package.json出现任何dependenciesnpm pack --dry-run输出含site/或tests/dist/components/*.css少于 80 个
已知风险:npm pack 需要网络(首次可能要求登录)。若无网络,跳过第 3 条验收并在交付说明注明。
S1-P3 · CI 回归流水线 | 预算 ~1.5 h | 优先级 P0
目标:每次 push/PR 自动跑回归,失败则阻止合并;结果可在 GitHub Actions 页面看到。
依据:G3。当前只有部署 workflow,回归靠人工手动跑 _collect.html,质量随提交退化不可见。
依赖:无(可与 P1/P2 并行)。
路径:
.github/workflows/regression.yml(新建)tools/run-regression.mjs(已存在,需改造为无网可用)
步骤:
- 现状:
tools/run-regression.mjs依赖playwright(npm 包),与"零依赖"约束冲突。 解法:CI 里允许用npx playwright(CI 环境的临时工具,不进package.json的 dependencies),或用devDependencies+ 在文档说明"CI 专用,运行时不依赖"。 推荐:把 playwright 放进devDependencies(这是构建期工具,不违反"零运行时依赖")。 - 写 workflow:
name: Regression on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: '20' } - run: npm i -D playwright && npx playwright install --with-deps chromium - run: node site/dev-server.js & - run: sleep 3 - run: node tools/run-regression.mjs - uses: actions/upload-artifact@v4 if: always() with: { name: regression-report, path: tests/report*.json } run-regression.mjs需改造:目前是纯 Node 脚本,要确保它在 Linux 下也能找到报告输出路径(Windows 路径分隔符问题)。- 增加失败阈值:
passRate < 100时process.exit(1)。
验收:
# 本地模拟 CI 步骤
node site/dev-server.js & sleep 3
node tools/run-regression.mjs
echo "exit=$?"
# 期望 exit=0 且 passRate=100
推送后在 GitHub Actions 页面确认 workflow 跑绿(需用户操作)。
预期输出:
passRate 100% | pages 79 (all-pass 79) | assertions 925/925
written: tests/report.json, tests/report-junit.xml
exit=0
失败判据:
- workflow 文件语法错误(用
actionlint或 GitHub 页面报错验证) - 本地模拟跑出
exit != 0 passRate < 100时没有exit 1
已知风险:playwright install 在 CI 上约需 1-2 分钟,可通过缓存加速(后续优化项)。
S1-P4 · data.js 瘦身 | 预算 ~3 h | 优先级 P0
目标:site/data.js 从 991 KB 降到 < 150 KB(实测可达成 ~110 KB)。
依据:G6。实测 data.js = 1058 KB(传输量),占文档站总传输 85%。
已完成的诊断(无需重做):
data.json 体积构成(实测)
总计: 991KB
components 974KB 98.3% ← 唯一大头
changelog 12KB 1.2%
tokens 5KB 0.5%
components[] 内部(单组件)
.sources 15.7KB ← 5 端源码全文
.contract 0.8KB
.specLines 0.2KB
.files 0.2KB
其余 <0.1KB
全部组件 .sources 合计: 888KB(占 data.json 的 89.6%)
结论:sources 字段(5 端源码全文,仅供详情页代码区展示)占了 89.6%。把它移出 data.js 改为按需 fetch,data.js 降到 ~110 KB(−89%)。
依赖:无。
路径:
build-site.ps1(改造产出:分文件写源码)site/app.js(改造消费:按需 fetch + loading 态)site/sources/<slug>/<kind>.txt(新增产出目录,79 × 5 = 395 个文件)site/data.js/site/data.json(产出物,不手改)
步骤:
- 改造
build-site.ps1:- 新增:把每个组件的 5 端源码写到
site/sources/<slug>/<kind>.txt(kind ∈ html/css/jsx/vue2/vue3) - 修改:
data.js里components[].sources改为sourcesRef: "sources/<slug>/"(只留路径,不留内容) - 保持
data.json完整(For Agents 页承诺"一次请求拿到全部",不能破坏 —— 这是硬约束)
- 新增:把每个组件的 5 端源码写到
- 改造
app.js(见下方「已完成的依赖分析」) - 验证 For Agents 承诺未破:
data.json仍然包含完整sources。
已完成的依赖分析(执行前必读)
app.js 里有 10 处消费 c.sources,分三类,改造难度不同:
类别 A · 渲染期同步解析(难点,2 处)
| 位置 | 函数 | 用途 |
|---|---|---|
app.js:969-971 |
extractComponentAPI(c) |
从 vue3/vue2/jsx 源码正则提取 Props/Events/Slots |
app.js:1544 |
extractScenarios(c) |
从 html 源码提取 <h2> 场景标题 |
这两个函数在渲染详情页时同步调用,依赖源码字符串立即可用。改成 fetch 后必须处理异步。
推荐解法:把「源码解析」的产物预计算进 data.js,而不是运行时 fetch 后再解析。
build-site.ps1已经能读源码 → 顺手把extractComponentAPI/extractScenarios的结果写进data.jsapp.js改为直接读预计算结果,这两个函数不再需要源码- 源码 fetch 只服务「代码展示」这一处需求
体积成本已实测(规划阶段验证过):
API 表(79 组件) : 0.2 KB
场景标题(79 组件) : 0.9 KB
合计新增 : 1.0 KB
sources 移除后节省 : 888 KB
净减少 : 887 KB
预计算产物只占 1 KB(原估 < 30KB,实测乐观 30 倍),代价可忽略。
这样做的好处:① 消除两处异步改造;② 解析逻辑从浏览器移到构建期(更快);③ 详情页首屏不再等源码下载。
类别 B · 代码展示(3 处,易改)
| 位置 | 用途 |
|---|---|
app.js:1635 |
Playground textarea 初值 |
app.js:1667 |
Playground 复位 |
app.js:1714/1722 |
代码区高亮渲染 |
改法:进详情页时 Promise.all 拉当前组件 5 个源码文件(或按需拉 tab 切换的那一个),拿到后填充。加 loading 占位。
类别 C · 判断可用性(2 处,用元数据替代)
| 位置 | 用途 |
|---|---|
app.js:1703-1704 |
判断哪些端有源码(决定 tab 显示) |
app.js:1720 |
复制按钮取当前 tab 源码 |
改法:data.js 保留一个轻量字段 sourcesAvailable: ["html","css","jsx","vue2","vue3"](79 × 5 个字符串,< 5KB),替代 sources[k] != null 的判断。复制按钮改为从已缓存的 fetch 结果取。
分阶段执行建议
- 阶段 1:
build-site.ps1预计算 API/场景 → 写入data.js;app.js改读预计算结果。此时sources仍完整(先不瘦身),跑回归确认无回退。 - 阶段 2:
build-site.ps1分离源码到文件 +data.js只留sourcesRef与sourcesAvailable;app.js改 fetch。 - 阶段 3:跑验收 + 浏览器实测(含断网兜底)。
验收:
# 1) 体积达标(实测应约 110KB)
node -e "
const fs=require('fs');
const core=fs.statSync('site/data.js').size;
console.log('data.js:', (core/1024).toFixed(0)+'KB');
if(core/1024 > 150) { console.error('FAIL: 超过 150KB 预算'); process.exit(1) }
console.log('budget OK');
"
# 2) 源码文件已分离(395 个)
# 注意:目录未创建时 ls 会报错到 stderr,这里显式兜底,让输出可读
n=$(ls site/sources/*/*.txt 2>/dev/null | wc -l)
echo "sources files: $n (期望 395)"
if [ "$n" -ne 395 ]; then
echo "FAIL: 期望 395 个源码文件,实际 $n"
echo " 提示:若目录不存在,说明 build-site.ps1 的分离逻辑未执行"
exit 1
fi
echo "split OK"
# 3) data.json 未缩水(For Agents 承诺不变 —— 硬约束)
node -e "
const d=require('./site/data.json');
const c=d.components.find(x=>x.slug==='button');
if(!c.sources||Object.keys(c.sources).length!==5) { console.error('FAIL: data.json 的 sources 被破坏'); process.exit(1) }
console.log('data.json intact OK');
"
# 4) data.js 里已无源码正文
node -e "
const d=require('./site/data.js')||{};
" 2>/dev/null || node -e "
const fs=require('fs');
const s=fs.readFileSync('site/data.js','utf8');
// 源码正文特征:不应再出现完整 HTML 文档
if(s.includes('<!DOCTYPE html>')) { console.error('FAIL: data.js 仍含源码正文'); process.exit(1) }
console.log('sources removed OK');
"
浏览器实测:
- 打开
#/component/button,代码演示区正常显示 - Network 面板出现
sources/button/html.txt请求 - 断网/404 情况下显示兜底提示而非空白
预期输出:
data.js: ~110KB
budget OK
sources files: 395
split OK
data.json intact OK
sources removed OK
失败判据:
data.js> 150 KBsite/sources/文件数 ≠ 395data.json的sources字段被动过(会破坏 For Agents 承诺)- 详情页代码区空白或报错
为什么不直接压缩:gzip 能压到 ~200KB,但解析时间与内存占用仍在(1MB JSON 在低端机解析约 100-200ms)。且 Pages 默认已开 gzip,压缩不是增量收益。根治要减体积。
S1-P4b · 首屏资源优化 | 预算 ~2 h | 优先级 P1
目标:文档站首屏传输从 1247 KB 降到 < 300 KB。
依据:P4 完成后 data.js ~110KB,但 app.js 101KB + style.css 40KB 仍是未压缩文本。
依赖:S1-P4。
步骤:
app.js(101KB 单文件)按路由拆分:首页逻辑与详情页逻辑分离,详情页按需加载。- 关键 CSS 内联(首屏可见部分),其余异步加载。
- 加入
build-site.ps1的产出:.min.js/.min.css(用 Node 内置能力做简单压缩:去注释、去多余空白)。
验收:
node -e "
const fs=require('fs');
const files=['site/data.js','site/app.js','site/style.css','site/i18n.js','.design_library/aurora-admin/colors_and_type.css'];
const total=files.reduce((s,f)=>s+fs.statSync(f).size,0);
console.log('首屏资源合计:', (total/1024).toFixed(0)+'KB');
if(total/1024>300){console.error('FAIL: 超过 300KB');process.exit(1)}
console.log('payload OK');
"
S2-P5 · 暗色模式真正实现 | 预算 ~1 天 | 优先级 P0
目标:html.aa-dark 下,79 个组件的演示页全部正常反色,对比度仍满足 WCAG AA。
依据:G4。实测令牌层 aa-dark 规则 0 条、组件层 0 条,只有站点骨架 7 条。CHANGELOG.md 的 v1.1.1 条目称"暗色模式令牌映射补全"—— 这是第 6 次声称≠实现。
依赖:S1-P4(体积优化后改 app.js 更清爽,非硬依赖)。
路径:
.design_library/aurora-admin/colors_and_type.css(加暗色令牌组)site/style.css(清理原有的 7 条临时规则)frameworks/*.html的内嵌样式(若需微调)tests/_runtime.js(增加暗色断言)
步骤:
- 设计暗色令牌组(在
colors_and_type.css里加html.aa-dark块):- 背景层:
page-bg#14161C→card-bg#1C1F26→table-header-bg#22262E(三层递进) - 文字层:
text-title#E8EAED→text-body#C9CDD4→text-secondary#9CA3AF→text-placeholder#6B7280 - 边框:
border#2A2F38、border-strong#363B45 - 品牌色:向白提亮(
#2F54EB在深底上对比度不足),推荐#5B7CF5系列 - 语义色:同样提亮(
#2E7D0A→#4CAF50一类)
- 背景层:
- 每个色值都要算对比度,写脚本验证(复用 v1.3.1 的方法):
node -e " // 对每个暗色令牌算 WCAG 对比度,全部 ≥4.5:1(文字)或 ≥3:1(图标) " - 演示页反色策略:v1.4.0 的注释说"演示 iframe 保持浅色,避免样例反色失真"。这条策略要么:
- A:改成可配置 —— 站点暗色时演示页也反色(更真实,但要确保反色后不难看)
- B:保持浅色,但在设计规范页显式说明这是有意设计
推荐 A,因为用户开暗色就是想整体变暗,局部刺眼是体验倒退。若选 A,需给演示页传
?theme=dark或用postMessage通知。
- 加断言:
_runtime.js增加matrix:dark-contrast,在暗色令牌下重跑对比度检查。
验收:
# 1) 令牌组存在且完整
grep -q "html.aa-dark" .design_library/aurora-admin/colors_and_type.css && echo "dark tokens OK"
node -e "
const css=require('fs').readFileSync('.design_library/aurora-admin/colors_and_type.css','utf8');
const m=css.match(/html\.aa-dark\s*\{([^}]+)\}/s);
if(!m){console.error('FAIL: 无暗色令牌组');process.exit(1)}
const n=(m[1].match(/--au-/g)||[]).length;
console.log('暗色令牌数:', n);
if(n<15){console.error('FAIL: 令牌数不足(预期 ≥15)');process.exit(1)}
"
# 2) 浏览器实测(关键验收)
# 打开 http://127.0.0.1:3311/site/index.html,切暗色模式,
# 逐个打开至少 10 个组件详情页,确认无「白底黑字残留」
浏览器实测项:
- 首页 / 总览 / 设计规范 / 组件详情 全部正常反色
- 任意 10 个组件演示页反色后对比度可读
- 截图对比(交付说明附 3 张暗色截图)
预期输出:
dark tokens OK
暗色令牌数: 20+(预期 ≥15)
失败判据:
- 暗色令牌数 < 15
- 任一页面出现"白底 + 白字"或"黑底 + 黑字"
- 对比度脚本报出 < 4.5:1 的文字
已知风险:79 个演示页的内嵌样式是硬编码过令牌化的(v1.4.0 完成),所以理论上改令牌就能全局生效 —— 这是 v1.4.0 打下的地基,本次直接受益。
S2-P6 · 跨端一致性自动验证 | 预算 ~1 天 | 优先级 P0
目标:能自动回答「H5 / React / Vue2 / Vue3 四个实现是否视觉一致」,并给出差异截图。
依据:G5。5 端 395 个文件,但从来没人验证过它们是否真的一致。契约里写的"各端视觉一致"是纯声称。
依赖:S1-P3(需要 CI 环境跑 Playwright)。
路径:
tools/verify-cross-platform.mjs(新建)tools/render-harness/(新建,把 React/Vue 组件渲染成静态 HTML)tests/cross-platform-report.json(产出)
步骤:
- 难点:React/Vue 组件需要构建才能渲染(.jsx/.vue 不能直接在浏览器跑)。
零依赖解法:
- 方案 A:用 CDN 版 React/Vue(
<script src="unpkg.com/react">)+@babel/standalone在浏览器里编译 JSX。缺点:依赖 CDN 可用性。 - 方案 B:更推荐 —— 只做结构性比对而非像素比对。提取各端的
className集合与 DOM 结构,比对差异。不要求渲染,纯静态分析。 - 方案 C:混合 —— 结构比对为主,挑选 6 个核心组件做像素比对(核心组件的 CDN 依赖可接受)。
- 方案 A:用 CDN 版 React/Vue(
- 推荐方案 B + 抽样 C:
- 全部 79 组件:静态提取 class 集合与结构骨架,diff 各端
- 6 个核心组件(button/input/select/table/card/modal):CDN 渲染 + 截图比对
- 输出报告:
{ slug, platforms: {h5:[...classes], react:[...], vue2:[...], vue3:[...]}, diffs:[...], severity }
验收:
node tools/verify-cross-platform.mjs
node -e "
const r=require('./tests/cross-platform-report.json');
console.log('检查组件:', r.total);
console.log('完全一致:', r.identical);
console.log('有差异 :', r.differing);
console.log('差异样本:', JSON.stringify(r.samples.slice(0,3),null,1));
"
预期输出:一份报告,明确列出哪些组件的哪些端存在 class/结构差异。
失败判据:
- 脚本报错或无输出
- 报告里
total < 79 - 差异项没有具体说明(只说"不一致"而不给差异内容)
重要说明:这个任务的产出可能揭露一批真实的不一致。这不是失败 —— 是这项任务的价值。发现的差异应作为新任务包列出,不在本任务内顺手修(避免范围蔓延)。
✅ 完成(方案 B:静态结构比对,零依赖零联网) — 验收输出:
检查组件: 79
完全一致: 2
有差异 : 77 (high 44 / medium 22 / low 11)
written: tests/cross-platform-report.json
改动文件:tools/verify-cross-platform.mjs(新建,class 集合 + 结构骨架双 diff,演示包装类/变量名/模板残留降噪,high/medium/low 分级)→ tests/cross-platform-report.json(产出,79 条全量 + samples 3)。
回归:tests/report.json 79/79 未动,site/data.json 79 组件未动(只读消费)。
发现的新问题 → 已追加为 S2-P6-F1~F4(见下方),不在本任务内修。
S2-P6-F1 · React 端缺失 is-disabled 等状态类(5 组件) | 预算 ~2 h | 优先级 P1
目标:is-disabled(5x)、is-active(3x)在 React 端补齐,与三端共识对齐。
依据:tests/cross-platform-report.json 的 missing-consensus 聚合。React 端多用 disabled 属性而非状态类,需确认是「有意用属性替代」还是「遗漏」——若有意,需在契约注明;若遗漏,补类。
验收:重跑 node tools/verify-cross-platform.mjs,is-disabled/is-active miss:react 条数归零或有契约说明。
S2-P6-F2 · H5 演示页缺失框架端结构类 | 预算 ~2 h | 优先级 P2
目标:col-selection/right/col-fixed/action(table 系)、ico/horizontal 等「三框架端有、H5 演示页缺」的类,核对 H5 演示是否漏了对应变体展示。
依据:同上报告。H5 是静态演示页,可能只是没展示该变体(非 bug);但若是变体缺失展示,补演示片段即可。
验收:逐项标注「演示缺展示(补)」或「框架端多余(不动)」,miss:h5 的 high 条数下降或全部有结论。
S2-P6-F3 · React 端 Tag/Select/Input 变体类缺失 | 预算 ~2 h | 优先级 P1
目标:aa-tag-dot/aa-tag-custom、au-input-lg/sm、aa-select-empty 等 React 端缺失的变体类,核对是未实现该变体还是类名拼写差异。
依据:同上报告(input 的 au-input-lg/sm 在本轮提取器修复后已证明是「追踪不到」而非「真缺失」的前车之鉴——先复核再修)。
验收:逐项复核,有真缺失则补实现;误报则修提取器。
S2-P6-F4 · P6 报告纳入 CI 门禁 | 预算 ~30 min | 优先级 P2
目标:regression.yml 追加一步 node tools/verify-cross-platform.mjs,total < 79 或脚本非零退出时标红(high 差异只告警不阻塞,避免把历史差异变成合并 blocker)。
依赖:S1-P3(CI 已存在)。
验收:workflow 文件含该步骤且语法正确。
S2-P7 · 行为断言 | 预算 ~1.5 天 | 优先级 P1
目标:测试从"元素存在"升级到"交互后发生什么",覆盖点击/输入/键盘的真实行为。
依据:G8。当前 12 项/页断言多为结构性(元素存在、有 aria、对比度达标),没有任何一条验证"点了按钮会怎样"。
依赖:无。
路径:
tests/_runtime.js(扩展断言引擎)tests/_behaviors.js(新建,行为断言库)
步骤:
- 定义行为断言协议(在演示页里声明):
<button data-behavior="click-toggles-class:.aa-modal|is-open">打开弹窗</button> - 实现常见行为模式:
click-toggles-class:<selector>|<class>— 点击切换类click-sets-attr:<selector>|<attr>|<value>— 点击设属性input-updates:<selector>|<text>— 输入后状态变化keyboard-activates:<key>— 键盘触发focus-trap:<container>— 弹窗焦点锁定
- 在 6 个核心组件(button/modal/select/input/table/tabs)的演示页里标注行为断言作为试点。
- 断言结果并入现有报告结构(
matrix:behavior:*)。
验收:
# 1) 行为断言库存在且语法正确
node --check tests/_behaviors.js && echo "syntax OK"
# 2) 6 个试点组件有行为断言
grep -l "data-behavior" frameworks/{Button,Modal,Select,Input,Table,Tabs}.html | wc -l
# 期望输出 6
# 3) 回归里出现行为断言
# 跑 _collect.html,检查 report.json 的 failureKinds 含 matrix:behavior:*
浏览器实测:点击弹窗按钮后,is-open 类被添加(观察 DOM)。
✅ 完成(6 试点全绿) — 验收输出:
button/modal/select/input/table/tabs 行为断言各 1 条,全部 PASS
全量回归:passRate 100% | pages 79 (all-pass 79) | assertions 1009/1009 | N/A 35
改动文件:tests/_behaviors.js(新建,8 动词 + 6 试点白名单 + 同步 pump + page-load 幂等缓存)、tests/_runtime.js(聚合行为断言 + 重载清缓存)、tests/_template.html(引入行为库)、6 演示页 data-behavior 标注、79 测试页重生成。
关键修复:回归 runner 的兜底重跑导致 run() 执行两次,行为断言第二轮误报——加 page-load 缓存解决(重载演示页后重测)。
规划偏差:试点 6 组件的 data-behavior 标注里,button 是纯静态页,为测而加了一个最小加载态切换交互(不破坏视觉)。
预期输出:
syntax OK
6
失败判据:
- 试点组件少于 6 个
- 行为断言在回归报告里不出现
- 断言只是"元素存在"换个名字(无实际交互逻辑)
S3-P8 · RTL 支持 | 预算 ~2 天 | 优先级 P1
目标:<html dir="rtl"> 时布局正确镜像,无错位。
依据:G7。逻辑属性 0 处,物理属性(margin-left 等)出现在 22 个文件。竞品 7 家里 3 家有 RTL。
依赖:S2-P5(暗色完成后改样式更安全,减少冲突)。
路径:
frameworks/*.css(22 个含物理属性的文件)frameworks/*.html内嵌样式.design_library/aurora-admin/colors_and_type.css(如需要在文档说明约定)
步骤:
- 批量迁移物理→逻辑属性:
物理 逻辑 margin-leftmargin-inline-startmargin-rightmargin-inline-endpadding-left/rightpadding-inline-start/endleft/right(定位)inset-inline-start/endtext-align: left/righttext-align: start/endborder-left/rightborder-inline-start/end - 注意例外:图标方向类(箭头、返回)需要
transform: scaleX(-1)而非属性迁移。 - 写一个 RTL 演示页:
site/scenario/user-management-rtl.html,用于人工验收。 - 增加 RTL 断言:
matrix:rtl-safe,检测是否还有物理属性的残留。
验收:
# 1) 物理属性清零
n=$(grep -l "margin-left\|margin-right\|padding-left\|padding-right" frameworks/*.css 2>/dev/null | wc -l)
echo "剩余物理属性文件: $n"
test "$n" -eq 0 && echo "RTL migration OK"
# 2) 逻辑属性已使用
grep -l "margin-inline\|padding-inline\|inset-inline" frameworks/*.css | wc -l
# 期望 ≥ 22
浏览器实测:user-management-rtl.html 加 dir="rtl",确认布局镜像无错位(附截图)。
预期输出:
剩余物理属性文件: 0
RTL migration OK
22+
失败判据:
- 仍有物理属性残留
- RTL 下出现元素重叠/溢出
- 图标方向错误(如右箭头在 RTL 下仍指右)
✅ 完成(28 文件迁移 + 14 处人工例外) — 验收输出:
扫描 CSS: 79 个 / 可迁移: 28 个(已写入)
剩余物理属性文件: 0(口径:排除 margin-left:auto 与拼接边框例外,见下)
逻辑属性文件: 19
RTL 实测:button/table/SideMenu 三页 LTR/RTL 均零溢出,0 JS 错误
全量回归:passRate 100% | pages 79 (all-pass 79) | assertions 1009/1009(与 P7 同轮验证)
改动文件:tools/migrate-rtl.mjs(新建,白名单 dry-run/--write 双模式)、28 个 frameworks/*.css(margin/padding/border-inline + text-align start/end)。
人工例外 14 处(脚本跳过并报告):margin-left:auto flex 推送(3 处)、按钮组拼接边框(5 处)、left:0+right:0 并存居中(4 处)——语义不等价,不机械替换。
规划偏差:验收命令的 grep -l "margin-left..." 会把上述例外计入,实际口径为“排除例外后清零”;user-management-rtl.html 演示页未建(场景页模板属 P11 范畴),以三代表页实测代替。
S3-P9 · FAQ 页 | 预算 ~1 天 | 优先级 P2
目标:新增 #/faq 页,自动聚合 139 条 unknowns + 101 条 doNotInvent,形成可检索的问答。
依据:契约里已有 240 条结构化条目,现成素材没被利用。Ant Design 每组件 FAQ 是行业惯例。
依赖:无。
路径:
site/app.js(新增renderFaq)site/index.html(导航加 FAQ 链接)site/i18n.js(文案)
步骤:
build-site.ps1已把 contract 注入data.js(字段unknowns/doNot),直接消费即可。- 渲染两种视图:
- 按组件:每个组件列出它的 unknowns("规范未明示")与 doNotInvent("不要自行发明")
- 按分类:把相似的 unknowns 聚类(如"最大宽度""省略方式"跨多个组件出现)
- 加搜索过滤(复用
_runtime的思路,纯前端)。
验收:
# 1) 路由可达
curl -s http://127.0.0.1:3311/site/index.html | grep -q 'href="#/faq"' && echo "nav OK"
# 2) 数据完整注入
node -e "
const d=require('./site/data.json');
const u=d.components.reduce((s,c)=>s+((c.contract&&c.contract.unknowns)||[]).length,0);
const dn=d.components.reduce((s,c)=>s+((c.contract&&c.contract.doNot)||[]).length,0);
console.log('unknowns:',u,'doNotInvent:',dn);
if(u<139||dn<101){console.error('FAIL: 契约数据缺失');process.exit(1)}
console.log('data OK');
"
浏览器实测:#/faq 显示 79 个组件的条目,搜索"宽度"能过滤出相关项。
预期输出:
nav OK
unknowns: 139 doNotInvent: 101
data OK
失败判据:条目数少于 139/101,或页面空白。
✅ 完成(details 懒加载版) — 验收输出:
title: 常见问题 / groups: 80 / items: 240
stat: 共 240 条,79 个组件有条目
search 宽度 -> items: 11 / groups: 11(stat: 命中 11 / 240 条,10 / 79 个组件)
restored: 240 / topnav faq: present / jserrors: none
FAQ BROWSER OK
改动文件:site/app.js(renderFaq + #/faq 路由 + 侧边栏入口)、site/index.html(顶栏入口)、site/style.css(guide-h3)、site/i18n.js(9 条英文)。
规划偏差:契约已在 P4 阶段 D 移出 data.js(site/data.js 含 unknowns 0 条),故 FAQ 改走 details/*.json 批量懒加载(每批 10 个)而非规划写的“直接消费 data.js”。data.json 的 139/101 计数仍作为数据源完整性断言。
S4-P10 · Figma 资源 | 预算 ~2 天 | 优先级 P2
目标:产出可导入 Figma 的变量集与组件描述,设计师能直接用同一套令牌。
依据:竞品标配 Figma/Sketch 资源。本项目已有 site/tokens/figma.json(Tokens Studio 格式),但没有 Figma Variables 原生格式。
依赖:S1-P2(需稳定的令牌导出管线)。
路径:
tools/export-figma.mjs(新建)dist/figma/(产出)
步骤:
- 产出 Figma Variables JSON(与 Tokens Studio 格式不同,是 Figma 原生 API 格式):
- 色彩变量 →
{ "color": { "brand": { "type": "COLOR", "value": {...} } } } - 数值变量(间距/圆角/字号)→
FLOAT - 建立 Light / Dark 两种 mode(呼应 S2-P5)
- 色彩变量 →
- 产出组件清单 Markdown(供设计师建组件时参考):每个组件的变体维度 + 尺寸 + 状态。
- 写导入说明(
dist/figma/README.md)。
验收:
node tools/export-figma.mjs
node -e "
const v=require('./dist/figma/variables.json');
const colors=Object.keys(v.color||{}).length;
console.log('色彩变量:', colors);
console.log('模式:', Object.keys(v.modes||{}).join(', '));
if(!v.modes||!v.modes.Dark){console.error('FAIL: 缺少 Dark 模式');process.exit(1)}
console.log('figma export OK');
"
预期输出:
色彩变量: 24+
模式: Light, Dark
figma export OK
失败判据:无 Dark 模式、变量数明显少于令牌数、JSON 不符合 Figma Variables 结构。
S4-P11 · 模板页库 | 预算 ~2 天 | 优先级 P2
目标:从 1 个场景页扩展到 5 个典型后台页面模板,展示组件组合用法。
依据:现有 site/scenario/user-management.html 是唯一的组合示范。模板页是"能否直接用"的关键 —— 用户要的不是组件,是页面。
依赖:无。
路径:site/scenario/*.html(新增 4 个)
步骤:
- 选定 4 个典型场景(覆盖不同组件组合):
login.html— 登录页(输入类组件 + 表单校验)dashboard.html— 数据看板(图表 + 指标卡 + 表格)order-list.html— 列表管理(表格 + 筛选 + 分页 + 批量操作)settings.html— 设置页(Tab + 表单 + 开关组)
- 每个模板页只用现有令牌与
components.css,纯 HTML + 原生 JS(零依赖)。 - 加到首页入口与 sitemap。
验收:每个模板页在浏览器实测可交互(筛选、分页、提交有反馈),附截图。
失败判据:页面白屏、交互无效、用了非项目内的样式。
S4-P12 · 版本发布流程 | 预算 ~1 天 | 优先级 P3
目标:有明确的 SemVer 发布流程与自动化。
依赖:S1-P2。
步骤:
tools/release.mjs:从 CHANGELOG 提取版本 → 更新package.json→ 打 tag → 提示推送。- 文档化到
CONTRIBUTING.md。
✅ 完成 — 验收输出:
package.json: 1.4.1 / CHANGELOG 最新: [1.4.1] 2026-09-11
OK: 版本一致
NOTE: [Unreleased] 段有 1251 字符未发布变更,发版前确认是否纳入本次
OK: build-dist 可调用
改动文件:tools/release.mjs(新建,检查模式 + --bump major|minor|patch --date 提升模式,只做本地准备不自动 push/tag)、CONTRIBUTING.md(发布流程 5 步 + 版本号规则)。
规划偏差:原步骤写“打 tag → 提示推送”由脚本做,实际拆为脚本只做本地准备、tag/push 由人按输出清单执行(不可逆的远端动作不自动化)。
S6-P21 · 组件族参数化(同源组件收敛为「1 基座 + N 参数」)| 预算 ~2 天 | 优先级 P1
目标:把"同一交互/结构各自手写一遍"的组件收敛为「1 个基座组件 + N 个参数取值」,消除重复身份,同时不改变组件身份(slug)、不减少 data.json 的组件数。
依赖:无(纯新增层,不动既有对外承诺)。
背景(实测):79 个组件里有 44 个能按语义归入 13 个族。判据不是名字相似,而是契约里可核对的四类字段:semanticTypeCandidates 重叠、anatomy 为同一骨架的子集、变体维度同构、以及 doNotInvent 里的显式从属声明 —— 例如 alertmodal 与 confirmmodal 的 doNotInvent 原文写着「弹窗尺寸档位(见 Modal 契约)」,steps 与 steplist 的 semanticTypeCandidates 完全相同(均为 steps|wizard)。
硬约束(决定了实现形态):
tools/pack-deploy.mjs:137-139硬断言site/components薄壳数 === 79、frameworks实现文件数 === 395;tools/precompute.mjs:38-39硬断言COMPONENT_COUNT = 79/SOURCE_FILE_COUNT = 395;tools/verify-cross-platform.mjs:695、tools/verify-package-import.mjs断言 79。
因此「新增族内核文件」与「删除变体文件」两条路都不可行。实现层合并只能走一份参数化模板 → 生成 N 组自包含产物(与 tests/_template.html → tests/<slug>.html × 79 同一套哲学)。
步骤:
tools/lib/family-model.mjs:13 族 / 44 成员的唯一真源;每族必写mergeBasis(实测依据)与paramSurface(参数名/类型/取值/默认值)。tools/gen-families.mjs:生成families.json;对 44 份契约做定点注入(只在"slug"行后插family/familyRole/familyParams,不重排 JSON、不覆盖他人未提交改动)。build-site.ps1+tools/precompute.mjs:把族层输出到data.json(顶层families+ 每组件family三元组)、data.js、site/details/<slug>.json。tools/lib/family-impl/nav-menu/*.tpl+tools/gen-family-impl.mjs:族实现生成器(带"不覆盖非生成物的脏文件"护栏)。tools/verify-families.mjs:端到端验证(模型 ↔ 契约 ↔ data.json ↔ details ↔ 395/79 硬约束)。
✅ 完成 — 验收输出(原样粘贴):
$ node tools/gen-families.mjs --dry-run
[dry-run] 族 13 / 登记成员 44 / 契约改动 44 / 已最新 0
独立组件(不属任何族):35 个 — button select tag breadcrumb tree dropdown popconfirm segmented transfer rate slider collapse …
OK: 族层数据一致
$ npm run build:site
families: 13 families, 44 member components
family layer: 13 families, 44/79 components carry family metadata
thin shells: 79, platform shells: 316, sitemap.xml: 396 urls
all components complete (5-form files + category)
[precompute] 完整性校验:79/79 个组件,395/395 个源文件
[precompute] 数据.js: 1147 KB → 122 KB
$ node tools/verify-families.mjs
族 13 个(其中已参数化实现 1 个)
族成员 44 个组件
独立组件 35 个
概念组件数 13(族)+ 35(独立)= 48 个,替代原本 79 个并列组件
契约注入 44/44
frameworks 395 文件(79 x 5,未增未删)
测试页 79 薄壳
· nav-menu 3 成员 → 1 基座(净减 2)
· table 6 成员 → 1 基座(净减 5)
· masked-input 7 成员 → 1 基座(净减 6)
· modal-shell 5 成员 → 1 基座(净减 4)
· preference-switcher 3 成员 → 1 基座(净减 2)
· card-shell 4 成员 → 1 基座(净减 3)
· feedback-page 3 成员 → 1 基座(净减 2)
· loading-state 3 成员 → 1 基座(净减 2)
· steps / tabs / notice / selection-card / combobox 各 2 成员 → 1 基座(各净减 1)
details 携带族字段:44/44
OK: 族层端到端一致(13 族 / 44 成员 / 79 组件不变 / 395 文件不变)
概念组件:79 → 48(合并掉 31 个重复身份)
$ node tools/gen-family-impl.mjs --only=nav-menu
[write] 族 nav-menu:写入 15 / 已最新 0 / 跳过 0
OK: 族实现与模板一致
$ md5sum frameworks/TopMenu.css frameworks/SideMenu.css frameworks/MixedNavigation.css
9f98c53aa3b2449ecc1fc42e34635129 *frameworks/TopMenu.css
9f98c53aa3b2449ecc1fc42e34635129 *frameworks/SideMenu.css
9f98c53aa3b2449ecc1fc42e34635129 *frameworks/MixedNavigation.css
# 三份逐字节相同 = 一份样式表服务三个组件(差异只在根元素的 data-direction 参数)
$ node tools/verify-cross-platform.mjs
检查组件: 79
完全一致: 79
有差异 : 0 (high 0 / medium 0 / low 0)
# 基线与口径(两个数不能混为一谈,否则会把自己的改动说大成整体改善):
# 已提交 HEAD(tests/cross-platform-report.json) identical 2 / differing 77 / high 44
# 本任务改动前的工作区 identical 79 / differing 0
# 本任务改造后 identical 79 / differing 0(持平)
# 改造中途曾出现 76/79:族模板最初把方向写成对象字面量 { direction: 'side' },Vue 端
# class 提取器会把其中的字符串值收作变体记号,H5/JSX 端不会。探针实验确认后用独立常量
# FAMILY_DIRECTION = 'side' 承载方向,四端恢复 79/79 —— 是改代码对齐既有约定,
# 不是放宽校验脚本。
$ node tools/run-regression.mjs
passRate 100% | pages 79 (all-pass 79) | assertions 1017/1017 | N/A 34
[OK] 全部通过
# 导航族逐页实测(playwright 打开 tests/<slug>.html,点「运行断言」后读帧内真实 DOM)
[topmenu] 总数 13 | 通过 13 | 失败 0 | 跳过 0
帧内: 3 个 .aa-menu / direction=[top,top,top] / 菜单项 30 / role+tabindex 齐备=true / JS 错误: 无
[sidemenu] 总数 13 | 通过 13 | 失败 0 | 跳过 0
帧内: 3 个 .aa-menu / direction=[side,side,side] / 菜单项 30 / role+tabindex 齐备=true / JS 错误: 无
[mixednavigation] 总数 13 | 通过 13 | 失败 0 | 跳过 0
帧内: 3 个 .aa-menu / direction=[mixed,mixed,mixed] / 菜单项 22 / role+tabindex 齐备=true / JS 错误: 无
# mixednavigation 菜单项少 8 个是因为 mixed 的侧栏只渲染当前一级的 children(22 = 一级 5 + 二级 3 + 案例 2/3 各 7)
$ node -e "...零依赖验收(AGENTS.md 铁律 1 原文脚本)"
zero-dep OK
改动文件:
.design_library/aurora-admin/families.json— 新建,族模型快照(25 202 bytes,build-site 消费).design_library/aurora-admin/components/*.json— 44 份各 +3 行(family/familyRole/familyParams),其余字节不变tools/lib/family-model.mjs— 新建,13 族真源(含 mergeBasis 与 paramSurface)tools/gen-families.mjs— 新建,契约层生成器(--dry-run/--check/ 幂等)tools/lib/family-impl/nav-menu/menu.{html,css,jsx,vue2,vue3}.tpl— 新建,导航族 5 端参数化模板tools/gen-family-impl.mjs— 新建,实现层生成器(脏文件护栏 +--check)tools/verify-families.mjs— 新建,族层端到端验证build-site.ps1— 读 families.json;data.json增顶层families与每组件族字段(families以原始 JSON 文本直插,避开 PS 5.1 的 PSCustomObject 序列化与空数组展开两个陷阱;仍为 ASCII-only)tools/precompute.mjs—site/details/<slug>.json增family/familyRole/familyParamsframeworks/{TopMenu,SideMenu,MixedNavigation}.{html,css,jsx,vue2.vue,vue3.vue}— 15 个文件改为族模板生成物(三份 CSS md5 相同:一份样式表服务三个组件)
规划偏差:
- 原设想「抽一个共享 Menu 内核,三个变体薄壳 import 它」→ 实际不可行:会同时打破 395 文件数、
build-dist打包与verify-package-import的 79 导出断言,并牺牲"单文件可拷贝"。改为模板生成:15 个产物保持自包含,重复消除在源头(改一份模板 + 重跑 = 三端同步)。 - 原计划把实现层一次覆盖多族 → 实际只做
nav-menu一族作为端到端切片,其余 12 族先落契约层。原因:实现层每族都要重写 5 端并过 9 项断言矩阵,一次做完无法逐族验证。 FAMILY_PARAMS从 JSON 字面量改为 JS 对象 + 只保留实现真正消费的参数(direction/collapsed):JSON 形态会让参数名/枚举值被跨端 class 提取器当成类记号。
发现的新问题(已登记,未在本任务顺手修):
- S6-P22(建议):
tools/verify-cross-platform.mjs的 class 提取器端间不对称 —— 同一份代码形态,Vue 端会把const X = { k: 'v' }的字符串值收作变体记号,H5/JSX 端不会。本次已用「方向独立常量」绕开(四端回到 79/79),但提取器本身未修;下一个族(如masked-input的mask/format一定是对象字面量里的字符串)还会再撞一次。建议统一三端口径:要么都在对象字面量里展开枚举,要么都不展开。 - S6-P23(建议):其余 12 族的实现层合并(
table净减 5、masked-input净减 6 收益最大)。族模板按现有生成器结构新增<family>/menu.*.tpl即可,数据层无需改动。 - S6-P24(建议):
MixedNavigation.css原文件同时写了border-inline-start与border-left-color(逻辑属性 + 物理属性混用,RTL 下两侧指示条表现不一致);族模板已统一为逻辑属性,但未做 RTL 实测。
回归:100%(1017/1017)/ 八次连跑一致 / 0 超时。
四 · 显式不做(及理由)
这份清单同样重要 —— 没有它,执行模型会以为"漏了",或者在错误的时机自作主张。
| 不做 | 理由 | 什么条件下应该做 |
|---|---|---|
| 不引入打包器(Vite / Rollup / esbuild) | 破除零依赖约束的代价远大于收益;本项目的卖点就是"打开 HTML 就能用" | 若未来要发布 ESM/CJS 模块化包,先评估是否需要独立仓库 |
| 不做像素级视觉回归(全量) | 79 组件 × 5 端 = 395 张基准图,维护成本极高且易误报(抗锯齿/字体渲染差异) | P6 已包含「6 个核心组件抽样像素比对」,够用 |
| 不做 SSR / SSG | 文档站是纯静态 SPA,爬虫需求已由 site/components/*.html 薄壳 + sitemap 解决 |
若 SEO 成为核心指标,再评估 |
| 不做多主题商店(Ant Design 式) | 已有 6 套预设 + 令牌导出,改色能力已足够;主题商店是运营功能非工程功能 | 产品化阶段(S4 之后) |
不重写 app.js |
2064 行单文件虽不理想,但功能正确、回归 100%。重构风险 > 收益 | 若某任务包因它受阻(如 P4 代码拆分),局部改造而非重写 |
不改 frameworks/ 的视觉 |
那是规范原文的忠实实现,改了会破坏契约一致性 | 只有规格原文变更时才同步 |
| 不做 Storybook 集成 | 与零依赖冲突,且 Playground(v1.2.0)已覆盖在线试玩需求 | 不考虑 |
| 不做 React/Vue 单元测试(Jest/Vitest) | 与零依赖冲突;跨端一致性靠 P6 的静态比对 + 抽样渲染 | 若组件逻辑复杂化到静态分析无法覆盖 |
五 · 未解问题(需用户或后续规划决策)
这些问题我作为规划师无法单方面决定,列出供决策。
| # | 问题 | 影响面 | 我的建议 |
|---|---|---|---|
| Q1 | 是否发布到 npm? 涉及包名占用、发布权、后续维护承诺 | S1-P2 | 建议发。当前无分发渠道是最大阻塞,且 npm 上同名包大概率未被占用 |
| Q2 | 目标用户是谁? 内部团队 / 开源社区 / 商业化产品,三者的优先级与验收标准不同 | 全局 | 建议先按「内部团队 + 开源复用」定位(对应 S1-S2 的优先级) |
| Q3 | 是否有设计团队协作? 若没有,S4-P10 的 Figma 产出无人使用 | S4-P10 | 建议推迟到有设计资源时再做,或降级为「Figma Variables JSON 导出」只做工程侧 |
| Q4 | 是否需要 RTL? 取决于是否有中东/希伯来语市场 | S3-P8 | 若无明确市场,降级为 P2 或推迟。迁移成本高(22 个文件) |
| Q5 | 性能预算的底线是多少? 我定的 300KB 是经验值 | S1-P4b | 建议以「3G 网络首屏可交互 < 3s」反推预算 |
| Q6 | 暗色模式下演示 iframe 是否反色? 两种都有道理 | S2-P5 | 建议反色(用户开暗色就是要整体变暗),但需人工验收视觉 |
六 · 风险登记
| 风险 | 影响 | 缓解 |
|---|---|---|
| 约束冲突:CI 需要 Playwright,但"零依赖" | 中 | 明确区分运行时依赖(保持零)与构建/测试依赖(可引入 devDependencies),并在 README 写清 |
| S2-P6 暴露大量跨端不一致 | 中 | 已在任务包里说明"发现即价值",差异作为新任务列出,不顺手修 |
| 暗色反色后视觉倒退 | 中 | 强制对比度脚本验证 + 人工截图验收 |
| RTL 迁移破坏现有布局 | 中 | 迁移后跑八次回归;逐文件比对渲染截图 |
data.js 拆分影响 For Agents 承诺 |
高 | 明确要求 data.json 保持完整,只拆 data.js |
| 用户未确认许可证类型 | 中 | 默认 MIT 并在交付说明标注"如需变更请告知" |
七 · 验收总纲
任一任务包完成,必须同时满足:
- 给出验收命令的完整输出(不是"我以为跑过了")
- 跑八次回归:
100% / 0 失败 / 0 超时 CHANGELOG.md有对应条目([Unreleased]下)- 本文件对应任务包下追加
✅ 完成于 <commit-hash> - 若发现新问题:写成新任务包追加到本文件,不在原任务里顺手修
附 · 执行状态总览
执行模型看这里:找下一个可开工的任务。状态为「待开始」且依赖已满足的最优先任务就是你的目标。
| ID | 任务 | 阶段 | 优先级 | 预算 | 依赖 | 状态 |
|---|---|---|---|---|---|---|
| P1 | LICENSE | S1 | P0 | 10 min | — | ✅ 已完成(MIT) |
| P2 | npm 分发 | S1 | P0 | 2 h | P1 ✅ | ✅ 已完成(dist 构建 + npm pack 115.9KB) |
| P3 | CI 回归 | S1 | P0 | 1.5 h | — | ✅ 已完成(workflow + runner 重构 + exit code) |
| P4 | data.js 瘦身 |
S1 | P0 | 3 h | — | ✅ 已完成(data.js 98KB/sources 395/data.json 完整/回归 1003/1003,见 c65a69c) |
| P5 | 暗色模式 | S2 | P0 | 1 d | P4 ✅ | ✅ 已完成(令牌组31/对比度10项≥4.5/10页实测/DARK VERIFY 32/32/首屏292KB/回归1003/1003,Q6选A) |
| P6 | 跨端一致性验证 | S2 | P0 | 1 d | P3 | ✅ 已完成(静态结构比对 79/79,high 44/medium 22/low 11,F1-F4 已追加) |
| P7 | 行为断言 | S2 | P1 | 1.5 d | — | ✅ 已完成(8 动词引擎 + 6 试点全绿/回归 1009/1009) |
| P8 | RTL | S3 | P1 | 2 d | P5 | ✅ 已完成(28 文件迁移/物理清零/14 处人工例外/三页实测零溢出) |
| P9 | FAQ 页 | S3 | P2 | 1 d | — | ✅ 已完成(240 条/79 组件/搜索过滤/浏览器实测,details 懒加载版) |
| P10 | Figma 资源 | S4 | P2 | 2 d | P2 | ⚪ 待 P2 |
| P11 | 模板页库 | S4 | P2 | 2 d | — | 🟢 可开工(优先级低) |
| P12 | 版本发布流程 | S4 | P3 | 1 d | P2 | ✅ 已完成(release.mjs 检查/bump + CONTRIBUTING 文档化,只做本地准备不自动 push/tag) |
推荐执行顺序
P3(CI,无依赖最快见效)
→ P4(瘦身,收益最大)
→ P2(分发,解除交付阻塞)
→ P5 + P6(可信度双支柱)
→ P7 → P8 → P9 → P10 → P11 → P12
为什么 P3 排第一:它是唯一能让后续所有改动自动受保护的任务 —— 有了 CI,其他任务的质量不再依赖人工记得跑回归。
为什么 P4 第二:实测净减 887 KB(991KB → ~110KB),收益/成本比最高,且依赖分析已完成。
并行机会:P2 与 P4 互不阻塞,可由两个模型同时做。P3 完成后应立即合并,让后续任务都在 CI 保护下。
四 · 2026-09-19 安全审计(衍生任务与已完成项)
起因:对仓库 +
192.168.5.7线上部署做全量安全与缺陷审计(报告与逐项证据见安全审计修复与待决事项.md)。 已修复项在 CHANGELOG[Unreleased] → Security有记录;下面只列衍生任务包与状态。
| ID | 任务 | 阶段 | 优先级 | 预算 | 状态 |
|---|---|---|---|---|---|
| S5-P13 | 部署可复现化 | S5 | P0 | 2 h | ✅ 已完成(tools/pack-deploy.mjs 以 .dockerignore 为唯一真源 + 硬断言;AGENTS §九 流程;2026-09-19 实际部署验证 6/6 断言 + 396/396 URL) |
| S5-P14 | 容器运行期加固 | S5 | P2 | 1 h | ⚪ 待决策(需停机窗口):read_only + tmpfs、cap_drop: [ALL]、no-new-privileges、基础镜像 digest 固定 |
| S5-P15 | 对外承诺口径统一 | S5 | P1 | 2 h | ⚪ 待决策:契约数量三处说法不一致(llms.txt「79 个」/AGENTS.md「79」/index.json「6 个核心」),线上实际只发布 6 份核心 + index.json |
| S5-P16 | 正式域名与 SEO | S5 | P2 | 1 h | ⚪ 待决策:sitemap.xml 与 package.json 仍是 YOUR-ACCOUNT.github.io 占位;build-site.ps1 已支持 SITE_URL_BASE,但 tools/verify-site-routing.mjs 目前把占位 URL 写成了断言,换域名需同步改 |
| S5-P17 | 回归偶发失败的可追溯性 | S5 | P1 | 1 h | ⚪ 待开工:2026-09-19 连跑 27 次中出现 2 次 1016/1017(78/79 页全通过),但 tests/report.json 的 failedPages/timedOut 为空、也未记录是哪条断言失败,导致无法定位。需让 runner 在非满分时落盘失败断言标识(页面 + data-assert + 期望/实际),并保留上一次报告不被覆盖 |
| S5-P18 | 组件库可 import(三端入口) | S5 | P0 | 3 h | ✅ 已完成(`dist/react |
已知但暂不处理(用户 2026-09-19 决定):不对外公用 —— 线上保持 127.0.0.1:3311 回环,需 SSH 隧道访问;不加反代、不加域名、不加认证。
S5-P15 补充说明
线上 /.design_library/aurora-admin/components/ 只有 button/card/input/modal/select/table.json + index.json;本地是 79 份 + index.json。三处文档说法互相矛盾,需先定「对外到底承诺几份契约」,再统一 llms.txt / AGENTS.md / index.json 与线上发布内容。
S5-P16 补充说明
sitemap.xml 的 396 条 URL 本身在线上全部 200(静态页无死链),问题只在文件本身的域名是占位值,且此前未 COPY 进镜像(已修)。换真域名时注意 tools/verify-site-routing.mjs:48 的断言。
五 · 2026-09-20 文档站导航(衍生任务与已完成项)
| ID | 任务 | 阶段 | 优先级 | 预算 | 状态 |
|---|---|---|---|---|---|
| S5-P19 | 导航语言选择改为下拉(美化) | S5 | P1 | 2 h | ✅ 已完成(原「中/EN 双段」按钮换成令牌化下拉:地球图标 + 当前语言 + 箭头触发器,186px 卡片菜单含标题分隔线、ZH/EN 角标、对勾选中态;listbox ARIA + 方向键/Home/End/Esc/Tab/点击外部全路径;暗色令牌继承,选中项对比度 4.57、标题 6.50;≤1100px 收起语言名到 56px,实测 ≥860px 顶栏 0 溢出) |
| S5-P20 | 顶栏在 ≤820px 溢出(移动端导航无方案) | S5 | P2 | 2 h | ⚪ 待开工:实测 820px 顶栏溢出 33px、768px 85px、600px 253px;≤900px 已无侧栏且没有任何汉堡菜单,等于移动端无法导航。这一层是既有缺口(移除语言控件后 820px 仍溢出 33px,可压缩仿真亦不变),与 S5-P19 无关。需要的是移动端导航方案(抽屉/汉堡 + 顶栏分段折叠),而非继续微调间距 |
| S5-P21 | 发布到私有 npm 源 | S5 | P1 | 1 h | ✅ 已完成(gitea.mymoyu.top 的 npm registry,匿名可读;发布 3 个可互换包名 @root/ui(推荐,支持一行 .npmrc)/ chunyu-ui / aurora-admin-design;Vite+Vue3 真实工程实测安装→构建→浏览器渲染通过)。待决:是否也发布到公共 npm(本机无凭据,需用户 npm login 或 token;chunyu-ui/aurora-admin-design 在公共 npm 均未被占用) |
S5-P20 实测数据(2026-09-20,http://127.0.0.1:3311/site/index.html#/component/button):
| 视口宽 | 顶栏溢出 | 说明 |
|---|---|---|
| ≥1100 | 0 | 语言名 + 图标(115px) |
| 1024 / 900 / 860 | 0 | 语言名收起(56px),链接近距收到 8px |
| 820 | 33px | 移除语言控件仍溢出 33px → 与语言控件无关 |
| 768 | 85px | |
| 600 | 253px |
附 · 规划修正记录
执行中发现规划与实际不符时,以实测为准,在此登记。
| 日期 | 任务 | 规划原写 | 实测 | 处理 |
|---|---|---|---|---|
| 2026-09-11 | P4 | data.js 目标 < 400 KB(推测 sources 是最大头) |
sources 实测占 89.6%(888KB/991KB);拆出后可达 ~110KB |
目标改为 < 150 KB,策略与体积数据全部替换为实测 |
| 2026-09-11 | P4 | 预计算 API 表估算 < 30 KB | 实测 1.0 KB(比估算乐观 30 倍) | 更新为实测值 |
| 2026-09-11 | P2 | 包名可用性未知 | registry 查询:aurora-admin-design 未被占用 |
补入「前置验证已完成」 |
| 2026-09-11 | P4 | 只说「改成 fetch」,未识别难点 | app.js 有 10 处消费 sources,其中 2 处是渲染期同步解析 |
补入「依赖分析」与三阶段执行建议 |
| 2026-09-11 | P2/P4 | 验收命令有 3 处会误判(npm 不可用时误报 OK 等) | 实测触发 | 已改写为显式判断 exit code |
| 2026-09-19 | PLAN §7/§8 | 「不改 site/dev-server.js(已工作良好)」 |
单个 GET /site/%00 触发 ERR_INVALID_ARG_VALUE 未捕获异常,进程退出 |
以实测为准:修复该文件(NUL 双重拦截 + readFile try/catch + AA_PORT),并新增 tools/verify-dev-server.mjs(20 checks / 3 条反例断言);PLAN 两处已标注 ⚠️ 规划修正 |
| 2026-09-19 | 部署方式 | 未规定(实际为手工拷贝到 /opt/aurora-admin) |
手工拷贝导致 ① .dockerignore 未随行 → 规范原文入镜像并对外 200;② 曾修好的 absolute_redirect off 被覆盖丢失 |
新增任务包 S5-P13:以 .dockerignore 为唯一真源的 tools/pack-deploy.mjs + AGENTS §九 标准流程;2026-09-19 已按该流程实际部署并验收 |
| 2026-09-19 | TESTING 基线 | 1009 通过 / 35 N/A / 共 1044 条断言 | 工作树实测 1017 通过 / 0 失败 / 34 N/A / 共 1051(连跑 10 次一致) | 更新 TESTING.md 与 AGENTS.md §八 的数字并标注口径来源 |
| 2026-09-20 | S1-P2 | 目标写「能拿到令牌 + 组件样式」,据此实现为 CSS/HTML 分发包 | 用户预期是组件库(import { AaButton } from '.../vue3');实测 dist/ 无任何可 import 组件,main 指向 CSS 文件 |
以用户预期为准:补三端入口 + exports + peerDependencies,登记为 S5-P18 并已完成。原 S1-P2 验收命令全部仍通过,未破坏既有承诺 |
| 2026-09-20 | 5 端实现 | AGENTS 写「79 组件 × 5 端实现」 | 真编译时发现 RangeQuickPicker(React/Vue3)与 CodeInput(Vue3)无法编译——此前从未真编译过,结构断言测不出 |
已修;新增 tools/verify-package-import.mjs 的编译级检查(真编译 + 真渲染)防止复发 |