iter(g297): 新增迭代循环诊断命令
This commit is contained in:
@@ -3059,6 +3059,15 @@
|
||||
"motivation": "提交守卫写成「找到焦点目标才中止」,而焦点目标只查 input/textarea/select;非搜索式下拉、评分、开关容器里没有原生 input,firstBad 为 null 时校验有错也照样调 onSubmit(demo 的「城市」必填下拉即可复现)。",
|
||||
"acceptance": "① 中止判据改为错误条数 bad > 0;② 焦点落点按 控件 → 触发器(button/[tabindex]) → 字段容器(tabIndex=-1) 三级退让;③ manifest 的 form 步骤与 demo 描述同步说明;④ npm run check 全绿。",
|
||||
"notes": "form 提交守卫改判错误条数;焦点按 控件 → 触发器 → 字段容器(tabIndex=-1) 三级退让,自绘控件字段也能被定位并拦住提交"
|
||||
},
|
||||
{
|
||||
"id": "g297",
|
||||
"domain": "tooling",
|
||||
"status": "open",
|
||||
"title": "新增 ua loop:循环锁 / 留痕 / 待办与选题建议一次给全",
|
||||
"motivation": "接力实例判断「该不该抢锁、上一条做完没、下一条选谁」过去要翻 .ua/loop.lock 的 mtime、tail 留痕、再数 backlog,或去查调度器 sqlite;口径散在 ITERATION.md 里靠人记。",
|
||||
"acceptance": "① ua loop 输出锁心跳(<15 分钟判定与协议同源)、上次留痕、backlog open 与视觉欠账数、下一条选题建议;② --json 给脚本;③ 进 usage 串与 CLI 清单 exports(doctor 的 ghost 检查通过);④ ITERATION.md 写明诊断入口、AGENTS 的 CLI 列表补一行;⑤ npm run check 与 doctor 全绿。",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "ui-arch-registry-index/0.1",
|
||||
"generatedAt": "2026-10-26T15:36:58.959Z",
|
||||
"generatedAt": "2026-10-26T15:38:27.731Z",
|
||||
"repoRoot": ".",
|
||||
"packages": [
|
||||
{
|
||||
@@ -4681,7 +4681,9 @@
|
||||
"ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数与每类命中行号(line / prop / value / hint,Agent 可直接定位)",
|
||||
"ua audit:contrast [--json|--list]:对比度门禁,把令牌里 18 组「前景色 × 底色」配对按 WCAG 公式算成对比度(求值支持 var() 与 color-mix,半透明底先压到页面底色),亮/暗 × 常规/高对比四套分算,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6 / 非文本 3)退出码 1;--json 出逐对明细,--list 只列配对表",
|
||||
"ua audit:motion [--json]:动效兜底门禁,两类必查——① 带 infinite 的动画逐个核对是否在 prefers-reduced-motion 块里 animation: none;② 悬停 / 按压的位移(translate)是否在减少动效下归零(认 ua- 类通配的全局规则)。缺兜底退出码 1(--json 出逐条明细)",
|
||||
"ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段"
|
||||
"ua audit:surface [--json|--list]:表面阶梯门禁,逐档核对底色对底色的亮度比(页面底 → 表面 → 悬停底 → 按下底 ≥1.04、凹陷底 ≥1.02),亮/暗 × 常规/高对比四套主题各算一遍,不达标退出码 1;对比度只管文字读不读得清,管不到「悬停有没有反馈」(实测过 1.008 的悬停底)",
|
||||
"ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段",
|
||||
"ua loop [--json]:迭代循环诊断,一眼看清锁心跳(是否 15 分钟内)、上次留痕、backlog 待办与视觉欠账数,并给出本轮该不该抢锁、下一条该选谁;多实例错位接力时不必翻调度器数据库"
|
||||
],
|
||||
"keyTypes": [
|
||||
"ua.manifest.json 清单结构",
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
可用工具:`list_components` / `get_component`(传 `format: "md"` 直接拿 Markdown 片段,等同 `ua info --md`)/ `search_components` / `get_tokens`(返回 --ua-* 令牌清单,避免硬编码颜色间距)/ `list_recipes` / `get_recipe`(取跨组件组合配方:骨架代码 + 要点 + 用到的组件 id,配好再逐个 `get_component` 查参数)。
|
||||
|
||||
2. **CLI**:`node packages/tools/cli/bin/ua.js list [--json|--components] | search <关键词> | info <name> [--json|--md](--md 出可粘贴的 Markdown 片段:标题 + 参数表 + 用法 + 示例) | recipes [编号] [--json] | index | docs | style | verify | changelog`(`--json` 给脚本与 Agent 用,不必解析文本);`search` 覆盖 id / 标题 / 参数名 / 参数说明 / 步骤五层 + 配方三层(标题 / 配方组件 / 要点,命中行直接给 `ua recipes <编号>` 指路),命中非 id 标题时会标出命中字段(如「← 参数说明含『处理中』」),`--json` 输出 `{ components, recipes }` 两数组、各项带 `matched` 字段
|
||||
2. **CLI**:`node packages/tools/cli/bin/ua.js loop [--json](迭代循环诊断:锁心跳 / 上次留痕 / 待办数) | list [--json|--components] | search <关键词> | info <name> [--json|--md](--md 出可粘贴的 Markdown 片段:标题 + 参数表 + 用法 + 示例) | recipes [编号] [--json] | index | docs | style | verify | changelog`(`--json` 给脚本与 Agent 用,不必解析文本);`search` 覆盖 id / 标题 / 参数名 / 参数说明 / 步骤五层 + 配方三层(标题 / 配方组件 / 要点,命中行直接给 `ua recipes <编号>` 指路),命中非 id 标题时会标出命中字段(如「← 参数说明含『处理中』」),`--json` 输出 `{ components, recipes }` 两数组、各项带 `matched` 字段
|
||||
|
||||
3. **静态索引**:`.ua/index.json`(机器可读索引)、`llms.txt`(速览)或 **`llms-full.txt`(全量 Agent 文档:逐组件参数 / 回调 / 手把手步骤 / 令牌 / 组合配方,无图形纯文本,喂给 Agent 首选)**。
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
- **时间预算**:单次唤起最多连续迭代约 50 分钟,到点在完成当前轮后停止(防止与下次唤起重叠)。**预算判断必须基于客观时钟**:点火时用 `date +%s` 记录起点并写入 `.ua/loop.lock`,每轮开始时重新取 `date +%s` 与起点求差,差值 ≥ 2700 秒(45 分钟)即到点;**禁止凭感觉估算时长**——LLM 无内部时钟,自估时长系统性偏高(实测自报 40+ 分钟而实际约 30 分钟)。
|
||||
- **熔断**:同一 backlog 条目连续 2 轮失败 → 停止循环,锁照常释放,等待下次唤起。
|
||||
- **重启语义**:锁心跳超过 15 分钟视为循环中断(崩溃/超时),调度器删旧锁、重新点火。
|
||||
- **诊断入口**:拿不准该不该抢锁、上一条做完没有,先跑 `ua loop`(锁心跳 / 上次留痕 / backlog 待办与视觉欠账 / 下一条选题建议一次给全);它只读不写,`--json` 给脚本用,判据与本文的 15 分钟口径同源,不要另写一套。
|
||||
- **调度器约束(踩过的坑,勿重复)**:本地调度器只支持 `HOURLY` 及以上粒度(`DAILY/WEEKLY/MONTHLY/YEARLY`),**`FREQ=MINUTELY` / `FREQ=SECONDLY` 不受支持**——写入后 `next_run_at` 会变成 `NULL`,首跑之后永久静默(实测 10/23–10/26 停摆三天)。因此「高频监护」不能靠加密 rrule 实现,只能靠**单次唤起内的连续循环**;心跳间隔 = 1 小时,循环预算 45–50 分钟,两者共同保证覆盖面。任何对自动化的改动(改 prompt、改 rrule、暂停恢复)都必须回查 `next_run_at` 非空,改 prompt 会把状态重置为 `PAUSED`,需再单独置 `ACTIVE`。
|
||||
- **有效性自检**:每轮迭代首步除同步仓库外,检查 `.ua/loop.lock` 与 `automation_runtime_state.last_run_at`;若发现上次运行距今超过 2 小时(说明调度中断),在留痕 `summary` 里注明「调度中断后恢复」,便于周报统计停摆时长。
|
||||
|
||||
|
||||
+3
-1
@@ -1,6 +1,6 @@
|
||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||
|
||||
> 自动生成于 2026-10-26T15:36:58.959Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||
> 自动生成于 2026-10-26T15:38:27.731Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||
|
||||
## 快速上手
|
||||
@@ -2688,7 +2688,9 @@ API 速览:
|
||||
- ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数与每类命中行号(line / prop / value / hint,Agent 可直接定位)
|
||||
- ua audit:contrast [--json|--list]:对比度门禁,把令牌里 18 组「前景色 × 底色」配对按 WCAG 公式算成对比度(求值支持 var() 与 color-mix,半透明底先压到页面底色),亮/暗 × 常规/高对比四套分算,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6 / 非文本 3)退出码 1;--json 出逐对明细,--list 只列配对表
|
||||
- ua audit:motion [--json]:动效兜底门禁,两类必查——① 带 infinite 的动画逐个核对是否在 prefers-reduced-motion 块里 animation: none;② 悬停 / 按压的位移(translate)是否在减少动效下归零(认 ua- 类通配的全局规则)。缺兜底退出码 1(--json 出逐条明细)
|
||||
- ua audit:surface [--json|--list]:表面阶梯门禁,逐档核对底色对底色的亮度比(页面底 → 表面 → 悬停底 → 按下底 ≥1.04、凹陷底 ≥1.02),亮/暗 × 常规/高对比四套主题各算一遍,不达标退出码 1;对比度只管文字读不读得清,管不到「悬停有没有反馈」(实测过 1.008 的悬停底)
|
||||
- ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段
|
||||
- ua loop [--json]:迭代循环诊断,一眼看清锁心跳(是否 15 分钟内)、上次留痕、backlog 待办与视觉欠账数,并给出本轮该不该抢锁、下一条该选谁;多实例错位接力时不必翻调度器数据库
|
||||
|
||||
一行用法:
|
||||
|
||||
|
||||
@@ -1750,6 +1750,41 @@ function cmdAuditSurface(args) {
|
||||
process.exit(result.failures.length === 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
/*** 迭代循环诊断:锁心跳 / 上次留痕 / 待办数一眼看清,多实例接力时不必翻调度器库或靠猜(--json 给脚本用) ***/
|
||||
function cmdLoop(args) {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const lockFile = path.join(INDEX_DIR, 'loop.lock');
|
||||
let lock = null;
|
||||
if (fs.existsSync(lockFile)) {
|
||||
const raw = Number(fs.readFileSync(lockFile, 'utf8').trim()); // 锁内是点火时的 epoch 秒
|
||||
const stamp = raw || Math.floor(fs.statSync(lockFile).mtimeMs / 1000); // 取不到数字就用 mtime 兜底
|
||||
const age = now - stamp;
|
||||
lock = { stamp, age, fresh: age < 900 }; // 15 分钟内的心跳算活着,与 ITERATION.md 的抢锁判据同口径
|
||||
}
|
||||
const logFile = path.join(INDEX_DIR, 'iterations.log');
|
||||
const text = fs.existsSync(logFile) ? fs.readFileSync(logFile, 'utf8') : '';
|
||||
const lines = text.split('\n').filter((l) => l.trim()); // 按行切即可:JSON.parse 容忍行尾 \r,不必额外归一化
|
||||
let last = null;
|
||||
try { last = lines.length ? JSON.parse(lines[lines.length - 1]) : null; } catch { last = null; } // 末行坏了不该让诊断命令崩,lint-iterations 会单独报
|
||||
const data = JSON.parse(fs.readFileSync(path.join(INDEX_DIR, 'backlog.json'), 'utf8'));
|
||||
const items = Array.isArray(data) ? data : data.items;
|
||||
const open = items.filter((i) => i.status === 'open');
|
||||
const pending = open.filter((i) => i.domain === 'visual');
|
||||
if (args.includes('--json')) {
|
||||
const out = { now, lock, entries: lines.length, last, total: items.length, open: open.length, openVisual: pending.length };
|
||||
console.log(JSON.stringify(out, null, 2));
|
||||
return;
|
||||
}
|
||||
console.log(`当前时间:${new Date(now * 1000).toLocaleString('zh-CN', { hour12: false })}`);
|
||||
if (!lock) console.log('循环锁:无 → 可直接点火(写 .ua/loop.lock 后进连续迭代)');
|
||||
else if (lock.fresh) console.log(`循环锁:心跳 ${lock.age} 秒前(< 15 分钟)→ 已有实例在跑,本轮不要抢锁`);
|
||||
else console.log(`循环锁:心跳 ${lock.age} 秒前(≥ 15 分钟)→ 疑似中断,可删锁重新点火`);
|
||||
console.log(last ? `上次留痕:${last.id} ${last.result} @ ${last.ts}(${last.commit})` : '上次留痕:无');
|
||||
console.log(`累计留痕:${lines.length} 条|backlog:${items.length} 条,open ${open.length} 条(视觉欠账 ${pending.length} 条)`);
|
||||
if (!open.length) console.log('选题:无 open 条目 → 本轮需按《选题规则》自补一条');
|
||||
else console.log(`选题:下一条取 ${pending[0]?.id ?? open[0].id}(${pending[0] ? '视觉欠账优先' : '按数组顺序'})`);
|
||||
}
|
||||
|
||||
/*** 迭代留痕校验:.ua/iterations.log 每行 JSON 字段与格式必须符合 ITERATION.md 规范 ***/
|
||||
function cmdLintIterations() {
|
||||
const file = path.join(INDEX_DIR, 'iterations.log');
|
||||
@@ -1929,10 +1964,13 @@ switch (cmd) {
|
||||
case 'audit:surface':
|
||||
cmdAuditSurface(args);
|
||||
break;
|
||||
case 'loop':
|
||||
cmdLoop(args);
|
||||
break;
|
||||
case 'mcp':
|
||||
cmdMcp();
|
||||
break;
|
||||
default:
|
||||
console.log('usage: ua <index|list|search <kw>|info <name> [--json|--md]|recipes [编号] [--json]|docs|tokens [--json|--unused]|style|sync-counts|verify|doctor|commit-lint|lint-iterations|audit:visual [--json|--baseline]|audit:contrast [--json|--list]|audit:motion [--json]|audit:surface [--json|--list]|changelog|mcp>');
|
||||
console.log('usage: ua <index|list|search <kw>|info <name> [--json|--md]|recipes [编号] [--json]|docs|tokens [--json|--unused]|style|sync-counts|verify|doctor|commit-lint|lint-iterations|audit:visual [--json|--baseline]|audit:contrast [--json|--list]|audit:motion [--json]|audit:surface [--json|--list]|loop [--json]|changelog|mcp>');
|
||||
process.exit(cmd ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
"ua audit:visual [--json|--baseline]:视觉漂移门禁,统计皮肤包里各类硬编码视觉字面量(字号/动效时长/阴影/圆角/焦点环/内距/控件高度/字重/行高/层级/边框色)并与 .ua/visual-baseline.json 棘轮比对,超基线退出码 1;--baseline 重写基线,--json 出机器可读计数与每类命中行号(line / prop / value / hint,Agent 可直接定位)",
|
||||
"ua audit:contrast [--json|--list]:对比度门禁,把令牌里 18 组「前景色 × 底色」配对按 WCAG 公式算成对比度(求值支持 var() 与 color-mix,半透明底先压到页面底色),亮/暗 × 常规/高对比四套分算,低于阈值(正文 7 / 常规 4.5 / 禁用 2.6 / 非文本 3)退出码 1;--json 出逐对明细,--list 只列配对表",
|
||||
"ua audit:motion [--json]:动效兜底门禁,两类必查——① 带 infinite 的动画逐个核对是否在 prefers-reduced-motion 块里 animation: none;② 悬停 / 按压的位移(translate)是否在减少动效下归零(认 ua- 类通配的全局规则)。缺兜底退出码 1(--json 出逐条明细)",
|
||||
"ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段"
|
||||
"ua audit:surface [--json|--list]:表面阶梯门禁,逐档核对底色对底色的亮度比(页面底 → 表面 → 悬停底 → 按下底 ≥1.04、凹陷底 ≥1.02),亮/暗 × 常规/高对比四套主题各算一遍,不达标退出码 1;对比度只管文字读不读得清,管不到「悬停有没有反馈」(实测过 1.008 的悬停底)",
|
||||
"ua mcp:零依赖 stdio MCP server,暴露 list_components / get_component / search_components / get_tokens / list_recipes / get_recipe;get_component 支持 format: md 直接取可粘贴的 Markdown 片段",
|
||||
"ua loop [--json]:迭代循环诊断,一眼看清锁心跳(是否 15 分钟内)、上次留痕、backlog 待办与视觉欠账数,并给出本轮该不该抢锁、下一条该选谁;多实例错位接力时不必翻调度器数据库"
|
||||
],
|
||||
"keyTypes": [
|
||||
"ua.manifest.json 清单结构",
|
||||
@@ -53,4 +55,4 @@
|
||||
],
|
||||
"components": []
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user