@qilitt-mickey/vue3-temp-skill 1.1.65 → 1.1.66
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/package.json
CHANGED
|
@@ -11,6 +11,8 @@ 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
|
+
**变更集落点唯一化(强制)**:变更集一律落在 `<落地项目>/.codebuddy/design/changeset-<场景>-<页型>-<主题>.json`,与交付台账 `ledger.json` 同目录;多份并存时无法判定「本轮交付物」(`design-audit.mjs` 会告警)。**落地台账**(`ledger.json`,由审计闸自动维护)记录每轮的 `covered/applicable`、设计层行数与已落地语义 ID 集合——它既是**回退检测**的依据,也是**断点续传**的入口:跨轮「继续」时按 `applicable − 已落地点数` 取剩余工作项,禁止把「继续」当成重跑一次同样的开头。
|
|
15
|
+
|
|
14
16
|
```
|
|
15
17
|
设计变更集 JSON(设计技能产出,中立协议)
|
|
16
18
|
↓ 项目侧统一获取(设计技能不感知下游)
|
|
@@ -29,6 +29,11 @@ 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 基线闸 / 覆盖率细闸,任一不通过)时 **禁止开工落地**。合法出路只有两条:① 回设计侧补齐(基线 / 登记项 / 点位)后重跑;② 经用户**显式同意**后按 `mode=design` 降级,并在交付说明写出「本次实际覆盖范围清单 + 未覆盖清单」。
|
|
33
|
+
>
|
|
34
|
+
> **禁止「挑拣完整的那部分先落地」**:最典型的错误是闸门报错后只落全局主题令牌(改 `platform-config.json` 的 `EpThemeColor` + 设计层覆盖 `--brand-primary`)再宣称完成——用户看到的是「只换了主题色,布局样式全不对」,且重建设计层时把上一轮成果一并覆盖(2026-09-23 橘橙跑三闸红 → 只落主题色 → 620 行设计层被覆盖为 77 行)。上表「禁止中途停顿」**只适用于能力缺口**(词典没建 ID / 字段没枚举),**不适用于闸门红**。
|
|
35
|
+
|
|
36
|
+
|
|
32
37
|
`缺口清单` 只承载第二类,且必须给出点位 ID + 缺失字段名。
|
|
33
38
|
|
|
34
39
|
**转换计划新增四类输出()**:每条 L1/L2 点位除样式指令外,还会输出——
|
|
@@ -47,7 +47,12 @@ html body .sidebar-container { /* 令牌宿主 = 该令牌被消费的
|
|
|
47
47
|
|
|
48
48
|
写入纪律:
|
|
49
49
|
|
|
50
|
-
- **本文件是产物,不是手改对象(流程红线)**:`design-shell.scss` 的每一行都必须由「变更集 + 词典」经本协议产出——发现缺规则时,正确动作是**补词典字段 / 前置条件 / 登记项**让下一次 design-apply
|
|
50
|
+
- **本文件是产物,不是手改对象(流程红线)**:`design-shell.scss` 的每一行都必须由「变更集 + 词典」经本协议产出——发现缺规则时,正确动作是**补词典字段 / 前置条件 / 登记项**让下一次 design-apply 自动生成,**禁止绕过流程直接手写项目文件**。仅「补通道」(变量消费者 / form 枚举 / 组件中性默认)允许动出厂源码并单列申报。
|
|
51
|
+
|
|
52
|
+
- **累积性红线(禁止整文件重写)**:本文件的**驱动源是变更集,不是词典**——「历史规则已落词典,重生成后会自动带出」**只在「本轮变更集交齐全部适用点位」时成立**。若上游只交了部分点位(`coverage.covered < applicable`、`gaps` 非空),按「出厂空文件」重建会把上一轮已落地的规则**整段覆盖**,用户侧表现为「布局样式全不对,只剩主题色」(2026-09-23 实证:620 行 → 77 行,同时上游 covered=31/186)。因此:
|
|
53
|
+
- **重建即合并**:本轮变更集未覆盖的既有段落**原样保留**,只增删本轮涉及的段;自举(文件缺失 → 新建)后不得把既有内容当作「历史包袱」丢弃;
|
|
54
|
+
- **落地前后各记一次状态**(设计层行数 + 已落地语义 ID 集合):`node scripts/design-audit.mjs --project <落地项目>` 的 E12 会自动写 `.codebuddy/design/ledger.json` 台账并与历史峰值比对,**行数或点位骤降 = 交付回退**,必须停下并向上游回流,不得继续宣称完成;
|
|
55
|
+
- 上游交付不完整时,正确动作是**拒绝落地 + 回流设计侧补齐**,**不是「挑完整的那部分先落」**(只落全局主题令牌 = 假交付)。
|
|
51
56
|
|
|
52
57
|
- 每条规则带来源标注(`/* <语义 ID> · <变更条目> */`),换设计时整段可替换;
|
|
53
58
|
- 本文件内新增的 CSS 变量必须**定义 + 引用成对出现**(出厂已有消费者也算),避免死通道;
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
- E8 浮层残留 → 声明收口下拉浮层的 form,组件却仍保留 `ElDropdown` / `#dropdown` 消费(teleport 浮层样式层收不掉,必须结构收口)
|
|
49
49
|
- E9 形态收口缺件 → `icon_only` 只落了「显图标」而没有**在同一作用域内**收口文案(作用域外的兄弟变体收口不算成对)
|
|
50
50
|
- E10 scoped 挂空 → 组件 scoped 规则依赖运行时并不存在的类(结构重构后残留,永不命中)
|
|
51
|
+
- E11 **交付完整性** → `mode=restore` 时 `covered < applicable`(欠声明)/ `gaps` 非空 / 缺口未在 `pending[]` 台账化(下游「继续」无工作项可执行)/ 自报 `registrable` 与登记表实物不符 / `notes` 的成功话术与 `coverage` 字段互相矛盾。**该项是「只换了主题色」的第一现场**:上游残缺口 + 下游挑拣落地,两头都能被它抓住
|
|
52
|
+
- E12 **设计层累积性** → 设计层文件行数或变更集点位相比历史峰值骤降(上一轮成果被重建覆盖)。本项会写 `.codebuddy/design/ledger.json` 台账(`--no-ledger` 可关;`--changeset <path>` / `--design-layer <path>` 可指定),**台账同时是「继续」的断点续传依据**:`applicable − 已落地点数` 即剩余工作项,不再依赖自然语言回忆
|
|
51
53
|
- [ ] **列宽由表头文案反推(E5 落地口径)**:每个业务列(含选择列、序号列)都要显式声明 `width` 或 `min-width`,取值 ≥ 该列表头文案宽度 + 左右各 16px 内边距(CJK 按 1em、ASCII 按 0.6em 估)。无宽列只能拿出厂兜底宽度参与布局,会被相邻固定宽列挤压——**表头换行与省略号截断的唯一根因就在这里**,不接受「跑起来看着还行」
|
|
52
54
|
|
|
53
55
|
- [ ] **缺口清单只含意图缺口**:清单每一项都必须是「设计侧未给值 / 值非法」且已注明「回流设计侧补齐」;能力缺口(词典缺 ID / 缺字段枚举 / 缺 form)不得出现在清单里——出现即把可落地项搁置
|
package/scripts/check.mjs
CHANGED
|
@@ -45,7 +45,7 @@ const checks = [
|
|
|
45
45
|
desc: "变量悬空/层错位/层叠胜负/锚点挂空/列宽预算/收口断言(无落地项目则跳过)",
|
|
46
46
|
cmd: () => {
|
|
47
47
|
const project = resolveAuditProject();
|
|
48
|
-
if (!project) return "SKIPPED 未发现落地项目(.codebuddy/design/changeset*.json
|
|
48
|
+
if (!project) return "SKIPPED 未发现落地项目(.codebuddy/design/changeset*.json,亦兼容 design/ 与 .changeset/);用 VUE3_AUDIT_PROJECT=<落地项目> 指定后复跑";
|
|
49
49
|
const args = ["--project", project];
|
|
50
50
|
if (process.env.VUE3_AUDIT_CONTAINER_WIDTH) {
|
|
51
51
|
args.push("--container-width", process.env.VUE3_AUDIT_CONTAINER_WIDTH);
|
package/scripts/design-audit.mjs
CHANGED
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
* E8 浮层残留 form 要求收口下拉浮层,但组件仍消费 #dropdown
|
|
28
28
|
* E9 形态收口缺件 form=icon_only 只显图标、未在同一作用域内收口文案(且不被兄弟变体规则骗过)
|
|
29
29
|
* E10 scoped 挂空 组件 scoped 规则依赖运行时并不存在的类(重构残留,永不命中)
|
|
30
|
+
* E11 交付完整性 mode=restore 时 covered<applicable / gaps 非空 / 缺口未台账化 / 分母漂移 / notes 与字段矛盾
|
|
31
|
+
* E12 设计层累积性 变更集点位或设计层行数相对历史骤降(上一轮成果被覆盖)→ 写交付台账 .codebuddy/design/ledger.json
|
|
30
32
|
*/
|
|
31
33
|
|
|
32
34
|
import fs from "fs";
|
|
@@ -249,19 +251,42 @@ const designRules = DESIGN_FILES.flatMap((f) =>
|
|
|
249
251
|
const PRIMARY_LAYER = path.resolve(PROJECT, argValue("--design-layer", "src/styles/design-shell.scss"));
|
|
250
252
|
const isPrimary = (file) => path.resolve(file) === PRIMARY_LAYER;
|
|
251
253
|
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
+
// 变更集发现:落点历史上不唯一(.codebuddy/design 与项目根 design/ 都出现过)。
|
|
255
|
+
// 硬编码单一目录的代价是「变更集搬家 → 闸门静默跳过」(2026-09-23 实证:E6/E8/E9 全跳过而无人察觉)。
|
|
256
|
+
const CHANGESET_DIRS = [".codebuddy/design", "design", ".changeset"];
|
|
257
|
+
const relp = (p) => path.relative(PROJECT, p).replace(/\\/g, "/");
|
|
258
|
+
const changesetCandidates = [];
|
|
259
|
+
{
|
|
260
|
+
const explicit = argValue("--changeset", "");
|
|
261
|
+
if (explicit) {
|
|
262
|
+
const p = path.resolve(PROJECT, explicit);
|
|
263
|
+
if (fs.existsSync(p)) changesetCandidates.push(p);
|
|
264
|
+
else err("E0", `--changeset 指定的变更集不存在:${p}`);
|
|
265
|
+
} else {
|
|
266
|
+
for (const d of CHANGESET_DIRS) {
|
|
267
|
+
const dir = path.join(PROJECT, d);
|
|
268
|
+
if (!fs.existsSync(dir)) continue;
|
|
269
|
+
for (const f of fs.readdirSync(dir).filter((x) => /changeset.*\.json$/i.test(x)).sort()) {
|
|
270
|
+
const full = path.join(dir, f);
|
|
271
|
+
if (!changesetCandidates.includes(full)) changesetCandidates.push(full);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
254
276
|
let changeset = null;
|
|
255
277
|
let changesetFile = null;
|
|
256
|
-
if (
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
278
|
+
if (changesetCandidates.length > 1) {
|
|
279
|
+
warn(
|
|
280
|
+
"E11",
|
|
281
|
+
`多份变更集并存:${changesetCandidates.map(relp).join("、")} —— 无法判定哪份是「本轮」交付物,暂取最近修改的一份。落点应唯一为 .codebuddy/design/ 并在交付台账登记。`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
if (changesetCandidates.length) {
|
|
285
|
+
changesetFile = changesetCandidates.map((p) => ({ p, t: fs.statSync(p).mtimeMs })).sort((a, b) => b.t - a.t)[0].p;
|
|
286
|
+
try {
|
|
287
|
+
changeset = JSON.parse(fs.readFileSync(changesetFile, "utf-8"));
|
|
288
|
+
} catch (e) {
|
|
289
|
+
err("E0", `变更集解析失败:${changesetFile} — ${e.message}`);
|
|
265
290
|
}
|
|
266
291
|
}
|
|
267
292
|
|
|
@@ -792,6 +817,156 @@ const lastClassOutsideNot = (sel) => {
|
|
|
792
817
|
})();
|
|
793
818
|
|
|
794
819
|
// ── 输出 ────────────────────────────────────────────────────
|
|
820
|
+
// ── E11 交付完整性 ──────────────────────────────────────────
|
|
821
|
+
// 「落地后只剩主题色」不是落点问题,是上游交付残缺口 + 下游挑拣落地。本项把这两件事都变成 ERROR。
|
|
822
|
+
(function checkE11() {
|
|
823
|
+
if (!changeset) {
|
|
824
|
+
warn(
|
|
825
|
+
"E11",
|
|
826
|
+
`未发现变更集(搜索 ${CHANGESET_DIRS.join(" / ")})—— 本轮交付完整性、收口断言、浮层残留、形态收口均无法判定(闸门静默跳过)。设计还原项目跑不出变更集,本身就是交付缺失。`,
|
|
827
|
+
);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
const mode = changeset.mode || "design";
|
|
831
|
+
const cov = changeset.coverage;
|
|
832
|
+
const gaps = (cov && cov.gaps) || [];
|
|
833
|
+
const pending = changeset.pending || [];
|
|
834
|
+
const changes = changeset.changes || [];
|
|
835
|
+
const notesText = JSON.stringify(changeset.notes || []);
|
|
836
|
+
|
|
837
|
+
if (mode !== "restore") {
|
|
838
|
+
info("E11", `交付模式 mode=${mode}(规范设计 / 显式轻量调整),跳过覆盖率硬闸;但若本次本意是场景还原,则「还原被降级为 design」= 交付失败。`);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
if (!cov) {
|
|
842
|
+
err("E11", "mode=restore 但缺少 coverage 通道 —— 还原交付必须有覆盖率记账;缺失即无法判定登记项是否逐条兑现,落地只会剩下主题色一类全局项。");
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
const R = cov.registrable;
|
|
846
|
+
const A = cov.applicable;
|
|
847
|
+
const C = cov.covered;
|
|
848
|
+
const numeric = typeof A === "number" && typeof C === "number";
|
|
849
|
+
if (numeric && C < A) {
|
|
850
|
+
err(
|
|
851
|
+
"E11",
|
|
852
|
+
`欠声明:coverage.covered=${C} < applicable=${A}(登记项 registrable=${R})—— 有 ${A - C} 条登记项未落进 changes[] / visible / pending。落地端只能兑现已声明的那部分,其余点位零变化,这就是「和设计对不上、只变了主题色」的机制成因。`,
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
if (gaps.length) {
|
|
856
|
+
err("E11", `coverage.gaps 非空(${gaps.length} 条)—— 交付契约要求 gaps 为空才可交付(自由文本理由不算闭合资质)。`);
|
|
857
|
+
if (!pending.length) {
|
|
858
|
+
err(
|
|
859
|
+
"E11",
|
|
860
|
+
`缺口未台账化:gaps 有 ${gaps.length} 条理由,但 pending[] 为空 —— 缺口没有变成可执行工作项,所以「继续」时无任何东西可做,复跑也不可能收敛(实证症状:多轮继续后效果不变)。`,
|
|
861
|
+
);
|
|
862
|
+
} else {
|
|
863
|
+
const declared = new Set(changes.map((c) => c.id));
|
|
864
|
+
const orphan = pending.filter((p) => p && p.proposed_id && !declared.has(p.proposed_id));
|
|
865
|
+
if (orphan.length) {
|
|
866
|
+
err("E11", `pending 中 ${orphan.length} 条未在 changes[] 双写(${orphan.slice(0, 5).map((p) => p.proposed_id).join("、")})—— 只写 pending 的点位不会被施工。`);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
// 分母漂移:自报 registrable 必须与设计侧登记表实物一致
|
|
871
|
+
if (typeof R === "number" && cov.registry) {
|
|
872
|
+
const cands = [
|
|
873
|
+
path.join(SKILL_ROOT, "..", "project-ui-design", "references", "page-specs", String(cov.registry)),
|
|
874
|
+
path.join(SKILL_ROOT, "..", "project-ui-design", String(cov.registry)),
|
|
875
|
+
].map((p) => path.resolve(p));
|
|
876
|
+
const real = cands.find((p) => fs.existsSync(p));
|
|
877
|
+
if (real) {
|
|
878
|
+
try {
|
|
879
|
+
const reg = JSON.parse(fs.readFileSync(real, "utf-8"));
|
|
880
|
+
const items = reg.items || reg.registry || [];
|
|
881
|
+
if (Array.isArray(items) && items.length && items.length !== R) {
|
|
882
|
+
err("E11", `登记表分母漂移:coverage.registrable=${R},登记表实物为 ${items.length} 条 —— 自报数与实物不一致(曾发生 186 对 190)。分母错则该报的欠声明数也不可信。`);
|
|
883
|
+
} else if (Array.isArray(items) && items.length) {
|
|
884
|
+
info("E11", `登记表分母对账:registrable=${R} = 实物 ${items.length} 条 ✓`);
|
|
885
|
+
}
|
|
886
|
+
} catch {
|
|
887
|
+
/* 解析异常忽略 */
|
|
888
|
+
}
|
|
889
|
+
} else {
|
|
890
|
+
warn("E11", `登记表实物未解析到(coverage.registry="${cov.registry}")—— 项目侧 G9 身份闸会以「登记表实物不存在」拒绝执行;覆盖率分母必须来自「方案 × 场景」的真实登记表。`);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
// 交付说明失真:notes 的成功话术与机器可读字段矛盾
|
|
894
|
+
const claimsComplete = /covered\s*[==]\s*applicable|gaps\s*[==]\s*\[\s*\]|覆盖率\s*100|全量覆盖/.test(notesText);
|
|
895
|
+
if (claimsComplete && ((numeric && C < A) || gaps.length)) {
|
|
896
|
+
err(
|
|
897
|
+
"E11",
|
|
898
|
+
`交付说明失真:notes 含「覆盖率 C=A / gaps=[] / 全量覆盖」类完成结论,而字段实为 covered=${C}/applicable=${A}、gaps=${gaps.length} 条 —— 成功话术与机器可读字段互相矛盾,会直接误导下游判定「已交付、无事可做」。`,
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
if (!errors.some((e) => e.id === "E11") && !warnings.some((w) => w.id === "E11")) {
|
|
902
|
+
info("E11", `交付完整性:mode=restore,covered=${C}=applicable=${A},gaps 空,台账闭合 ✓`);
|
|
903
|
+
}
|
|
904
|
+
})();
|
|
905
|
+
|
|
906
|
+
// ── E12 设计层累积性(跨轮回退检测 + 交付台账) ──────────────
|
|
907
|
+
// 「继续一次处理不完、继续了也没变化」的根治:把每轮落地状态落成机器可读台账,
|
|
908
|
+
// 使「本轮是否把上一轮成果覆盖掉」可被断言,而不是靠人记得。
|
|
909
|
+
const LEDGER_PATH = path.join(PROJECT, ".codebuddy", "design", "ledger.json");
|
|
910
|
+
const WRITE_LEDGER = !process.argv.includes("--no-ledger");
|
|
911
|
+
(function checkE12() {
|
|
912
|
+
const primaryText = srcTextByFile.get(PRIMARY_LAYER) ?? readIf(PRIMARY_LAYER) ?? "";
|
|
913
|
+
const primaryLines = primaryText ? primaryText.split("\n").length : 0;
|
|
914
|
+
const appliedSids = changeset ? (changeset.changes || []).map((c) => c.id).sort() : [];
|
|
915
|
+
const cov = changeset && changeset.coverage;
|
|
916
|
+
const cur = {
|
|
917
|
+
at: new Date().toISOString(),
|
|
918
|
+
changeset: changesetFile ? relp(changesetFile) : null,
|
|
919
|
+
mode: changeset ? changeset.mode || "design" : null,
|
|
920
|
+
covered: cov ? cov.covered : null,
|
|
921
|
+
applicable: cov ? cov.applicable : null,
|
|
922
|
+
design_shell_lines: primaryLines,
|
|
923
|
+
applied_sids: appliedSids,
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
let ledger = null;
|
|
927
|
+
if (fs.existsSync(LEDGER_PATH)) {
|
|
928
|
+
try {
|
|
929
|
+
ledger = JSON.parse(fs.readFileSync(LEDGER_PATH, "utf-8"));
|
|
930
|
+
} catch {
|
|
931
|
+
ledger = null;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
const history = (ledger && ledger.history) || [];
|
|
935
|
+
const priorRounds = ledger ? [ledger, ...history] : [];
|
|
936
|
+
|
|
937
|
+
if (!ledger) {
|
|
938
|
+
info("E12", `无交付台账(${relp(LEDGER_PATH)})—— 无法检测「本轮是否覆盖掉上一轮设计层」;已写入本轮台账作为基线。`);
|
|
939
|
+
} else {
|
|
940
|
+
// 与历史峰值比较,避免「坏一轮 → 台账被改写 → 下轮检测不出」的掩盖效应
|
|
941
|
+
const peakLines = Math.max(0, ...priorRounds.map((r) => r.design_shell_lines || 0));
|
|
942
|
+
if (peakLines && primaryLines && primaryLines < peakLines * 0.6) {
|
|
943
|
+
err(
|
|
944
|
+
"E12",
|
|
945
|
+
`交付回退:设计层 ${argValue("--design-layer", "src/styles/design-shell.scss")} 行数 ${peakLines} → ${primaryLines}(骤降 >40%)—— 上一轮已落地的规则被覆盖/丢失。设计层是累积产物,禁止按「出厂空文件」重建;重建必须合并既有规则。(2026-09-23 实证:620 行被覆盖为 75 行,用户侧表现为「布局样式全不对,只剩主题色」。)`,
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
const priorSids = new Set(priorRounds.flatMap((r) => r.applied_sids || []));
|
|
949
|
+
const lost = [...priorSids].filter((s) => !appliedSids.includes(s));
|
|
950
|
+
if (priorSids.size && lost.length > Math.max(3, priorSids.size * 0.3)) {
|
|
951
|
+
err("E12", `交付回退:本轮变更集比历史已声明点位少 ${lost.length} 条(${lost.slice(0, 6).join("、")}${lost.length > 6 ? "…" : ""})—— 本轮只覆盖了更小范围,按现状落地会把上一轮成果清掉。`);
|
|
952
|
+
} else if (priorSids.size && lost.length) {
|
|
953
|
+
warn(
|
|
954
|
+
"E12",
|
|
955
|
+
`本轮未覆盖历史已声明点位 ${lost.length} 条(${lost.slice(0, 6).join("、")}${lost.length > 6 ? "…" : ""})—— 若本轮 scope(coverage.page_types)确实不含这些点位可忽略;否则它们会从项目里静默消失。`,
|
|
956
|
+
);
|
|
957
|
+
} else if (priorSids.size) {
|
|
958
|
+
info("E12", `累积性:本轮 ${appliedSids.length} 条 vs 历史已声明 ${priorSids.size} 条,未检出回退`);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
if (WRITE_LEDGER) {
|
|
963
|
+
const next = { round: ledger ? (ledger.round || 1) + 1 : 1, ...cur, history: [...history, ...(ledger ? [ledger] : [])].slice(0, 20) };
|
|
964
|
+
for (const r of next.history) delete r.history;
|
|
965
|
+
fs.mkdirSync(path.dirname(LEDGER_PATH), { recursive: true });
|
|
966
|
+
fs.writeFileSync(LEDGER_PATH, JSON.stringify(next, null, 2), "utf-8");
|
|
967
|
+
}
|
|
968
|
+
})();
|
|
969
|
+
|
|
795
970
|
const result = { project: PROJECT, errors, warnings, infos };
|
|
796
971
|
|
|
797
972
|
const CHECK_TITLES = {
|