Repository Wiki
Brandon030722/ark-ui-skill

验证流程与质量关卡

Ark UI skill 通过一套分层的验证流程(validation pipeline)与质量关卡(quality gates)来约束交付物:从项目自身的测试/构建,到内置的启发式审计脚本 scripts/audit-ark-ui.mjs,再到浏览器中的交互与双视口(桌面/竖屏)人工检查,最后以 references/depth-levels.md 中的六轴评分卡(validation scorecard)判定深度契约是否达成。本页是这套机制的唯一权威参考。

Purpose and Scope

本页覆盖 Ark UI skill 中"实现之后如何验证"的完整机制:

  • SKILL.md 中 Validate 与 Iterate with an evidence lock 两节定义的验证步骤与证据锁(evidence lock)规则
  • scripts/audit-ark-ui.mjs 启发式审计脚本的检查项、严重级别与退出码语义
  • references/depth-levels.md 中的 Validation scorecard(六轴评估)与 Escalation/reduction(升降级)规则
  • scripts/capture-showcases.mjs 的截屏回归关卡(水平溢出即失败)
  • README 中的 skill 级验证入口 quick_validate.py

明确不在本页范围内、由兄弟页面承接的内容:

  • 审计脚本要检查的设计规则本身(语义令牌、reduced-motion、focus-visible 等)→ 见"实现契约与深度系统"
  • CSS 证据采集与来源台账(analyze-css-evidence.py、source ledger)→ 见"证据研究与来源管理"
  • 家族(family)选择与各家族视觉语言 → 见"产品家族体系"

Overview

这套验证体系的设计意图可以概括为一句话:证据先于判断,关卡先于迭代。

Ark UI 的工作循环是"研究证据 → 锁定契约 → 实现 → 验证 → 迭代"。验证流程不是实现后的收尾动作,而是迭代循环的前置条件——SKILL.md 明确规定"compile, interaction, contrast, and screenshot checks pass"(编译、交互、对比度与截图检查全部通过)之后才允许进入下一轮迭代。这样设计的动机:

  1. 防止基于猜测或陈旧状态做优化:"Never optimize from a guessed or stale state"。所有视觉判断必须来自真实渲染的真实状态。
  2. 防止深度失控:深度(depth)评估必须基于"shell 转换、stage 分层、组件覆盖"等结构维度,而不是元素数量或装饰密度,否则会被诱导成堆装饰。
  3. 防止法律与来源风险:官方 CDN 资源与受保护 logo 的嵌入是最高严重级别(error)的关卡。
  4. 防止可用性退化:可达性(reduced-motion、focus-visible、landmark、ID 引用)在脚本中被强制为 error 级。

验证关卡共分四道,按执行顺序:

关卡工具/依据通过标准
G1 项目门目标项目自身 test / lint / build全部通过
G2 脚本审计门audit-ark-ui.mjserrors.length === 0(退出码 0)
G3 浏览器门桌面 + 竖屏双视口人工检查无裁切、无碰撞、焦点顺序正确、reduced-motion 正常
G4 契约门depth-levels 评分卡 + 截图对比与所选深度匹配,逐轴判断

Architecture

Loading diagram...

图中体现的关键设计:

  • 关卡串联而非并联:G2(脚本)失败直接回到实现阶段,不会进入昂贵的人工浏览器检查。便宜的自动检查永远先跑。
  • error/warning 分级:audit-ark-ui.mjs 内部把检查分为决定退出码的 error 与只提示的 warning,让"必须修"与"建议修"分离。
  • QA 入口服务截图证据:当难以触达某个代表性状态时,用确定性的非持久 QA 入口暴露该状态,而不是放弃交互证据——这是"证据锁"的核心配套机制。

主流程:从实现到放行

第一步:项目门(G1)

SKILL.md Validate 第 1 条要求先跑目标项目自身的 tests / lint / build。这一关不依赖 skill 提供的任何脚本,纯粹是宿主项目的回归底线。

第二步:脚本审计门(G2)

运行方式(出自 SKILL.md Validate 第 2 条):

bash
node "$CODEX_HOME/skills/ark-ui/scripts/audit-ark-ui.mjs" <html-or-css-path>

Source: SKILL.md

脚本会对目标路径做递归收集,然后输出 JSON 报告并以 errors.length ? 1 : 0 决定退出码。文件收集逻辑是理解其边界的第一步:

javascript
1const root = resolve(process.argv[2] ?? '.'); 2const supported = new Set(['.html', '.css', '.js', '.mjs', '.jsx', '.ts', '.tsx', '.vue', '.svelte']); 3 4async function collect(path) { 5 const info = await stat(path); 6 if (info.isFile()) return supported.has(extname(path)) ? [path] : []; 7 const entries = await readdir(path, { withFileTypes: true }); 8 const nested = await Promise.all(entries 9 .filter((entry) => !['node_modules', '.next', 'dist', 'build'].includes(entry.name)) 10 .map((entry) => collect(resolve(path, entry.name)))); 11 return nested.flat(); 12} 13 14const files = await collect(root); 15if (!files.length) { 16 console.error(`No supported frontend files found at ${root}`); 17 process.exit(2); 18}

Source: scripts/audit-ark-ui.mjs

要点:

  • 只处理 9 种前端扩展名;传入二进制或非前端目录会得到退出码 2。
  • 显式跳过 node_modules / .next / dist / build,避免把依赖与产物当作交付物审计。
  • 所有检查基于把全部文件文本拼接成 all、HTML/CSS/JS 分别拼接为 html/css/js 四个大字符串后做正则匹配——这是一种刻意的**启发式(heuristic)**设计:不解析语法树、不依赖框架,保证对任意技术栈(Vue/Svelte/React/原生)都能跑。

审计项分级表

audit-ark-ui.mjs 通过统一的 check() 收集结果:

javascript
1const errors = []; 2const warnings = []; 3const passes = []; 4 5function check(condition, pass, fail, severity = 'warning') { 6 if (condition) passes.push(pass); 7 else (severity === 'error' ? errors : warnings).push(fail); 8}

Source: scripts/ark-ui/../audit-ark-ui.mjs

检查严重级别通过条件(正则语义)设计意图
官方 CDN 资产error不匹配 web(-ipv6)?.hycdn.cn 或 hypergryph.com/*.png/jpg/svg/woff…法律红线:不得内嵌受保护资产
品牌/logo 引用error不出现 (rhodes island|arknights|endfield|monster siren)…(logo|wordmark|copyright)同上,防伪造授权
reduced-motionerrorCSS 中存在 prefers-reduced-motion 规则可达性不可妥协
响应式/方向errorCSS 中存在 @media … (max-width|orientation)不允许只缩放桌面布局
focus-visibleerrorCSS 中存在 :focus-visible键盘焦点可见
viewport metaerrorHTML 存在 name="viewport"移动正确渲染前提
lang 属性error<html … lang= 存在屏幕阅读器语言
语义 landmarkerror存在 <main/nav/header/footer>结构语义
ID 引用解析erroraria-* / for 引用的 id 全部声明防 ARIA 悬空引用
语义颜色令牌warningCSS 中存在 --ark-(ink|paper|signal)鼓励语义令牌而非散落字面色
JS 选择器钩子warningJS 中 querySelector(All) 的字面选择器在 HTML 中可命中防"改了类名断 wiring"
科幻陈词滥调warning不出现 scanline / glitch / random-hex / matrix-rain防风格化退化为俗套噪声
字面颜色数量warning#hex 出现次数 < 180推动收敛进令牌系统

其中"JS 选择器钩子"检查尤其值得展开——它是 SKILL.md 实现守则第 3 条(重命名 starter 类名/ID 时必须同步更新所有 JS 选择器)的机械化执行:

javascript
const queriedSelectors = [...js.matchAll(/\bquerySelector(?:All)?\(\s*(["'])([^"']+)\1\s*\)/g)] .map((match) => match[2]); const missingSelectors = [...new Set(queriedSelectors.filter((selector) => !htmlHasSelector(selector)))];

Source: scripts/audit-ark-ui.mjs

htmlHasSelector() 依次尝试 #id、.class、[attr] 三种形式,把选择器翻译成对 HTML 文本的匹配;ID 引用检查则从 aria-controls/aria-labelledby/aria-describedby/for 抽取被引用 id,与声明的 id 集合求差集。

第三步:浏览器门(G3)

SKILL.md Validate 第 3、4 条要求在桌面宽度与竖屏宽度各渲染一次,检查裁切、文本碰撞、焦点顺序、激活态与 reduced-motion 行为;并在浏览器中实际操作主要控件、检查运行时错误。配套守则是"do not infer behavior from static markup alone"——静态标记不能替代运行时证据。

对 skill 自带的 showcase 样本,这一关由脚本机械化:

bash
node "$CODEX_HOME/skills/ark-ui/scripts/capture-showcases.mjs" --mobile

Source: README.md

capture-showcases.mjs 在精确的桌面与(可选)移动 CDP 视口下渲染五个样本,遇到水平溢出(horizontal overflow)直接失败——把"不许用横向滚动解决布局碰撞"变成可执行断言(脚本职责出自 SKILL.md Bundled code 清单)。

第四步:契约门(G4)与评分卡

references/depth-levels.md 定义了在代表性视口上评审的六个轴:

  1. Shell transformation(外壳转换) — 原生结构保留 / 重组 / 全系统重构 / 按模式定制
  2. Stage layering(舞台分层) — 无或一层 / 一到两层 / 两到四层 / 四到六层
  3. Component coverage(组件覆盖) — 仅关键控件 / 共享主件 / 完整共享集 / 状态特定变体
  4. State instrumentation(状态仪表化) — 选择提示 / 分组状态 / 全系统真实状态 / 改变构图的实时状态
  5. Motion coordination(动效协调) — 仅直接反馈 / 揭示+直接 / 多族协调 / 章节级编排
  6. Responsive recomposition(响应式重构) — 安全堆叠 / 外壳适配 / 完整重美术方向 / 模式特定构图

评分卡的元规则(防止机械凑分):

"The level is a holistic judgment. Do not average scores mechanically or add decorative elements merely to increase a score. A level fails if its extra depth reduces task clarity, truthful state visibility, accessibility, or performance beyond the product's budget." Source: references/depth-levels.md

此外还有两条边界校验(SKILL.md Validate 第 10 条):

  • 最密的屏幕不得超过所选深度一个局部级别;
  • 主屏幕不得低于所选深度;
  • 有意为之的局部例外必须记录。

Core Flow:证据锁迭代中的验证回路

Loading diagram...

这条回路里的"证据锁"(evidence lock)规则是验证流程能闭环的原因:

  1. 改代码前必须先命名主家族、深度与台账中的确切公开模式——把审美争议锚定在可核查证据上。
  2. 每轮只改"一个共享令牌族 + 一个深度行为 + 一个代表性屏幕",把变更控制在可归因范围内。
  3. 截图必须在相同视口下做 before/after;若结果没有"visibly closer to the cited evidence"、降低了可读性、引入碰撞或让真实机制更难找到,则回退或修订。
  4. "compile, interaction, contrast, and screenshot checks pass"是进入下一轮的硬前置。

Source: SKILL.md

QA 入口的严格定义

SKILL.md 对"非持久 QA 入口"给出了操作性定义,避免它变成永久后门:

  • 非持久 = 测试挂载显式抑制存档写入与存档删除,而不仅是创建内存对象;
  • 坐标自动化不可靠时,通过 QA 入口直接暴露确切的模态/确认状态,而不是放弃交互证据;
  • 对自动隐藏的过渡,QA 入口可以以"禁用自动隐藏"方式重启正常动画,但不得改变生产时序;
  • 必须在瞬态 UI 可见时完成截屏与验证。

Source: SKILL.md

Usage Examples

运行启发式审计(HTML 或 CSS 路径)

bash
node "$CODEX_HOME/skills/ark-ui/scripts/audit-ark-ui.mjs" <html-or-css-path>

Source: SKILL.md

审计报告输出格式

脚本最终以 JSON 打印报告并按错误数决定退出码,便于 CI 直接消费:

javascript
const report = { root, files: files.length, errors, warnings, passes }; console.log(JSON.stringify(report, null, 2)); process.exit(errors.length ? 1 : 0);

Source: scripts/audit-ark-ui.mjs

深度契约的稳定键表示(契约门校验对象)

审计与人工评审最终对照的是用稳定键表达的契约,HTML 用根属性、React 用组件 props,两轴(family/depth)可独立变化:

html
<html data-ark-theme="endfield" data-ark-depth="complex">

Source: references/depth-levels.md

jsx
<ArkShell theme="endfield" depth="complex" />

Source: references/depth-levels.md

skill 级验证入口(维护本 skill 时)

bash
python3 "$CODEX_HOME/skills/.system/skill-creator/scripts/quick_validate.py" "$CODEX_HOME/skills/ark-ui"

Source: README.md

API Reference

audit-ark-ui.mjs(CLI)

bash
node audit-ark-ui.mjs [root]

参数:

  • root(可选,默认 .):要审计的 HTML/CSS 文件或目录;目录会递归收集支持的扩展名并跳过 node_modules/.next/dist/build。

输出: JSON 报告 { root, files, errors[], warnings[], passes[] }。

退出码:

  • 0:无 error 级失败(warning 不影响)
  • 1:存在至少一个 error
  • 2:路径下没有受支持的前端文件

capture-showcases.mjs(CLI)

bash
node capture-showcases.mjs [--mobile]

参数:

  • --mobile:附加移动端 CDP 视口截屏。

行为: 在精确桌面视口(及可选移动视口)渲染五个 showcase 样本,遇到水平溢出即失败。

说明:capture-showcases.mjs 与 quick_validate.py、analyze-css-evidence.py 的完整内部实现未在本次源码阅读范围内,以上职责描述取自 SKILL.md 的 Bundled code 清单与 README 中的调用示例;实现细节未在源码中核实之处如实标注。

Failure Modes, Edge Cases & Concurrency

已知边界与降级路径

情形行为依据
传入路径无受支持前端文件退出码 2,stderr 提示collect() + 显式 process.exit(2)
出现 error 级失败退出码 1,CI 阻断process.exit(errors.length ? 1 : 0)
只有 warning退出码 0,仅提示同上
无法解析的选择器形式htmlHasSelector 返回 true(默认放行)只覆盖 #id / .class / [attr] 三种字面形式
模板字符串拼接的动态选择器不被 querySelector 正则捕获启发式设计的已知盲区
深度超标或不足契约门不通过,需记录或修正Validate 第 10 条
状态难以触达必须建非持久 QA 入口,不得放弃证据Evidence lock 第 6 条

语义边界(为什么是启发式而非解析器)

审计脚本刻意选择拼接文本 + 正则而非 AST:

  • 要兼容 .vue/.svelte/.jsx/.tsx 等所有技术栈,任何单一解析器都会失配;
  • 检查项本身是"是否存在某类规则/资产"的存在性判断,正则足够;
  • 代价是精度有限(例如颜色计数 < 180 是经验阈值、陈词滥调靠关键词命中),因此这些项是 warning 而非 error。

Performance & Operational Notes

  • 关卡成本排序:G1(项目构建)与 G2(脚本审计,纯本地文件读取 + 正则)远便宜于 G3(人工浏览器双视口)与 G4(对照评分卡)。架构上把便宜的自动关卡放在前面,避免在必然返工的状态上消耗昂贵检查。
  • 审计脚本 I/O 并行:所有文件用 Promise.all 并发读取,目录遍历也并发展开,对大仓库友好。
  • 截屏回归可机械化:对 skill 自带 showcase,capture-showcases.mjs 把"同视口 before/after"要求固化为脚本断言(水平溢出即失败),可作为第三方项目自建截屏回归的模板。
  • 升降级同样以验证为前置:depth-levels 的 Escalation/reduction 规则要求"每补上一个缺失系统就先 Validate 再继续加";降级时先移除信息价值最低的持久层,且绝不靠隐藏必要信息或把字号压到不可读来"假装极简"。

Extension Points

  • 新增审计项:在 audit-ark-ui.mjs 中追加一条 check(condition, passText, failText, severity) 即可,severity 决定它是 error(阻断)还是 warning(提示)。
  • 调整阻断策略:把某条 warning 升为 error 只需改 check() 的第四个参数,例如把"语义颜色令牌"升级为硬门。
  • 接入 CI:直接消费退出码(0/1/2)与 JSON 报告即可,无需额外封装。
  • 评分卡扩展:六轴之外新增轴时,应保持"holistic judgment"原则——不允许机械平均,也不允许为提分加装饰。
  • 家族/深度双轴解耦:契约用 data-ark-theme + data-ark-depth 两个根属性表达,验证时可独立替换任一轴而不动语义内容与可访问名称。

Sources

(3 files)