@qilitt-mickey/vue3-temp-skill 1.1.70 → 1.1.71

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qilitt-mickey/vue3-temp-skill",
3
- "version": "1.1.70",
3
+ "version": "1.1.71",
4
4
  "type": "module",
5
5
  "description": "Vue 3 企业级中后台项目开发规范技能包 — core-kernel 架构、按需功能模块",
6
6
  "bin": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "generated_at": "2026-09-23T09:41:58.162Z",
2
+ "generated_at": "2026-09-24T07:14:25.807Z",
3
3
  "oracle": "E:\\work\\Project\\Template\\skills\\vue3-temp-skill\\parser\\fixtures\\verify\\oracle.json",
4
4
  "actual": "E:\\work\\Project\\Template\\skills\\vue3-temp-skill\\parser\\fixtures\\verify\\actual-clean.json",
5
5
  "points": {
@@ -135,6 +135,17 @@ parser match(确定性翻译)→ 施工坐标 → AI 按坐标改代码 →
135
135
 
136
136
  **红线摘要(全文适用,细节在各步子文件)**:出厂中性 · 变更集驱动(上文);先结构后样式(第 2 步先于第 3 步);缺口只分「能力缺口(当场落地)」与「意图缺口(回流设计侧)」两类;**效果图/大文件只读一次**——期望值落盘 `oracle.json` 后,后续环节一律消费落盘产物,禁止反复重读。环境无 Node 时按「环境备注」(见 maintenance.md)人工执行。
137
137
 
138
+ ## 施工标记规范(G3:@sid / @closed,design-audit E15 机检)
139
+
140
+ 结构性声明(`form` / `suppress` / `suppress_inner` / `structure_ops` / `text_visible:none` / `visible:false`)**必须在设计层留机读标记**——声明了不等于落地了,没有标记就没有对账凭证(实证:`collapse_trigger.form=header_inline` 声明后壳层零规则,落地端静默无效):
141
+
142
+ | 标记 | 写法 | 作用 |
143
+ |---|---|---|
144
+ | `@sid` | 规则上方 `/* @sid: <语义ID> */` | 「变更集 ↔ 设计层」逐条落点对账(@sid 强制) |
145
+ | `@closed` | 收口规则上方 `/* @closed: <通道名> */` | suppress 收口兑现证据(通道名 = 词典 forms/css_vars/custom_style_map 的键) |
146
+
147
+ 补充证据通道:通道为 `css_vars` 键时,设计层对该 var 写关闭值(`transparent` / `none` / `0`)即视为兑现,可免 `@closed` 标记;`visible:false` 的 `display:none` 规则按 E6 口径取证。**双向对账(E15)**:设计层 `@closed` 了变更集未声明的通道 → WARN(台账失真,收口记录进不了 D13 继承链);声明了词典不认识的 form 值 / 通道名 → ERROR(悬空声明 = 无规则可挂);结构性声明在设计层零证据 → ERROR。
148
+
138
149
  ## 会话结束条件(终态硬闸,违反 = 交付未完成)
139
150
 
140
151
  **落地会话不许「做一批、停下来等用户说继续」。** 「继续」只是异常中断后的恢复手段,不是交付形态——把恢复手段当成默认流程 = 把 1:1 还原降级为多次半成品。本轮做完一批点位后,**必须自动进入下一批**,直到同时满足以下三条才允许结束会话:
package/scripts/check.mjs CHANGED
@@ -53,6 +53,49 @@ const checks = [
53
53
  return run(NODE, "scripts/design-audit.mjs", args);
54
54
  },
55
55
  },
56
+ {
57
+ name: "审计闸 G3 回归",
58
+ desc: "结构声明兑现闸:悬空 form / 未收口通道的假项目必须被 design-audit 拒绝,带 @sid/@closed 标记的正例必须通过",
59
+ cmd: () => {
60
+ const neg = path.join(ROOT, "tests/fixtures/audit-g3-negative");
61
+ const pos = path.join(ROOT, "tests/fixtures/audit-g3-positive");
62
+ const lines = [];
63
+ if (fs.existsSync(neg)) {
64
+ let rejected = false;
65
+ let out = "";
66
+ try {
67
+ out = run(NODE, "scripts/design-audit.mjs", ["--project", neg, "--no-ledger"]);
68
+ } catch (e) {
69
+ rejected = true;
70
+ out = `${e.stdout || ""}${e.stderr || ""}`;
71
+ }
72
+ if (!rejected) {
73
+ throw Object.assign(new Error("审计负例未被拒绝"), {
74
+ stdout: " ✗ audit-g3-negative 竟然通过 —— E15 结构声明兑现闸判据被削弱,必须修复\n",
75
+ });
76
+ }
77
+ if (!/\[E15\]/.test(out)) {
78
+ throw Object.assign(new Error("审计负例未命中 E15"), {
79
+ stdout: ` ✗ audit-g3-negative 被拒绝,但未命中 [E15] —— 断言已失效\n${out.slice(-800)}\n`,
80
+ });
81
+ }
82
+ lines.push("negative 被拒绝并命中 [E15]");
83
+ }
84
+ if (fs.existsSync(pos)) {
85
+ let out = "";
86
+ try {
87
+ out = run(NODE, "scripts/design-audit.mjs", ["--project", pos, "--no-ledger"]);
88
+ } catch (e) {
89
+ throw Object.assign(new Error("审计正例被拒绝"), {
90
+ stdout: ` ✗ audit-g3-positive 被拒绝(E15 存在误报,必须修复):\n${(e.stdout || "") + (e.stderr || "")}\n`,
91
+ });
92
+ }
93
+ lines.push("positive 通过");
94
+ }
95
+ if (!lines.length) return "SKIPPED 未发现审计夹具(仅源码检出时可用)";
96
+ return ` ${lines.join(";")}`;
97
+ },
98
+ },
56
99
  ];
57
100
 
58
101
  function run(runner, script, args = []) {
@@ -32,6 +32,7 @@
32
32
  * E13 固定尺寸内容度量 固定 width 小于内容需求且无收口兜底 → 文字换行/压缩/重叠
33
33
  * E14 列宽预算链自洽 列宽(含公式)扣掉实际 td 内边距后装不下内容 → 操作列按钮被压
34
34
  * E16 引用完整性 变更集引用的实物不存在(改名未同步)/ restore 缺顶层 theme
35
+ * E15 结构声明兑现 form 值 / suppress 通道名必须在词典枚举内,且设计层有 @sid / @closed 落点证据(G3 双向对账)
35
36
  */
36
37
 
37
38
  import fs from "fs";
@@ -826,6 +827,195 @@ const lastClassOutsideNot = (sel) => {
826
827
  }
827
828
  })();
828
829
 
830
+ // ── E15 结构声明兑现闸(G3:@sid 强制 + form/suppress 双向对账)──
831
+ // 结构性声明(form / suppress / suppress_inner / structure_ops / text_visible:none /
832
+ // visible:false)不能停留在台账上——项目侧必须有可指认的落点证据。实证缺口
833
+ // (2026-09-24 孔雀绿轮):collapse_trigger.form=header_inline 声明了,但词典 forms
834
+ // 只有 bottom_bar / center_edge —— 壳层零规则,落地端静默无效,而 E6/E8/E9 的
835
+ // 特判窗口都盖不住它。
836
+ // 证据通道(按优先级):
837
+ // ① /* @sid: <语义ID> */ —— 逐条落点标记(@sid 强制的机读载体)
838
+ // ② /* @closed: <通道名> */ —— suppress 收口证据(与 @sid 同构)
839
+ // ③ 通道为 css_vars 键时,设计层对该 var 写入关闭值(transparent / none / hidden / 0)
840
+ // ④ visible:false 的 display:none 收口规则(E6 同源)
841
+ // 判定:
842
+ // · form 值不在词典 forms 枚举内 / suppress 通道名不是 forms∪css_vars∪custom_style_map
843
+ // 的键 → ERROR(声明悬空 = 项目侧无规则可挂)
844
+ // · suppress_rules[form] 与 suppress[] 要求的通道无任何兑现证据 → ERROR
845
+ // · 有落点证据但缺 @sid → WARN(补标记使逐条对账可机读)
846
+ // · 出厂默认形态且无其他结构载荷 → WARN(保持现状无需规则,但仍需标记)
847
+ // · 反向:设计层 @closed 了变更集未声明的通道 → WARN(台账失真,收口记录进不了 D13 继承链)
848
+ const CLOSING_VALUE_RE = /^(transparent|none|hidden|0(?:px)?)(\s|$)/i;
849
+ const markedSids = new Set([...designCss.matchAll(/@sid\s*:\s*([\w.$-]+)/g)].map((m) => m[1]));
850
+ const closedMarkers = new Set([...designCss.matchAll(/@closed\s*:\s*([\w.-]+)/g)].map((m) => m[1]));
851
+
852
+ /** 与 parser extractSuppressRules 同源:结构化 suppress_rules 优先,forms 描述「suppress 必含」兜底 */
853
+ function extractSuppressRulesAudit(entry) {
854
+ const rules = {};
855
+ if (entry && typeof entry.suppress_rules === "object" && entry.suppress_rules !== null) {
856
+ for (const [form, channels] of Object.entries(entry.suppress_rules)) {
857
+ rules[form] = Array.isArray(channels) ? channels.filter((c) => typeof c === "string") : [];
858
+ }
859
+ }
860
+ const forms = entry?.forms;
861
+ if (forms && typeof forms === "object" && !Array.isArray(forms)) {
862
+ for (const [form, desc] of Object.entries(forms)) {
863
+ if (typeof desc !== "string") continue;
864
+ for (const m of desc.matchAll(/suppress\s*必含\s*([a-z][a-z0-9_]*(?:[、,/][a-z][a-z0-9_]*)*)/gi)) {
865
+ const channels = m[1].split(/[、,/]/).map((s) => s.trim()).filter(Boolean);
866
+ rules[form] = [...new Set([...(rules[form] || []), ...channels])];
867
+ }
868
+ }
869
+ }
870
+ return rules;
871
+ }
872
+
873
+ const channelNamesOf = (entry) =>
874
+ new Set([
875
+ ...Object.keys(entry?.forms || {}),
876
+ ...Object.keys(entry?.css_vars || {}),
877
+ ...Object.keys(entry?.custom_style_map || {}),
878
+ ]);
879
+
880
+ /** 通道收口证据:@closed 标记 或(css_vars 键)关闭值覆写 */
881
+ function channelEvidence(entry, ch) {
882
+ if (closedMarkers.has(ch)) return `@closed:${ch}`;
883
+ const varName = entry?.css_vars?.[ch];
884
+ if (varName) {
885
+ const re = new RegExp(`${varName}\\s*:\\s*([^;\\n}]+)`, "i");
886
+ const m = re.exec(designCss);
887
+ if (m && CLOSING_VALUE_RE.test(m[1].trim())) return `${varName} 关闭值覆写`;
888
+ }
889
+ return null;
890
+ }
891
+
892
+ /** visible:false 落点证据(E6 同源口径) */
893
+ function visibleClosureRule(change) {
894
+ const entry = dictSidMap.get(change.id);
895
+ const target = classTokens(entry?.selector || "").slice(-1)[0];
896
+ if (!target) return null;
897
+ return (
898
+ designRules.find((r) => r.selector.includes(`.${target}`) && /none/i.test(r.decls.display || "")) || null
899
+ );
900
+ }
901
+
902
+ (function checkE15() {
903
+ if (!changeset) {
904
+ info("E15", "未发现变更集,跳过结构声明兑现对账");
905
+ return;
906
+ }
907
+ const isStructural = (c) =>
908
+ typeof c?.form === "string" ||
909
+ (Array.isArray(c?.suppress) && c.suppress.length > 0) ||
910
+ (typeof c?.suppress_inner === "string" && c.suppress_inner.trim()) ||
911
+ (Array.isArray(c?.structure_ops) && c.structure_ops.length > 0) ||
912
+ c?.style?.text_visible === "none" ||
913
+ c?.visible === false;
914
+
915
+ const changes = changeset.changes || [];
916
+ for (const change of changes) {
917
+ if (!isStructural(change)) continue;
918
+ const entry = dictSidMap.get(change.id);
919
+ if (!entry) {
920
+ warn("E15", `${change.id} 声明结构语义(form/suppress 等),但词典无该条目 —— 项目侧无挂载点,无法兑现(match 会拦截)。`);
921
+ continue;
922
+ }
923
+
924
+ // ① form 值真实性:悬空 form 值 = 壳层零规则(header_inline 实证)
925
+ if (typeof change.form === "string" && change.form) {
926
+ const forms = entry.forms && typeof entry.forms === "object" ? Object.keys(entry.forms) : [];
927
+ const rules = entry.suppress_rules || {};
928
+ if (forms.length && !forms.includes(change.form) && !(change.form in rules)) {
929
+ err(
930
+ "E15",
931
+ `${change.id} 的 form="${change.form}" 不在词典 forms 枚举 [${forms.join("、")}] 内 —— 项目侧无规则可挂(壳层零规则),声明悬空。形态值只能取词典已支持的枚举;确需新形态先走 pending 补词典。`,
932
+ );
933
+ }
934
+ }
935
+
936
+ // ② suppress 通道名真实性(「声明 → 词典」对账)
937
+ const declared = Array.isArray(change.suppress) ? change.suppress : [];
938
+ const known = channelNamesOf(entry);
939
+ const unknown = declared.filter((ch) => !known.has(ch));
940
+ if (unknown.length) {
941
+ err(
942
+ "E15",
943
+ `${change.id}.suppress 引用了词典不认识的通道 [${unknown.join("、")}](可用通道:${[...known].join("、")})—— 通道名以该条目 forms/css_vars/custom_style_map 的键为准。`,
944
+ );
945
+ }
946
+
947
+ // ③ suppress_rules[form] 与已声明通道的兑现证据(「声明 → 设计层」对账)
948
+ const required = extractSuppressRulesAudit(entry)[change.form] || [];
949
+ const demanded = [...new Set([...required, ...declared])];
950
+ let allClosed = true;
951
+ for (const ch of demanded) {
952
+ const ev = channelEvidence(entry, ch);
953
+ if (!ev) {
954
+ allClosed = false;
955
+ const varHint = entry.css_vars?.[ch]
956
+ ? `,或(通道为 css_vars 键)在设计层覆写 ${entry.css_vars[ch]} 为关闭值(transparent / none / 0)`
957
+ : "";
958
+ err(
959
+ "E15",
960
+ `${change.id} 要求收口通道「${ch}」,但设计层无任何兑现证据 —— 需要 /* @closed: ${ch} */ 标记${varHint}。声明了不收口 = 出厂旧装饰残留(双竖条并存类实证)。`,
961
+ );
962
+ }
963
+ }
964
+
965
+ // ④ @sid 强制(零证据 = 壳层零规则;有证据缺标记 = 降级 WARN 促标记)
966
+ const hasSid = markedSids.has(change.id);
967
+ if (!hasSid) {
968
+ const visRule = change.visible === false ? visibleClosureRule(change) : null;
969
+ if (change.visible === false && !visRule) {
970
+ // E6 已就同一根因报 ERROR,此处不重复
971
+ } else if (demanded.length && allClosed) {
972
+ warn("E15", `${change.id} 的收口已有落点证据,但缺 /* @sid: ${change.id} */ 标记 —— 补标记使「变更集 ↔ 设计层」逐条对账可机读(G3)。`);
973
+ } else if (change.visible === false && visRule) {
974
+ warn("E15", `${change.id} 的 display:none 收口已落(E6 ✓),但缺 /* @sid: ${change.id} */ 标记 —— 补标记使逐条对账可机读。`);
975
+ } else {
976
+ const forms = entry.forms && typeof entry.forms === "object" ? entry.forms : {};
977
+ const isDefaultForm = typeof change.form === "string" && /出厂默认|默认形态|default/i.test(String(forms[change.form] || ""));
978
+ const hasOps = Array.isArray(change.structure_ops) && change.structure_ops.length > 0;
979
+ const hasInner = typeof change.suppress_inner === "string" && change.suppress_inner.trim().length > 0;
980
+ const hasTextNone = change.style?.text_visible === "none";
981
+ const otherPayload = hasOps || hasInner || hasTextNone || demanded.length > 0;
982
+ if (isDefaultForm && !otherPayload) {
983
+ warn("E15", `${change.id} 的 form="${change.form}" 是出厂默认形态(无需新增规则),但缺 /* @sid: ${change.id} */ 标记 —— 建议补标记。`);
984
+ } else if (hasOps && !otherPayload) {
985
+ warn("E15", `${change.id} 的 structure_ops 由 parser G2 施工证据闸取证;设计层建议补 /* @sid: ${change.id} */ 标记。`);
986
+ } else {
987
+ err(
988
+ "E15",
989
+ `${change.id} 的结构性声明在设计层零落点证据(无 @sid 标记、无 @closed 兑现标记、无收口规则)—— 壳层零规则,声明悬空(实证:collapse_trigger.form=header_inline 声明后壳层无任何规则)。每条结构性声明必须在设计层留机读标记:/* @sid: <语义ID> */。`,
990
+ );
991
+ }
992
+ }
993
+ }
994
+ }
995
+
996
+ // ⑤ 反向对账:设计层收口了变更集未声明的通道 → 台账失真
997
+ for (const ch of closedMarkers) {
998
+ const owners = changes
999
+ .map((c) => ({ c, e: dictSidMap.get(c?.id) }))
1000
+ .filter(({ e }) => e && channelNamesOf(e).has(ch));
1001
+ if (!owners.length) continue; // 不属于本变更集涉及的点位(历史层),不判
1002
+ const accounted = owners.some(
1003
+ ({ c, e }) =>
1004
+ (Array.isArray(c.suppress) ? c.suppress.includes(ch) : false) ||
1005
+ (extractSuppressRulesAudit(e)[c.form] || []).includes(ch),
1006
+ );
1007
+ if (!accounted) {
1008
+ warn(
1009
+ "E15",
1010
+ `设计层 /* @closed: ${ch} */ 收口了通道,但变更集未声明该收口(涉及点位:${owners.map((o) => o.c.id).join("、")})—— 台账失真:收口记录进不了 D13 继承链,下轮漏抄即出厂形态复现。`,
1011
+ );
1012
+ }
1013
+ }
1014
+ if (!errors.some((e) => e.id === "E15") && !warnings.some((w) => w.id === "E15")) {
1015
+ info("E15", "结构声明兑现:form/suppress 全部落在词典已知通道且设计层有落点证据 ✓");
1016
+ }
1017
+ })();
1018
+
829
1019
  // ── @sid 标记覆盖率(可选增强) ─────────────────────────────
830
1020
  (function checkSidCoverage() {
831
1021
  const marked = new Set([...designCss.matchAll(/@sid\s*:\s*([\w.$-]+)/g)].map((m) => m[1]));