@qilitt-mickey/vue3-temp-skill 1.1.71 → 1.1.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +3 -2
- package/package.json +1 -1
- package/parser/index.js +51 -8
- package/references/code-quality.md +3 -2
- package/references/design-apply.md +4 -4
- package/references/steps/maintenance.md +2 -2
- package/references/steps/step1-match.md +2 -2
- package/references/steps/step2-structure.md +1 -1
- package/references/steps/step3b-shell.md +2 -2
- package/references/steps/step5-gates.md +1 -1
- package/scripts/apply-final-gate.mjs +4 -4
- package/scripts/check.mjs +38 -5
- package/scripts/design-audit.mjs +20 -16
package/SKILL.md
CHANGED
|
@@ -32,7 +32,7 @@ tags: [vue3, typescript, element-plus, pinia, vite, unocss, crud, demo, code-qua
|
|
|
32
32
|
|
|
33
33
|
1. **项目探查**:读取目标项目的 `package.json`、路由、页面、`Re*` 组件、Hooks、API 和样式;以源码事实为准。
|
|
34
34
|
2. **能力路由**:运行 `scripts/match.py "需求" --json`,**仅**按输出中的 `load_files` 阅读 reference;禁止自行展开全部 `references/`。无 Python 时按下方能力组表手动匹配。
|
|
35
|
-
3. **质量闸门**:代码完成后读取 `references/code-quality.md`,执行清单和项目命令。设计变更集落地后追加**设计层闭环审计**:`node scripts/design-audit.mjs --project <落地项目> --container-width <内容区实际宽>`(ERROR 必须清零;无落地项目时自动跳过)。**会话结束前必须跑通终态凭证闸**:`node scripts/apply-final-gate.mjs --project <落地项目> --container-width <内容区实际宽>`(串联 changeset-selfcheck → parser match → parser audit → design-audit 四闸,`FINAL-GATE: PASS` 才算交付完成,凭证写入 `.
|
|
35
|
+
3. **质量闸门**:代码完成后读取 `references/code-quality.md`,执行清单和项目命令。设计变更集落地后追加**设计层闭环审计**:`node scripts/design-audit.mjs --project <落地项目> --container-width <内容区实际宽>`(ERROR 必须清零;无落地项目时自动跳过)。**会话结束前必须跑通终态凭证闸**:`node scripts/apply-final-gate.mjs --project <落地项目> --container-width <内容区实际宽>`(串联 changeset-selfcheck → parser match → parser audit → design-audit 四闸,`FINAL-GATE: PASS` 才算交付完成,凭证写入 `.design/ledger.json`)。
|
|
36
36
|
|
|
37
37
|
## 能力组路由
|
|
38
38
|
|
|
@@ -91,7 +91,7 @@ tags: [vue3, typescript, element-plus, pinia, vite, unocss, crud, demo, code-qua
|
|
|
91
91
|
### 4. 验证与交付
|
|
92
92
|
|
|
93
93
|
- 对照 `references/code-quality.md` 检查类型、组件复用、API、路由、权限、安全、异常反馈、响应式和可演进性。
|
|
94
|
-
- 在目标项目执行 `pnpm lint:fix`、`pnpm type-check`;改动构建配置时再执行 `pnpm build
|
|
94
|
+
- 在目标项目执行 `pnpm lint:fix`、`pnpm type-check`;改动构建配置时再执行 `pnpm build`。**本轮产出或修改设计变更集时,这两条不含还原度检查——必须按 code-quality 协议 3 同链追加终态闸 `apply-final-gate`(串联 selfcheck → match → audit → design-audit,取得 FINAL-GATE: PASS),禁止只跑 lint 链就报「全部质量闸门通过」。**
|
|
95
95
|
- **设计文件落地时追加渲染态验收**:变更集含 `oracle`(或效果图带 `data-sid`)时,跑 `node parser/index.js verify`——抽取项目同名 `data-sid` 元素的 computed style,与效果图期望值逐点 diff;缺口按四类归因(选择器落空 / 权重被压 / 语境错位 / **存在性缺口 G12**:`oracle.absent` 点位仍可见)修正,禁止用 `!important` 掩盖。多轮迭代场景每轮动工前先 `--snapshot` 导快照、交付前 `--baseline` 比对——上轮命中本轮退步 = 基线漂移,先回归上轮修法。申报过的固有残差(`oracle.residual_accepted`)不计缺口。
|
|
96
96
|
- **设计文件落地时核对登记表覆盖率**:变更集带登记表(`coverage.registry` 或 `--registry`)时,跑 `node parser/index.js match <cs.json> --registry <restore-registry.json>`——`C = A` 且无缺口才可交付;parser **当场以登记表为权威重算**,设计侧自报的 `coverage` 数字不参与。单独核验登记表对词典的通道就绪度用 `node parser/index.js registry <restore-registry.json>`(硬缺口 exit 1、通道缺口列出待补字段)。
|
|
97
97
|
- 最终说明:命中能力组、修改文件、功能验证、命令结果、未解决问题和下一步。
|
|
@@ -131,6 +131,7 @@ tags: [vue3, typescript, element-plus, pinia, vite, unocss, crud, demo, code-qua
|
|
|
131
131
|
- 功能:...
|
|
132
132
|
- `pnpm lint:fix`:通过 | 失败(原因)
|
|
133
133
|
- `pnpm type-check`:通过 | 失败(原因)
|
|
134
|
+
- 设计闸(本轮有设计变更集时必填,无则 N/A):`apply-final-gate` → FINAL-GATE: PASS ✅(凭证已写入 ledger.json)
|
|
134
135
|
- 设计验收:无 oracle 时 N/A | 逐点 diff 结果(命中 N / 缺口 M,归因与修正)
|
|
135
136
|
- 缺口回流:无 | 仅限「设计侧未给值」的维度(列出维度名,注明已回流设计侧补齐)
|
|
136
137
|
```
|
package/package.json
CHANGED
package/parser/index.js
CHANGED
|
@@ -111,7 +111,7 @@ function printHelp() {
|
|
|
111
111
|
|
|
112
112
|
用法:
|
|
113
113
|
node parser/index.js build [--dry] 校验映射表并生成 dist/ 产物
|
|
114
|
-
node parser/index.js match <changeset.json...>
|
|
114
|
+
node parser/index.js match <changeset.json...> 对设计变更集执行三级匹配(可多文件;mode 唯一合法值 restore——G8 模式闸强制 oracle.mockup + coverage.registry 齐备,显式 design 直接拒绝)
|
|
115
115
|
主题身份:changeset.theme(优先)→ global.brand_primary(回退);
|
|
116
116
|
基线图色板:图内 data-mockup-theme(优先)→ 文件名主题词(回退)。
|
|
117
117
|
二者不一致时须声明 oracle.theme_mapping(只做色值归一,不减声明)。
|
|
@@ -261,11 +261,18 @@ function matchOne(file, dataset, batchSummary, opts = {}) {
|
|
|
261
261
|
// 「场景还原应给」的软提示升级为「mode=restore 必须给」的硬闸。
|
|
262
262
|
// 二轮强化:oracle/coverage 须有「芯」——oracle.mockup 与 coverage.registry 非空,
|
|
263
263
|
// 堵住空壳伪基线(有对象无内容)继续蒙混。
|
|
264
|
-
// mode
|
|
265
|
-
|
|
266
|
-
const mode = changeset.mode === undefined || changeset.mode === null ?
|
|
267
|
-
if (
|
|
268
|
-
console.error(`✖ mode
|
|
264
|
+
// 2026-09-24 终判:降级通道废除——mode 唯一合法值 restore,显式 "design" 直接拒绝。
|
|
265
|
+
// mode 缺省按旧协议兼容(legacy 变更集无 scheme/page_types,由下方 G8b 基线命中闸兜底)。
|
|
266
|
+
const mode = changeset.mode === undefined || changeset.mode === null ? null : changeset.mode;
|
|
267
|
+
if (mode === "design") {
|
|
268
|
+
console.error(`✖ mode="design" 不是合法交付模式:还原任务的目标是把设计 100% 落地到项目——环境受限 / 覆盖不足 / 意图不明时唯一正确动作是停下向用户多轮询问确认,不得缩水交付;用户显式只要小改动时,不产出还原变更集(按普通需求直接修改)。基线三件套(oracle.mockup + coverage.registry)是还原交付的固有组成,任何 mode 形态都不得移除。`);
|
|
269
|
+
batchSummary?.invalid.push(path.basename(file));
|
|
270
|
+
if (!batchSummary)
|
|
271
|
+
process.exit(1);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (mode !== null && mode !== "restore") {
|
|
275
|
+
console.error(`✖ mode 应为 "restore"(唯一合法值),实际 "${mode}"`);
|
|
269
276
|
batchSummary?.invalid.push(path.basename(file));
|
|
270
277
|
if (!batchSummary)
|
|
271
278
|
process.exit(1);
|
|
@@ -292,8 +299,8 @@ function matchOne(file, dataset, batchSummary, opts = {}) {
|
|
|
292
299
|
if (missing.length > 0) {
|
|
293
300
|
console.error(`\n✖ G8 模式闸:mode=restore(场景还原)但缺少 ${missing.join(" 与 ")} → 拒绝执行。`);
|
|
294
301
|
console.error(` 场景还原的完备性以登记表覆盖率为分母、以 oracle 为验收基线——缺任一项即「伪还原变更集」:`);
|
|
295
|
-
console.error(`
|
|
296
|
-
console.error(`
|
|
302
|
+
console.error(` 只换主题色也能一路绿灯落地,但布局细节点位从未被声明,还原度不可证明。`);
|
|
303
|
+
console.error(` 处置:补齐后重跑——mode=design 不是合法出口;范围确需缩小时停下向用户多轮询问确认。`);
|
|
297
304
|
batchSummary?.invalid.push(path.basename(file));
|
|
298
305
|
if (!batchSummary)
|
|
299
306
|
process.exit(1);
|
|
@@ -302,6 +309,42 @@ function matchOne(file, dataset, batchSummary, opts = {}) {
|
|
|
302
309
|
console.log(` ✔ G8 模式闸:mode=restore,oracle + coverage 齐备(基线三件套完整)`);
|
|
303
310
|
}
|
|
304
311
|
|
|
312
|
+
// G8 三轮强化(2026-09-24 孔雀绿轮实证):基线命中 ⇒ 不得以缺 mode / 降级形态移除基线三件套。
|
|
313
|
+
// 事故形态:执行会话把「环境无浏览器」读成降级许可,以 6 条品牌色变更交差,
|
|
314
|
+
// notes 自述「未强制绑定基线登记表与 oracle.json」。显式 mode=design 已被上方硬拒,
|
|
315
|
+
// 本闸兜底「缺 mode 但带 scheme + page_types」的现代形态:三件套缺一即拒。
|
|
316
|
+
if (mode !== "restore") {
|
|
317
|
+
const covG8b = changeset.coverage && typeof changeset.coverage === "object" ? changeset.coverage : {};
|
|
318
|
+
const pageTypesG8b = Array.isArray(covG8b.page_types) ? covG8b.page_types : [];
|
|
319
|
+
const hasCoreG8b =
|
|
320
|
+
typeof changeset.oracle?.mockup === "string" && changeset.oracle.mockup.trim().length > 0 &&
|
|
321
|
+
typeof covG8b.registry === "string" && covG8b.registry.trim().length > 0;
|
|
322
|
+
if (pageTypesG8b.length && changeset.scheme && !hasCoreG8b) {
|
|
323
|
+
// 方案注册表(含 baseline 登记)在设计侧技能内;跨技能同机部署时按兄弟目录解析
|
|
324
|
+
const schemeRegistryG8b = path.resolve(__dirname, "..", "..", "project-ui-design", "references", "page-specs", "styles", "registry.json");
|
|
325
|
+
let baselineHitsG8b = [];
|
|
326
|
+
try {
|
|
327
|
+
const srG8b = JSON.parse(readFileSync(schemeRegistryG8b, "utf-8"));
|
|
328
|
+
for (const schG8b of srG8b.schemes || []) {
|
|
329
|
+
if (schG8b.id !== changeset.scheme) continue;
|
|
330
|
+
for (const [ptG8b, bG8b] of Object.entries(schG8b.baseline || {})) {
|
|
331
|
+
if (ptG8b.startsWith("$") || !bG8b || typeof bG8b !== "object" || !bG8b.mockup) continue;
|
|
332
|
+
if (pageTypesG8b.includes(ptG8b)) baselineHitsG8b.push(ptG8b);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
} catch { /* 设计侧技能未检出(非同机部署)时跳过——设计侧 D15 为权威闸 */ }
|
|
336
|
+
if (baselineHitsG8b.length) {
|
|
337
|
+
console.error(`\n✖ G8 模式闸(三轮强化):还原目标存在注册基线(scheme="${changeset.scheme}" × scene="${changeset.scene || "?"}" × 页型 ${baselineHitsG8b.join("/")}),却以 mode=${mode || "(缺省)"} 且缺基线三件套交付 → 拒绝执行。`);
|
|
338
|
+
console.error(` 环境受限 / 覆盖不足 / 意图不明时唯一正确动作是停下向用户多轮询问确认,基线三件套(oracle.mockup + coverage.registry)照交;`);
|
|
339
|
+
console.error(` 严禁借任何 mode 形态把还原缩水成主题色变更。`);
|
|
340
|
+
batchSummary?.invalid.push(path.basename(file));
|
|
341
|
+
if (!batchSummary)
|
|
342
|
+
process.exit(1);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
305
348
|
// G13 页面绑定闸(2026-09-22 事故机制化):效果图页面是设计角色坐标系,不是页面增量。
|
|
306
349
|
// 事故背景:效果图演示页「运单管理」被误落地为项目新菜单/新页面——效果图标题只是
|
|
307
350
|
// 设计侧为承载设计语言虚构的演示名,落地动作恒为「映射已有页面 + 在其上同步结构与样式」,
|
|
@@ -13,7 +13,7 @@ tags: [code-review, quality, checklist, lint, typescript, vue3, security, perfor
|
|
|
13
13
|
|
|
14
14
|
1. 重读本文件
|
|
15
15
|
2. 列出本轮改动路径,对照下方清单;**硬失败表命中则先改代码再继续**
|
|
16
|
-
3. 业务项目根目录必跑:`pnpm lint:fix` → `pnpm type-check`
|
|
16
|
+
3. 业务项目根目录必跑:`pnpm lint:fix` → `pnpm type-check`;**本轮产出或修改了设计变更集(`.design/changeset*.json`)时,同一条命令链追加终态闸:`node <技能目录>/scripts/apply-final-gate.mjs --project <项目根> --container-width <内容区宽>`,取得 `FINAL-GATE: PASS` 才算通过**(该命令串联 changeset-selfcheck → parser match → parser audit → design-audit 四闸,PASS 凭证写入 ledger.json;只做设计侧产出、未落项目时至少实跑 changeset-selfcheck 并附 exit 0 输出原文)。**禁止把 lint/type-check 单独当「全部质量闸门」汇报**——它们不含任何还原度检查
|
|
17
17
|
4. 失败则修代码并重跑,直至通过或写明阻塞
|
|
18
18
|
5. 最终回复追加:
|
|
19
19
|
|
|
@@ -22,7 +22,7 @@ tags: [code-review, quality, checklist, lint, typescript, vue3, security, perfor
|
|
|
22
22
|
- 闸门:已执行 code-quality(生成后)
|
|
23
23
|
- 改动文件:`path/a`、`path/b`、…
|
|
24
24
|
- 清单:选型 ✅ / 类型 ✅ / 组件 ✅ / API ✅ / 安全 ✅ / 业务 ✅ / 列表→详情 ✅或N/A / 列表布局 ✅或N/A / 风格 ✅
|
|
25
|
-
- 设计落地:通道活性 ✅ / 锚点契约 ✅ / 结构施工 ✅或N/A / 覆写提权 ✅ / 登记表覆盖率 ✅或N/A / 渲染态验收 ✅或N/A
|
|
25
|
+
- 设计落地:通道活性 ✅ / 锚点契约 ✅ / 结构施工 ✅或N/A / 覆写提权 ✅ / 登记表覆盖率 ✅或N/A(仅本轮无设计变更集时可用 N/A)/ 渲染态验收 ✅或N/A(**同左;有变更集而无 oracle = 硬失败,不得 N/A**)/ 设计闸命令链 ✅(selfcheck + match + audit 输出原文已附)或N/A(无变更集)
|
|
26
26
|
- 硬失败:无 | 已拦截并修正:…
|
|
27
27
|
- 已修正:…(无则写「无」)
|
|
28
28
|
- 命令:`pnpm lint:fix` → …;`pnpm type-check` → …
|
|
@@ -48,6 +48,7 @@ lint / type-check **过了也不能放过**下列项。发现即改代码,禁
|
|
|
48
48
|
| 列表查询区 `el-form` / 查询按钮 | 写了 `size="small"`(紧凑主题用户未要求) | 不写 size,用 default=`--control-height` |
|
|
49
49
|
| 本轮新增 addon 并引用对应 `Re*` / Vant / 微信 | 已装包但 `tsconfig exclude` 仍含该路径,或 `addon-enable` 要求的 build/src 未改 | 按 **`addon-enable.md`** 对应章节恢复后重跑 type-check |
|
|
50
50
|
| 设计层(`design-shell.scss` / 页面 `scoped`)新增规则 | 与对手**同权重**(对手为组件 `<style scoped>` 时平手即失效——组件样式在全局之后注入),或对手带 `!important` 而本条没带 | 按 `priority.strategy` 提权;**数权重,不许目测** |
|
|
51
|
+
| 本轮产出/修改了设计变更集(`.design/changeset*.json`) | ① 变更集 `mode` 非 `restore`(缺 mode / 写 design 均为硬错误——无登记表、无基线的轻量变更集落地后必然「只变主题色、布局细节零变化」);② 变更集 `coverage.page_types` 命中注册基线页型,却缺 `oracle.mockup` 或 `coverage.registry`(基线三件套是还原交付的固有组成);③ 设计闸未实跑(selfcheck exit 0 输出原文未附 / `match --registry` / `design-audit` 缺任一);④ notes 的覆盖话术与 coverage 字段矛盾(如无三件套却写「等价覆盖全量」) | 补齐三件套(oracle 值可由规范派生并在 notes 声明「未做浏览器实测」)后重跑全部设计闸;范围疑问停下向用户多轮询问确认——不存在降级出口 |
|
|
51
52
|
| 变更集带 `oracle` | ① `oracle.mockup` 不是本页型的**场景基线图**(对照注册表 `baseline.<页型>.mockup`);② 基线图色板(图内 `data-mockup-theme`)与本次主题(`changeset.theme`)不一致**且未声明** `oracle.theme_mapping`;③ `oracle.sids` 与 `changes[]` 未双向闭合(存在欠声明 / 无基线点位)。**注意:主题 ≠ 基线图色板本身不是违规**——基线图是场景基线、与主题无关,声明 `theme_mapping`(色值归一)后照常全量施工,**不得**把主题不一致当回流理由缩水成只换主题色 | 视缺口而定:缺 `theme_mapping` → 回流设计侧补声明;`sids` 未闭合 → 回流设计侧补齐后再开工,禁止直接施工 |
|
|
52
53
|
| `element-plus.scss` / `sidebar.scss` 等样式消费层 | `var(--font-size-*)` 用于 `line-height` / `height` 等非字号属性 | 查 TSB:`classifyToken` + `PROPERTY_FORBIDDEN`;`line-height` → `--line-height` 或 `--el-menu-item-height` |
|
|
53
54
|
| `element-plus.scss` 内视觉规则(shadow/hover/transition) | 出现非令牌引用的裸 hex / px / rgba 值 | 全部改为 `var()` 引用令牌 |
|
|
@@ -11,7 +11,7 @@ tags: [design-apply, changeset, semantic-id, mapping, parser, theme, layout]
|
|
|
11
11
|
|
|
12
12
|
本能力组是**双 Skill 链路的项目侧执行端**:统一获取上游设计技能产出的「语义 ID 变更集」JSON(`design.changeset/1.0`,协议技术栈中立),由本 Skill 负责翻译并落地到目标项目(vue3-web-temp 及派生项目)。上游设计技能为 **project-ui-design**,协议文档见其 `references/changeset-schema.md`。
|
|
13
13
|
|
|
14
|
-
**变更集落点唯一化(强制)**:变更集一律落在 `<落地项目>/.
|
|
14
|
+
**变更集落点唯一化(强制)**:变更集一律落在 `<落地项目>/.design/changeset-<场景>-<页型>-<主题>.json`,与交付台账 `ledger.json` 同目录;多份并存时无法判定「本轮交付物」(`design-audit.mjs` 会告警)。**落地台账**(`ledger.json`,由审计闸自动维护)记录每轮的 `covered/applicable`、设计层行数与已落地语义 ID 集合——它既是**回退检测**的依据,也是**断点续传**的入口:跨轮「继续」时按 `applicable − 已落地点数` 取剩余工作项,禁止把「继续」当成重跑一次同样的开头。
|
|
15
15
|
|
|
16
16
|
```
|
|
17
17
|
设计变更集 JSON(设计技能产出,中立协议)
|
|
@@ -137,7 +137,7 @@ parser match(确定性翻译)→ 施工坐标 → AI 按坐标改代码 →
|
|
|
137
137
|
|
|
138
138
|
## 施工标记规范(G3:@sid / @closed,design-audit E15 机检)
|
|
139
139
|
|
|
140
|
-
结构性声明(`form` / `suppress` / `suppress_inner` / `structure_ops` / `text_visible:none` / `visible:false
|
|
140
|
+
结构性声明(`form` / `suppress` / `suppress_inner` / `structure_ops` / `text_visible:none` / `visible:false`)**必须在设计层留机读标记**——声明了不等于落地了,没有标记就没有对账凭证(悬空声明例:`collapse_trigger.form=header_inline` 声明后壳层零规则,落地端静默无效):
|
|
141
141
|
|
|
142
142
|
| 标记 | 写法 | 作用 |
|
|
143
143
|
|---|---|---|
|
|
@@ -152,10 +152,10 @@ parser match(确定性翻译)→ 施工坐标 → AI 按坐标改代码 →
|
|
|
152
152
|
|
|
153
153
|
| # | 结束条件 | 判定方式 |
|
|
154
154
|
|---|---|---|
|
|
155
|
-
| 1 | **施工对账清零**:变更集全部已声明点位(`changes[]` ∪ `visible:false` ∪ `pending` 兑现项)全部落地 | 对照 `.
|
|
155
|
+
| 1 | **施工对账清零**:变更集全部已声明点位(`changes[]` ∪ `visible:false` ∪ `pending` 兑现项)全部落地 | 对照 `.design/ledger.json` 台账与 parser match 输出逐条核销;剩余 >0 → 继续施工 |
|
|
156
156
|
| 2 | **静态闸清零**:`parser audit` 与 `scripts/design-audit.mjs` 的 ERROR = 0 | 两闸任一 ERROR 即返工,不得带错收尾 |
|
|
157
157
|
| 3 | **验收闭环**:变更集含 oracle 时跑第 6 步 verify(本环境无浏览器 → 渲染态抽取显式移交用户执行,并说明移交项) | verify 全绿或移交说明已给出 |
|
|
158
|
-
| 4 | **终态机读凭证**:跑通 `node <技能>/scripts/apply-final-gate.mjs --project <项目根> --container-width <内容区宽>`,取得 `FINAL-GATE: PASS` | 该命令串联四闸(changeset-selfcheck → parser match → parser audit → design-audit),任何一步非零即 `FAIL`,会话**不得以完成态结尾**;PASS 凭证自动写入 `.
|
|
158
|
+
| 4 | **终态机读凭证**:跑通 `node <技能>/scripts/apply-final-gate.mjs --project <项目根> --container-width <内容区宽>`,取得 `FINAL-GATE: PASS` | 该命令串联四闸(changeset-selfcheck → parser match → parser audit → design-audit),任何一步非零即 `FAIL`,会话**不得以完成态结尾**;PASS 凭证自动写入 `.design/ledger.json` 的 `finalGate` 字段——终态判定以凭证为准,不以会话自述为准。**mode 唯一合法值 restore**:缺 mode / 写 design / 基线命中却缺三件套,均在此拦截 |
|
|
159
159
|
|
|
160
160
|
> 条件 4 是 1–3 的机器汇总,不替代前三条的人读核对;前 3 条满足而凭证 FAIL = 仍有静态闸未清零,按 FAIL 输出逐项修复。
|
|
161
161
|
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
## 词典维护纪律
|
|
8
8
|
|
|
9
|
-
### 出厂状态漂移(词典假前提 =
|
|
9
|
+
### 出厂状态漂移(词典假前提 = 风险源,落地前必查)
|
|
10
10
|
|
|
11
|
-
**项目可能随时被还原到出厂**(design-shell 回空壳、`.changeset/*` 消失、本体系此前补的通道全部消失)。因此词典里凡「出厂已具备 X」类表述必须满足:① X 是**真出厂能力**(对照出厂源码可验证);② 由本体系补建的通道一律写成**条件式**——「若缺失,按 structure_ops / 补通道流程补建并申报」,不写「已存在」。**落地前必须跑 `node parser/index.js audit --project <dir
|
|
11
|
+
**项目可能随时被还原到出厂**(design-shell 回空壳、`.changeset/*` 消失、本体系此前补的通道全部消失)。因此词典里凡「出厂已具备 X」类表述必须满足:① X 是**真出厂能力**(对照出厂源码可验证);② 由本体系补建的通道一律写成**条件式**——「若缺失,按 structure_ops / 补通道流程补建并申报」,不写「已存在」。**落地前必须跑 `node parser/index.js audit --project <dir>`**:锚点/选择器/变量消费者存在性静态校验,会当场暴露词典假前提(例:词典写「出厂已渲染分割线 / 工具栏行已挂语义类」,项目还原后全部失真 → 规则落空)。
|
|
12
12
|
|
|
13
13
|
- mapping/ 是唯一事实源:施工前 `parser query <id>` 精读条目,**不凭记忆施工**。
|
|
14
14
|
- **禁止编造的是「既有条目的映射」**(改既有 selector / css_vars / 变量指向 = 静默篡改,绝对禁止)。**词典缺 ID / 缺字段枚举不属编造**——那是新点位,按 L3 当场落地并写**新增的**临时映射条目,交付说明申报,等人工评审转正。
|
|
@@ -29,9 +29,9 @@ node parser/index.js match --batch <changesets-dir>
|
|
|
29
29
|
| **能力缺口**(词典缺 ID / 缺字段枚举 / 缺 form) | 设计侧意图完整、值合法 | **项目侧当场落地**:按 L3 定制布局施工并写 mapping 临时映射;出厂文件仅在「补通道」时可动,交付说明单列申报。**进清单 = 把可落地项搁置 = 交付失败** |
|
|
30
30
|
| **意图缺口**(设计侧没给值 / 值非法) | 变更集里本就没有这个值 | 才可进「缺口清单」,交付说明写明「回流设计侧补齐」——**不是问用户要不要做** |
|
|
31
31
|
|
|
32
|
-
> **闸门红 = 硬停(交付缺陷,性质与上表相反)**:`parser match` **退出码非 0**(G8 模式闸 / G9 登记表身份闸 / G10 dims 闸 / G11 oracle 基线闸 / 覆盖率细闸,任一不通过)时
|
|
32
|
+
> **闸门红 = 硬停(交付缺陷,性质与上表相反)**:`parser match` **退出码非 0**(G8 模式闸 / G9 登记表身份闸 / G10 dims 闸 / G11 oracle 基线闸 / 覆盖率细闸,任一不通过)时 **禁止开工落地**。合法出路只有一条:回设计侧补齐(基线 / 登记项 / 点位)后重跑——**`mode` 唯一合法值是 `restore`**,不存在「经用户同意降级」的出口;范围确需缩小时,停下向用户多轮询问确认真实意图,按确认结论调整变更集范围后重算。
|
|
33
33
|
>
|
|
34
|
-
> **禁止「挑拣完整的那部分先落地」**:最典型的错误是闸门报错后只落全局主题令牌(改 `platform-config.json` 的 `EpThemeColor` + 设计层覆盖 `--brand-primary
|
|
34
|
+
> **禁止「挑拣完整的那部分先落地」**:最典型的错误是闸门报错后只落全局主题令牌(改 `platform-config.json` 的 `EpThemeColor` + 设计层覆盖 `--brand-primary`)再宣称完成——用户看到的是「只换了主题色,布局样式全不对」,且重建设计层时会把上一轮成果一并覆盖。上表「禁止中途停顿」**只适用于能力缺口**(词典没建 ID / 字段没枚举),**不适用于闸门红**。
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
`缺口清单` 只承载第二类,且必须给出点位 ID + 缺失字段名。
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
- 设计未声明该点位(效果图上没有这个位置的操作)→ 收口隐藏(`visible:false` 或隐藏通道),不显示演示文字;
|
|
23
23
|
- **判据一句话:还原后的页面不得残留模板的演示说明性文字与占位按钮——它们既不是设计效果,也不是交付目标。** 残留 = 把演示噪音当成了还原结果,即「效果 vs 内容」混淆的实景表现。
|
|
24
24
|
|
|
25
|
-
**结构/样式图层分工**:`form`(高度模式、形态枚举)是**结构意图**,必须落到 DOM 属性/组件开关/配置字段(如 `data-content-mode`、`ShowModel`);**禁止把结构意图降级翻译成 SCSS 变量写进样式文件**(form=scroll 只写 `--vts-margin` =
|
|
25
|
+
**结构/样式图层分工**:`form`(高度模式、形态枚举)是**结构意图**,必须落到 DOM 属性/组件开关/配置字段(如 `data-content-mode`、`ShowModel`);**禁止把结构意图降级翻译成 SCSS 变量写进样式文件**(form=scroll 只写 `--vts-margin` = 未施工)。样式文件(如 design-shell.scss)只承载 css_vars/custom_style_map 翻译产物;壳层通用文件禁止代打页面级结构属性(lay-content 硬编码 `data-content-mode="scroll"` 会压掉 `:has()` 自动定高探测,useTableSearch 高度链断裂致列表区整体崩坏,禁止此类写法)。
|
|
26
26
|
|
|
27
27
|
**结构链不可侵(高度链保命红线)**:内容区高度链是**结构层**,已与视觉解耦,两侧各有边界——
|
|
28
28
|
|
|
@@ -49,9 +49,9 @@ html body .sidebar-container { /* 令牌宿主 = 该令牌被消费的
|
|
|
49
49
|
|
|
50
50
|
- **本文件是产物,不是手改对象(流程红线)**:`design-shell.scss` 的每一行都必须由「变更集 + 词典」经本协议产出——发现缺规则时,正确动作是**补词典字段 / 前置条件 / 登记项**让下一次 design-apply 自动生成,**禁止绕过流程直接手写项目文件**。仅「补通道」(变量消费者 / form 枚举 / 组件中性默认)允许动出厂源码并单列申报。
|
|
51
51
|
|
|
52
|
-
- **累积性红线(禁止整文件重写)**:本文件的**驱动源是变更集,不是词典**——「历史规则已落词典,重生成后会自动带出」**只在「本轮变更集交齐全部适用点位」时成立**。若上游只交了部分点位(`coverage.covered < applicable`、`gaps`
|
|
52
|
+
- **累积性红线(禁止整文件重写)**:本文件的**驱动源是变更集,不是词典**——「历史规则已落词典,重生成后会自动带出」**只在「本轮变更集交齐全部适用点位」时成立**。若上游只交了部分点位(`coverage.covered < applicable`、`gaps` 非空),按「出厂空文件」重建会把上一轮已落地的规则**整段覆盖**,用户侧表现为「布局样式全不对,只剩主题色」(例:620 行 → 77 行,同时上游 covered=31/186)。因此:
|
|
53
53
|
- **重建即合并**:本轮变更集未覆盖的既有段落**原样保留**,只增删本轮涉及的段;自举(文件缺失 → 新建)后不得把既有内容当作「历史包袱」丢弃;
|
|
54
|
-
- **落地前后各记一次状态**(设计层行数 + 已落地语义 ID 集合):`node scripts/design-audit.mjs --project <落地项目>` 的 E12 会自动写 `.
|
|
54
|
+
- **落地前后各记一次状态**(设计层行数 + 已落地语义 ID 集合):`node scripts/design-audit.mjs --project <落地项目>` 的 E12 会自动写 `.design/ledger.json` 台账并与历史峰值比对,**行数或点位骤降 = 交付回退**,必须停下并向上游回流,不得继续宣称完成;
|
|
55
55
|
- 上游交付不完整时,正确动作是**拒绝落地 + 回流设计侧补齐**,**不是「挑完整的那部分先落」**(只落全局主题令牌 = 假交付)。
|
|
56
56
|
|
|
57
57
|
- 每条规则带来源标注(`/* <语义 ID> · <变更条目> */`),换设计时整段可替换;
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
- E9 形态收口缺件 → `icon_only` 只落了「显图标」而没有**在同一作用域内**收口文案(作用域外的兄弟变体收口不算成对)
|
|
51
51
|
- E10 scoped 挂空 → 组件 scoped 规则依赖运行时并不存在的类(结构重构后残留,永不命中)
|
|
52
52
|
- E11 **交付完整性** → `mode=restore` 时 `covered < applicable`(欠声明)/ `gaps` 非空 / 缺口未在 `pending[]` 台账化(下游「继续」无工作项可执行)/ 自报 `registrable` 与登记表实物不符 / `notes` 的成功话术与 `coverage` 字段互相矛盾。**该项是「只换了主题色」的第一现场**:上游残缺口 + 下游挑拣落地,两头都能被它抓住
|
|
53
|
-
- E12 **设计层累积性** → 设计层文件行数或变更集点位相比历史峰值骤降(上一轮成果被重建覆盖)。本项会写 `.
|
|
53
|
+
- E12 **设计层累积性** → 设计层文件行数或变更集点位相比历史峰值骤降(上一轮成果被重建覆盖)。本项会写 `.design/ledger.json` 台账(`--no-ledger` 可关;`--changeset <path>` / `--design-layer <path>` 可指定),**台账同时是「继续」的断点续传依据**:`applicable − 已落地点数` 即剩余工作项,不再依赖自然语言回忆
|
|
54
54
|
- E13 **固定尺寸内容度量** → 设计层出现「固定 `width` 小于内容需求、且未声明收口兜底」的规则(典型:页签位 40px 里还留着标题、图标位里塞着文案)。固定宽装不下内容时文字会换行、被压缩、甚至字符重叠渲染——**「设计说 40px 就固定 40px」必然复现**。判据:盒内还有文案却用 exact 宽度又无收口
|
|
55
55
|
- E14 **列宽预算链自洽** → 列宽(含公式推出的值)扣掉**实际生效的 td 水平内边距**后装不下该列内容(操作列 = 平铺槽数 × 槽宽);或同一列宽存在两个来源且值不一致(列定义死值 vs 模板公式)。**这是「按钮文字换行 / 重叠」的第一现场**
|
|
56
56
|
- E16 **引用完整性** → 变更集 `oracle.mockup` / `coverage.registry` 指向的实物不存在(标识改名后未同步引用,全链路静默降级为无基线还原);`mode=restore` 缺顶层 `theme` 字段
|
|
@@ -45,7 +45,7 @@ if (!PROJECT || !fs.existsSync(PROJECT)) {
|
|
|
45
45
|
const CONTAINER_WIDTH = argValue("--container-width", "1200");
|
|
46
46
|
|
|
47
47
|
// ── 变更集发现(与 design-audit 同口径:多目录 + 显式 --changeset 优先)──
|
|
48
|
-
const DESIGN_DIRS = [".
|
|
48
|
+
const DESIGN_DIRS = [".design", "design", ".changeset"];
|
|
49
49
|
let changeset = argValue("--changeset") ? path.resolve(argValue("--changeset")) : null;
|
|
50
50
|
if (!changeset) {
|
|
51
51
|
const found = [];
|
|
@@ -63,7 +63,7 @@ if (!changeset) {
|
|
|
63
63
|
}
|
|
64
64
|
if (!changeset || !fs.existsSync(changeset)) {
|
|
65
65
|
console.error(`FINAL-GATE: FAIL(无变更集)`);
|
|
66
|
-
console.error(` ${PROJECT} 下未发现变更集(查找 .
|
|
66
|
+
console.error(` ${PROJECT} 下未发现变更集(查找 .design、design、.changeset)——没有交付物就没有终态。`);
|
|
67
67
|
process.exit(1);
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -138,7 +138,7 @@ for (const s of steps) {
|
|
|
138
138
|
const status = allOk ? "PASS" : "FAIL";
|
|
139
139
|
console.log(`\nFINAL-GATE: ${status}`);
|
|
140
140
|
if (allOk) {
|
|
141
|
-
console.log(` 四闸清零,交付完成。凭证已写入 .
|
|
141
|
+
console.log(` 四闸清零,交付完成。凭证已写入 .design/ledger.json(finalGate)。`);
|
|
142
142
|
} else {
|
|
143
143
|
const firstBad = steps.find((s) => !s.ok);
|
|
144
144
|
console.log(` 终态未达成(首个未清零闸:${firstBad?.name || "?"})——会话不得以完成态结尾,须继续修复后复跑。`);
|
|
@@ -146,7 +146,7 @@ if (allOk) {
|
|
|
146
146
|
|
|
147
147
|
// 凭证写入 ledger.json(不改动 design-audit 维护的轮次历史,只追加 finalGate 字段)
|
|
148
148
|
try {
|
|
149
|
-
const ledgerPath = path.join(PROJECT, ".
|
|
149
|
+
const ledgerPath = path.join(PROJECT, ".design", "ledger.json");
|
|
150
150
|
let ledger = {};
|
|
151
151
|
if (fs.existsSync(ledgerPath)) {
|
|
152
152
|
try {
|
package/scripts/check.mjs
CHANGED
|
@@ -23,10 +23,13 @@ const PYTHON_ARGS = process.platform === "win32" && !process.env.VUE3_SKILL_PYTH
|
|
|
23
23
|
/** 设计层闭环审计的目标项目:默认当前目录,可用 VUE3_AUDIT_PROJECT 指向落地项目 */
|
|
24
24
|
function resolveAuditProject() {
|
|
25
25
|
const dir = process.env.VUE3_AUDIT_PROJECT || process.cwd();
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
for (const d of [".design", "design", ".changeset"]) {
|
|
27
|
+
const designDir = path.join(dir, d);
|
|
28
|
+
if (!fs.existsSync(designDir)) continue;
|
|
29
|
+
const has = fs.readdirSync(designDir).some((f) => /^changeset.*\.json$/i.test(f));
|
|
30
|
+
if (has) return dir;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
const checks = [
|
|
@@ -45,7 +48,7 @@ const checks = [
|
|
|
45
48
|
desc: "变量悬空/层错位/层叠胜负/锚点挂空/列宽预算/收口断言(无落地项目则跳过)",
|
|
46
49
|
cmd: () => {
|
|
47
50
|
const project = resolveAuditProject();
|
|
48
|
-
if (!project) return "SKIPPED 未发现落地项目(.
|
|
51
|
+
if (!project) return "SKIPPED 未发现落地项目(.design/changeset*.json,亦兼容 design/ 与 .changeset/);用 VUE3_AUDIT_PROJECT=<落地项目> 指定后复跑";
|
|
49
52
|
const args = ["--project", project];
|
|
50
53
|
if (process.env.VUE3_AUDIT_CONTAINER_WIDTH) {
|
|
51
54
|
args.push("--container-width", process.env.VUE3_AUDIT_CONTAINER_WIDTH);
|
|
@@ -96,6 +99,36 @@ const checks = [
|
|
|
96
99
|
return ` ${lines.join(";")}`;
|
|
97
100
|
},
|
|
98
101
|
},
|
|
102
|
+
{
|
|
103
|
+
name: "模式闸唯一值回归",
|
|
104
|
+
desc: "mode 唯一合法值 restore:显式 design 的变更集必须被 parser match 拒绝",
|
|
105
|
+
cmd: () => {
|
|
106
|
+
const cs = path.join(
|
|
107
|
+
ROOT,
|
|
108
|
+
"tests/fixtures/pattern-g15-negative/.design/changeset-g15-negative.json",
|
|
109
|
+
);
|
|
110
|
+
if (!fs.existsSync(cs)) return "SKIPPED 未发现模式闸夹具(仅源码检出时可用)";
|
|
111
|
+
let rejected = false;
|
|
112
|
+
let out = "";
|
|
113
|
+
try {
|
|
114
|
+
out = run(NODE, "parser/index.js", ["match", cs]);
|
|
115
|
+
} catch (e) {
|
|
116
|
+
rejected = true;
|
|
117
|
+
out = `${e.stdout || ""}${e.stderr || ""}`;
|
|
118
|
+
}
|
|
119
|
+
if (!rejected) {
|
|
120
|
+
throw Object.assign(new Error("模式闸负例未被拒绝"), {
|
|
121
|
+
stdout: " ✗ pattern-g15-negative 竟然通过 —— mode 唯一合法值 restore 的判据被削弱,必须修复\n",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (!/基线三件套/.test(out)) {
|
|
125
|
+
throw Object.assign(new Error("模式闸负例未命中预期判据"), {
|
|
126
|
+
stdout: ` ✗ pattern-g15-negative 被拒绝,但未命中「基线三件套」判据 —— 断言已失效\n${out.slice(-800)}\n`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return " negative 被拒绝并命中「基线三件套」判据(mode 唯一合法值 restore)";
|
|
130
|
+
},
|
|
131
|
+
},
|
|
99
132
|
];
|
|
100
133
|
|
|
101
134
|
function run(runner, script, args = []) {
|
package/scripts/design-audit.mjs
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* E9 形态收口缺件 form=icon_only 只显图标、未在同一作用域内收口文案(且不被兄弟变体规则骗过)
|
|
29
29
|
* E10 scoped 挂空 组件 scoped 规则依赖运行时并不存在的类(重构残留,永不命中)
|
|
30
30
|
* E11 交付完整性 mode=restore 时 covered<applicable / gaps 非空 / 缺口未台账化 / 分母漂移 / notes 与字段矛盾
|
|
31
|
-
* E12 设计层累积性 变更集点位或设计层行数相对历史骤降(上一轮成果被覆盖)→ 写交付台账 .
|
|
31
|
+
* E12 设计层累积性 变更集点位或设计层行数相对历史骤降(上一轮成果被覆盖)→ 写交付台账 .design/ledger.json
|
|
32
32
|
* E13 固定尺寸内容度量 固定 width 小于内容需求且无收口兜底 → 文字换行/压缩/重叠
|
|
33
33
|
* E14 列宽预算链自洽 列宽(含公式)扣掉实际 td 内边距后装不下内容 → 操作列按钮被压
|
|
34
34
|
* E16 引用完整性 变更集引用的实物不存在(改名未同步)/ restore 缺顶层 theme
|
|
@@ -255,9 +255,9 @@ const designRules = DESIGN_FILES.flatMap((f) =>
|
|
|
255
255
|
const PRIMARY_LAYER = path.resolve(PROJECT, argValue("--design-layer", "src/styles/design-shell.scss"));
|
|
256
256
|
const isPrimary = (file) => path.resolve(file) === PRIMARY_LAYER;
|
|
257
257
|
|
|
258
|
-
//
|
|
259
|
-
// 硬编码单一目录的代价是「变更集搬家 →
|
|
260
|
-
const CHANGESET_DIRS = [".
|
|
258
|
+
// 变更集发现:规范落点 .design/,兼容 design/ 与 .changeset/。
|
|
259
|
+
// 硬编码单一目录的代价是「变更集搬家 → 闸门静默跳过」。
|
|
260
|
+
const CHANGESET_DIRS = [".design", "design", ".changeset"];
|
|
261
261
|
const relp = (p) => path.relative(PROJECT, p).replace(/\\/g, "/");
|
|
262
262
|
const changesetCandidates = [];
|
|
263
263
|
{
|
|
@@ -282,7 +282,7 @@ let changesetFile = null;
|
|
|
282
282
|
if (changesetCandidates.length > 1) {
|
|
283
283
|
warn(
|
|
284
284
|
"E11",
|
|
285
|
-
`多份变更集并存:${changesetCandidates.map(relp).join("、")} —— 无法判定哪份是「本轮」交付物,暂取最近修改的一份。落点应唯一为 .
|
|
285
|
+
`多份变更集并存:${changesetCandidates.map(relp).join("、")} —— 无法判定哪份是「本轮」交付物,暂取最近修改的一份。落点应唯一为 .design/ 并在交付台账登记。`,
|
|
286
286
|
);
|
|
287
287
|
}
|
|
288
288
|
if (changesetCandidates.length) {
|
|
@@ -681,7 +681,7 @@ function matchArrayLiteral(text, anchorRe) {
|
|
|
681
681
|
// ── E6 visible:false 收口断言 ──────────────────────────────
|
|
682
682
|
(function checkE6() {
|
|
683
683
|
if (!changeset) {
|
|
684
|
-
info("E6", "未发现变更集(.
|
|
684
|
+
info("E6", "未发现变更集(.design/changeset*.json),跳过收口断言");
|
|
685
685
|
return;
|
|
686
686
|
}
|
|
687
687
|
const changes = changeset.changes || [];
|
|
@@ -957,7 +957,7 @@ function visibleClosureRule(change) {
|
|
|
957
957
|
: "";
|
|
958
958
|
err(
|
|
959
959
|
"E15",
|
|
960
|
-
`${change.id} 要求收口通道「${ch}」,但设计层无任何兑现证据 —— 需要 /* @closed: ${ch} */ 标记${varHint}。声明了不收口 =
|
|
960
|
+
`${change.id} 要求收口通道「${ch}」,但设计层无任何兑现证据 —— 需要 /* @closed: ${ch} */ 标记${varHint}。声明了不收口 = 出厂旧装饰残留(双竖条并存类症状)。`,
|
|
961
961
|
);
|
|
962
962
|
}
|
|
963
963
|
}
|
|
@@ -986,7 +986,7 @@ function visibleClosureRule(change) {
|
|
|
986
986
|
} else {
|
|
987
987
|
err(
|
|
988
988
|
"E15",
|
|
989
|
-
`${change.id} 的结构性声明在设计层零落点证据(无 @sid 标记、无 @closed 兑现标记、无收口规则)——
|
|
989
|
+
`${change.id} 的结构性声明在设计层零落点证据(无 @sid 标记、无 @closed 兑现标记、无收口规则)—— 壳层零规则,声明悬空(例:collapse_trigger.form=header_inline 声明后壳层无任何规则)。每条结构性声明必须在设计层留机读标记:/* @sid: <语义ID> */。`,
|
|
990
990
|
);
|
|
991
991
|
}
|
|
992
992
|
}
|
|
@@ -1042,7 +1042,7 @@ function visibleClosureRule(change) {
|
|
|
1042
1042
|
);
|
|
1043
1043
|
return;
|
|
1044
1044
|
}
|
|
1045
|
-
const mode = changeset.mode
|
|
1045
|
+
const mode = changeset.mode;
|
|
1046
1046
|
const cov = changeset.coverage;
|
|
1047
1047
|
const gaps = (cov && cov.gaps) || [];
|
|
1048
1048
|
const pending = changeset.pending || [];
|
|
@@ -1050,7 +1050,11 @@ function visibleClosureRule(change) {
|
|
|
1050
1050
|
const notesText = JSON.stringify(changeset.notes || []);
|
|
1051
1051
|
|
|
1052
1052
|
if (mode !== "restore") {
|
|
1053
|
-
|
|
1053
|
+
err(
|
|
1054
|
+
"E11",
|
|
1055
|
+
`mode 必须为 "restore"(实为 ${JSON.stringify(changeset.mode ?? null)}):` +
|
|
1056
|
+
`还原任务的目标是把设计 100% 落地到项目,环境受限 / 覆盖不足 / 意图不明时唯一正确动作是停下向用户多轮询问确认,不得缩水交付。`,
|
|
1057
|
+
);
|
|
1054
1058
|
return;
|
|
1055
1059
|
}
|
|
1056
1060
|
if (!cov) {
|
|
@@ -1072,7 +1076,7 @@ function visibleClosureRule(change) {
|
|
|
1072
1076
|
if (!pending.length) {
|
|
1073
1077
|
err(
|
|
1074
1078
|
"E11",
|
|
1075
|
-
`缺口未台账化:gaps 有 ${gaps.length} 条理由,但 pending[] 为空 ——
|
|
1079
|
+
`缺口未台账化:gaps 有 ${gaps.length} 条理由,但 pending[] 为空 —— 缺口没有变成可执行工作项,所以「继续」时无任何东西可做,复跑也不可能收敛(症状:多轮继续后效果不变)。`,
|
|
1076
1080
|
);
|
|
1077
1081
|
} else {
|
|
1078
1082
|
const declared = new Set(changes.map((c) => c.id));
|
|
@@ -1094,7 +1098,7 @@ function visibleClosureRule(change) {
|
|
|
1094
1098
|
const reg = JSON.parse(fs.readFileSync(real, "utf-8"));
|
|
1095
1099
|
const items = reg.items || reg.registry || [];
|
|
1096
1100
|
if (Array.isArray(items) && items.length && items.length !== R) {
|
|
1097
|
-
err("E11", `登记表分母漂移:coverage.registrable=${R},登记表实物为 ${items.length} 条 ——
|
|
1101
|
+
err("E11", `登记表分母漂移:coverage.registrable=${R},登记表实物为 ${items.length} 条 —— 自报数与实物不一致。分母错则该报的欠声明数也不可信。`);
|
|
1098
1102
|
} else if (Array.isArray(items) && items.length) {
|
|
1099
1103
|
info("E11", `登记表分母对账:registrable=${R} = 实物 ${items.length} 条 ✓`);
|
|
1100
1104
|
}
|
|
@@ -1121,7 +1125,7 @@ function visibleClosureRule(change) {
|
|
|
1121
1125
|
// ── E12 设计层累积性(跨轮回退检测 + 交付台账) ──────────────
|
|
1122
1126
|
// 「继续一次处理不完、继续了也没变化」的根治:把每轮落地状态落成机器可读台账,
|
|
1123
1127
|
// 使「本轮是否把上一轮成果覆盖掉」可被断言,而不是靠人记得。
|
|
1124
|
-
const LEDGER_PATH = path.join(PROJECT, ".
|
|
1128
|
+
const LEDGER_PATH = path.join(PROJECT, ".design", "ledger.json");
|
|
1125
1129
|
const WRITE_LEDGER = !process.argv.includes("--no-ledger");
|
|
1126
1130
|
(function checkE12() {
|
|
1127
1131
|
const primaryText = srcTextByFile.get(PRIMARY_LAYER) ?? readIf(PRIMARY_LAYER) ?? "";
|
|
@@ -1131,7 +1135,7 @@ const WRITE_LEDGER = !process.argv.includes("--no-ledger");
|
|
|
1131
1135
|
const cur = {
|
|
1132
1136
|
at: new Date().toISOString(),
|
|
1133
1137
|
changeset: changesetFile ? relp(changesetFile) : null,
|
|
1134
|
-
mode: changeset ? changeset.mode
|
|
1138
|
+
mode: changeset ? changeset.mode ?? null : null,
|
|
1135
1139
|
covered: cov ? cov.covered : null,
|
|
1136
1140
|
applicable: cov ? cov.applicable : null,
|
|
1137
1141
|
design_shell_lines: primaryLines,
|
|
@@ -1157,7 +1161,7 @@ const WRITE_LEDGER = !process.argv.includes("--no-ledger");
|
|
|
1157
1161
|
if (peakLines && primaryLines && primaryLines < peakLines * 0.6) {
|
|
1158
1162
|
err(
|
|
1159
1163
|
"E12",
|
|
1160
|
-
`交付回退:设计层 ${argValue("--design-layer", "src/styles/design-shell.scss")} 行数 ${peakLines} → ${primaryLines}(骤降 >40%)——
|
|
1164
|
+
`交付回退:设计层 ${argValue("--design-layer", "src/styles/design-shell.scss")} 行数 ${peakLines} → ${primaryLines}(骤降 >40%)—— 上一轮已落地的规则被覆盖/丢失。设计层是累积产物,禁止按「出厂空文件」重建;重建必须合并既有规则(否则用户侧表现为「布局样式全不对,只剩主题色」)。`,
|
|
1161
1165
|
);
|
|
1162
1166
|
}
|
|
1163
1167
|
const priorSids = new Set(priorRounds.flatMap((r) => r.applied_sids || []));
|
|
@@ -1398,7 +1402,7 @@ const MIN_ANY_BOX = 24;
|
|
|
1398
1402
|
}
|
|
1399
1403
|
// restore 模式的顶层 theme 字段:缺失会让「本次主题」回落到 global.brand_primary 反推,
|
|
1400
1404
|
// 重新引入「换主题 = 换读取路径」的分叉
|
|
1401
|
-
if (
|
|
1405
|
+
if (changeset.mode === "restore" && !changeset.theme) {
|
|
1402
1406
|
warn(
|
|
1403
1407
|
"E16",
|
|
1404
1408
|
"变更集 mode=restore 但缺顶层 `theme` 字段 —— 本次主题只能回落按 `global.brand_primary` 反推(旧口径)。补 `theme` 可让「本次主题」与「基线图色板」两个身份各自可机读、互不推断。",
|