@diviops/mcp-server 1.5.14 → 1.5.17

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.
Files changed (36) hide show
  1. package/README.md +10 -1
  2. package/data/verified-attrs-backlog.json +5 -5
  3. package/data/verified-attrs.json +8 -8
  4. package/dist/compatibility.d.ts +25 -0
  5. package/dist/index.js +349 -4
  6. package/dist/preset-cli/button-emitter.d.ts +1 -1
  7. package/dist/preset-cli/button-emitter.js +1 -1
  8. package/dist/preset-cli/cli.d.ts +4 -3
  9. package/dist/preset-cli/cli.js +11 -10
  10. package/dist/preset-cli/heading-font-emitter.d.ts +1 -1
  11. package/dist/preset-cli/heading-font-emitter.js +3 -3
  12. package/dist/preset-cli/spacing-emitter.d.ts +13 -13
  13. package/dist/preset-cli/spacing-emitter.js +23 -23
  14. package/dist/preset-cli/write-path.d.ts +3 -3
  15. package/dist/preset-cli/write-path.js +3 -3
  16. package/dist/wp-cli-fs-validator.d.ts +1 -1
  17. package/dist/wp-cli-fs-validator.js +1 -1
  18. package/dist/wp-cli.js +1 -2
  19. package/dist/wp-client.js +17 -0
  20. package/package.json +3 -2
  21. package/dist/preset-cli/__tests__/button-emitter.test.d.ts +0 -8
  22. package/dist/preset-cli/__tests__/button-emitter.test.js +0 -188
  23. package/dist/preset-cli/__tests__/cli.test.d.ts +0 -9
  24. package/dist/preset-cli/__tests__/cli.test.js +0 -791
  25. package/dist/preset-cli/__tests__/heading-font-emitter.test.d.ts +0 -12
  26. package/dist/preset-cli/__tests__/heading-font-emitter.test.js +0 -249
  27. package/dist/preset-cli/__tests__/preset-create-unchanged.test.d.ts +0 -13
  28. package/dist/preset-cli/__tests__/preset-create-unchanged.test.js +0 -64
  29. package/dist/preset-cli/__tests__/registry.test.d.ts +0 -5
  30. package/dist/preset-cli/__tests__/registry.test.js +0 -175
  31. package/dist/preset-cli/__tests__/spacing-emitter.test.d.ts +0 -20
  32. package/dist/preset-cli/__tests__/spacing-emitter.test.js +0 -409
  33. package/dist/preset-cli/__tests__/text-body-font-emitter.test.d.ts +0 -14
  34. package/dist/preset-cli/__tests__/text-body-font-emitter.test.js +0 -191
  35. package/dist/preset-cli/__tests__/write-path.test.d.ts +0 -8
  36. package/dist/preset-cli/__tests__/write-path.test.js +0 -287
@@ -1,12 +0,0 @@
1
- /**
2
- * `divi/font` heading emitter shape + gating coverage:
3
- * - fixture-based shape assertion against round-1a (Pattern A) and
4
- * round-1b (Pattern B) canonical captures;
5
- * - Pattern B no-weight discriminator: omitted `weight` produces no key;
6
- * - Pattern B + explicit weight refused as unverified/out-of-scope;
7
- * - variant-aware registry gating: missing applicability or under-
8
- * verified evidence on the chosen variant throws with attr / pattern /
9
- * effective-level / source in the message; Pattern A evidence does NOT
10
- * vouch for Pattern B and vice versa.
11
- */
12
- export {};
@@ -1,249 +0,0 @@
1
- /**
2
- * `divi/font` heading emitter shape + gating coverage:
3
- * - fixture-based shape assertion against round-1a (Pattern A) and
4
- * round-1b (Pattern B) canonical captures;
5
- * - Pattern B no-weight discriminator: omitted `weight` produces no key;
6
- * - Pattern B + explicit weight refused as unverified/out-of-scope;
7
- * - variant-aware registry gating: missing applicability or under-
8
- * verified evidence on the chosen variant throws with attr / pattern /
9
- * effective-level / source in the message; Pattern A evidence does NOT
10
- * vouch for Pattern B and vice versa.
11
- */
12
- import { test } from "node:test";
13
- import assert from "node:assert/strict";
14
- import { readFileSync } from "node:fs";
15
- import { fileURLToPath } from "node:url";
16
- import { dirname, join } from "node:path";
17
- import { emitHeadingFontGroupPreset, composeHeadingFontAttrs, buildHeadingFontPresetCreateBody, UnsupportedVariantCombinationError, HEADING_FONT_MODULE, HEADING_FONT_GROUP_NAME, HEADING_FONT_GROUP_ID, HEADING_FONT_PATTERN_VARIANTS, } from "../heading-font-emitter.js";
18
- import { loadRegistry, EvidenceGateError, } from "../registry.js";
19
- const __dirname = dirname(fileURLToPath(import.meta.url));
20
- const REPO_ROOT = join(__dirname, "..", "..", "..", "..");
21
- const FIXTURE_DIR = join(REPO_ROOT, "docs/verification/evidence/canonical-shape-dumps-2026-05-18");
22
- const FIXTURE_1A = join(FIXTURE_DIR, "round-1a-heading-h1-pattern-a-google.json");
23
- const FIXTURE_1B = join(FIXTURE_DIR, "round-1b-heading-h1-pattern-b-local.json");
24
- const registry = loadRegistry();
25
- test("emitter byte-matches round-1a (Pattern A — Google Fonts) fixture attrs", () => {
26
- const fixture = JSON.parse(readFileSync(FIXTURE_1A, "utf8"));
27
- const canonicalAttrs = fixture.preset_entry.attrs;
28
- const canonicalStyleAttrs = fixture.preset_entry.styleAttrs;
29
- const entry = emitHeadingFontGroupPreset({
30
- name: "canonical-heading-h1-vb-google-2026-05-18",
31
- pattern: "google",
32
- family: "Inter",
33
- weight: "700",
34
- color: '$variable({"type":"color","value":{"name":"gcid-heading-color","settings":{}}})$',
35
- size: "48px",
36
- }, registry);
37
- assert.deepEqual(entry.attrs, canonicalAttrs, "emitted attrs must byte-match the Pattern A canonical capture");
38
- // Round 1a observation: attrs and styleAttrs are byte-identical. The
39
- // emitter only emits `attrs`; the plugin's /preset/create route mirrors
40
- // into styleAttrs. So `attrs` must equal the fixture's styleAttrs too.
41
- assert.deepEqual(entry.attrs, canonicalStyleAttrs, "attrs must equal styleAttrs (mirror at /preset/create layer)");
42
- assert.equal(entry.type, "group");
43
- assert.equal(entry.group_name, HEADING_FONT_GROUP_NAME);
44
- assert.equal(entry.group_id, HEADING_FONT_GROUP_ID);
45
- assert.equal(entry.module_name, HEADING_FONT_MODULE);
46
- assert.equal(entry.pattern_variant, HEADING_FONT_PATTERN_VARIANTS.google);
47
- });
48
- test("emitter byte-matches round-1b (Pattern B — local-hosted) fixture attrs", () => {
49
- const fixture = JSON.parse(readFileSync(FIXTURE_1B, "utf8"));
50
- const canonicalAttrs = fixture.preset_entry.attrs;
51
- const canonicalStyleAttrs = fixture.preset_entry.styleAttrs;
52
- const entry = emitHeadingFontGroupPreset({
53
- name: "canonical-heading-h1-vb-local-2026-05-18",
54
- pattern: "local",
55
- family: "Sora 700",
56
- // weight intentionally absent — Pattern B encodes the weight in the
57
- // family string itself.
58
- color: '$variable({"type":"color","value":{"name":"gcid-heading-color","settings":{}}})$',
59
- size: "48px",
60
- }, registry);
61
- assert.deepEqual(entry.attrs, canonicalAttrs, "emitted attrs must byte-match the Pattern B canonical capture");
62
- assert.deepEqual(entry.attrs, canonicalStyleAttrs, "attrs must equal styleAttrs (mirror at /preset/create layer)");
63
- assert.equal(entry.pattern_variant, HEADING_FONT_PATTERN_VARIANTS.local);
64
- });
65
- test("Pattern B discriminator: omitted weight produces NO weight key", () => {
66
- const attrs = composeHeadingFontAttrs({
67
- name: "B",
68
- pattern: "local",
69
- family: "Sora 700",
70
- color: "#666666",
71
- size: "48px",
72
- });
73
- const value = attrs.title.decoration.font.font.desktop.value;
74
- assert.equal("weight" in value, false, "Pattern B canonical shape has no `weight` key in attrs.title.decoration.font.font.desktop.value");
75
- // The exact set of structural keys, no more, no less. Order is not
76
- // semantically meaningful (deep-equal is order-insensitive on objects)
77
- // — sort here for a stable assertion.
78
- assert.deepEqual(Object.keys(value).sort(), ["color", "family", "size"], "Pattern B emits ONLY the keys the user specified — no defaulted weight");
79
- });
80
- test("Pattern A discriminator: weight emitted when specified, absent when not", () => {
81
- const withWeight = composeHeadingFontAttrs({
82
- name: "A+w",
83
- pattern: "google",
84
- family: "Inter",
85
- weight: "700",
86
- });
87
- const withoutWeight = composeHeadingFontAttrs({
88
- name: "A-w",
89
- pattern: "google",
90
- family: "Inter",
91
- });
92
- const wv = withWeight.title.decoration.font.font.desktop.value;
93
- const wov = withoutWeight.title.decoration.font.font.desktop.value;
94
- assert.equal(wv.weight, "700");
95
- assert.equal("weight" in wov, false, "weight absent when not specified");
96
- });
97
- test("Pattern B + explicit --font-weight is refused as out-of-scope", () => {
98
- assert.throws(() => emitHeadingFontGroupPreset({
99
- name: "B-with-weight",
100
- pattern: "local",
101
- family: "Sora 700",
102
- weight: "700",
103
- }, registry), (err) => {
104
- assert.ok(err instanceof UnsupportedVariantCombinationError);
105
- assert.match(err.message, /Pattern B/);
106
- assert.match(err.message, /weight/);
107
- return true;
108
- });
109
- });
110
- test("emit-on-specification: only specified keys produce keys (Pattern A)", () => {
111
- const attrs = composeHeadingFontAttrs({
112
- name: "minimal",
113
- pattern: "google",
114
- family: "Inter",
115
- });
116
- const value = attrs.title.decoration.font.font.desktop.value;
117
- assert.deepEqual(Object.keys(value), ["family"], "ONLY the touched key emitted — no defaulted weight/color/size/lineHeight");
118
- });
119
- test("variable color tokens get the {name,settings} object shape + trailing )$", () => {
120
- const attrs = composeHeadingFontAttrs({
121
- name: "v",
122
- pattern: "google",
123
- color: "gcid-heading-color",
124
- });
125
- const color = attrs.title.decoration.font.font.desktop.value.color;
126
- assert.ok(color.endsWith(")$"));
127
- assert.match(color, /^\$variable\(/);
128
- const payload = JSON.parse(color.slice("$variable(".length, -2));
129
- assert.deepEqual(payload, {
130
- type: "color",
131
- value: { name: "gcid-heading-color", settings: {} },
132
- });
133
- });
134
- test("literal hex color is emitted verbatim, not wrapped", () => {
135
- const attrs = composeHeadingFontAttrs({
136
- name: "h",
137
- pattern: "google",
138
- color: "#666666",
139
- });
140
- assert.equal(attrs.title.decoration.font.font.desktop.value.color, "#666666");
141
- });
142
- test("lineHeight is emitted only when specified", () => {
143
- const withLH = composeHeadingFontAttrs({
144
- name: "lh+",
145
- pattern: "google",
146
- family: "Inter",
147
- lineHeight: "1.1",
148
- });
149
- assert.equal(withLH.title.decoration.font.font.desktop.value.lineHeight, "1.1");
150
- const without = composeHeadingFontAttrs({
151
- name: "lh-",
152
- pattern: "google",
153
- family: "Inter",
154
- });
155
- assert.equal("lineHeight" in without.title.decoration.font.font.desktop.value, false);
156
- });
157
- test("emitter rejects an invalid `pattern` value", () => {
158
- assert.throws(() => emitHeadingFontGroupPreset(
159
- // @ts-expect-error — exercising runtime validation
160
- { name: "x", pattern: "auto", family: "Inter" }, registry), /pattern.*google.*local/i);
161
- });
162
- test("emitter rejects an empty preset (no styling specified)", () => {
163
- assert.throws(() => emitHeadingFontGroupPreset({ name: "empty", pattern: "google" }, registry), /empty preset/);
164
- });
165
- test("emitter rejects a missing name", () => {
166
- assert.throws(() => emitHeadingFontGroupPreset({ name: "", pattern: "google", family: "Inter" }, registry), /requires a non-empty `name`/);
167
- });
168
- test("variant-aware gate: missing applicability throws with attr+variant+source", () => {
169
- // Build a synthetic registry where the Pattern A entry's applicability
170
- // for divi/heading is REMOVED — Pattern B still verified. Confirms a
171
- // missing applicability cell on the chosen variant is caught at the
172
- // gate (not silently inherited from the pattern level), and that the
173
- // error message names the variant and the registry source.
174
- const real = loadRegistry();
175
- const synthetic = JSON.parse(JSON.stringify(real));
176
- const t2 = synthetic.tier2 ?? [];
177
- const a = t2.find((e) => e.pattern_family === "divi/font" &&
178
- e.pattern_variant === HEADING_FONT_PATTERN_VARIANTS.google);
179
- assert.ok(a, "test prereq: Pattern A entry must exist in the real registry");
180
- // Strip the divi/heading applicability cell from the Pattern A variant.
181
- delete a.applicability["divi/heading"];
182
- assert.throws(() => emitHeadingFontGroupPreset({
183
- name: "missing-applic",
184
- pattern: "google",
185
- family: "Inter",
186
- weight: "700",
187
- }, synthetic), (err) => {
188
- assert.ok(err instanceof EvidenceGateError);
189
- assert.match(err.message, /divi\/font/);
190
- assert.match(err.message, /google_fonts_pattern_a/);
191
- assert.match(err.message, /UNVERIFIED \(0\)/);
192
- assert.match(err.message, /absent from the registry entry/);
193
- return true;
194
- });
195
- });
196
- test("variant isolation: Pattern A evidence does NOT vouch for Pattern B (and vice versa)", () => {
197
- // Build a synthetic registry containing ONLY the Pattern A divi/font
198
- // entry (Pattern B variant removed). A Pattern-B caller must be
199
- // refused — its variant is entirely absent — even though a Pattern A
200
- // entry under the same `pattern_family` is fully verified.
201
- const real = loadRegistry();
202
- const synthetic = JSON.parse(JSON.stringify(real));
203
- synthetic.tier2 = (synthetic.tier2 ?? []).filter((e) => !(e.pattern_family === "divi/font" &&
204
- e.pattern_variant === HEADING_FONT_PATTERN_VARIANTS.local));
205
- assert.throws(() => emitHeadingFontGroupPreset({
206
- name: "B-without-its-entry",
207
- pattern: "local",
208
- family: "Sora 700",
209
- }, synthetic), (err) => {
210
- // Variant absent entirely → resolveEvidence throws the
211
- // "absent from verified-attrs.json" error (not EvidenceGateError).
212
- assert.match(err.message, /absent from verified-attrs\.json/, "missing variant must NOT silently fall back to the other variant's evidence");
213
- assert.match(err.message, /local_hosted_pattern_b/);
214
- return true;
215
- });
216
- // The reverse: stripping Pattern A and asking for Pattern A.
217
- const synthetic2 = JSON.parse(JSON.stringify(real));
218
- synthetic2.tier2 = (synthetic2.tier2 ?? []).filter((e) => !(e.pattern_family === "divi/font" &&
219
- e.pattern_variant === HEADING_FONT_PATTERN_VARIANTS.google));
220
- assert.throws(() => emitHeadingFontGroupPreset({ name: "A-without-its-entry", pattern: "google", family: "Inter" }, synthetic2), /google_fonts_pattern_a/);
221
- });
222
- test("buildHeadingFontPresetCreateBody mirrors the diviops_preset_create body shape", () => {
223
- const entry = emitHeadingFontGroupPreset({ name: "H1", pattern: "google", family: "Inter", weight: "700" }, registry);
224
- const body = buildHeadingFontPresetCreateBody(entry, { dry_run: true });
225
- assert.deepEqual(body, {
226
- module_name: HEADING_FONT_MODULE,
227
- name: "H1",
228
- attrs: entry.attrs,
229
- type: "group",
230
- group_name: HEADING_FONT_GROUP_NAME,
231
- group_id: HEADING_FONT_GROUP_ID,
232
- dry_run: true,
233
- });
234
- // `pattern_variant` is in-memory metadata; it must NOT leak into the wire body.
235
- assert.equal("pattern_variant" in body, false, "pattern_variant is client-side gating metadata; it must not be sent over the wire");
236
- const noDry = buildHeadingFontPresetCreateBody(entry);
237
- assert.equal("dry_run" in noDry, false);
238
- });
239
- test("real registry: both divi/font variants on divi/heading clear the write threshold", () => {
240
- // The verified-attrs.json shipped in-repo MUST keep both Pattern A and
241
- // Pattern B at effective-evidence VB_PRESET_STORAGE_VERIFIED (4) for
242
- // divi/heading. If a registry edit ever drops one below 4, this test
243
- // is the canary.
244
- const reg = loadRegistry();
245
- const a = emitHeadingFontGroupPreset({ name: "A", pattern: "google", family: "Inter", weight: "700" }, reg);
246
- assert.equal(a.pattern_variant, HEADING_FONT_PATTERN_VARIANTS.google);
247
- const b = emitHeadingFontGroupPreset({ name: "B", pattern: "local", family: "Sora 700" }, reg);
248
- assert.equal(b.pattern_variant, HEADING_FONT_PATTERN_VARIANTS.local);
249
- });
@@ -1,13 +0,0 @@
1
- /**
2
- * AC #9 — `diviops_preset_create` MCP tool behavior is unchanged.
3
- *
4
- * Track 4 adds a standalone CLI; it must NOT modify the existing MCP tool.
5
- * This test pins two contracts:
6
- * 1. The CLI posts to the SAME route the MCP tool uses (`/preset/create`)
7
- * with a body whose keys are a subset of the MCP tool's body keys —
8
- * i.e. the CLI is a peer consumer of the existing route, not a fork.
9
- * 2. The `diviops_preset_create` registration block in `src/index.ts` is
10
- * byte-identical to the committed baseline on `main` (a guard against
11
- * an accidental edit landing in this PR).
12
- */
13
- export {};
@@ -1,64 +0,0 @@
1
- /**
2
- * AC #9 — `diviops_preset_create` MCP tool behavior is unchanged.
3
- *
4
- * Track 4 adds a standalone CLI; it must NOT modify the existing MCP tool.
5
- * This test pins two contracts:
6
- * 1. The CLI posts to the SAME route the MCP tool uses (`/preset/create`)
7
- * with a body whose keys are a subset of the MCP tool's body keys —
8
- * i.e. the CLI is a peer consumer of the existing route, not a fork.
9
- * 2. The `diviops_preset_create` registration block in `src/index.ts` is
10
- * byte-identical to the committed baseline on `main` (a guard against
11
- * an accidental edit landing in this PR).
12
- */
13
- import { test } from "node:test";
14
- import assert from "node:assert/strict";
15
- import { execFileSync } from "node:child_process";
16
- import { readFileSync } from "node:fs";
17
- import { fileURLToPath } from "node:url";
18
- import { dirname, join } from "node:path";
19
- import { PRESET_CREATE_ROUTE } from "../write-path.js";
20
- import { buildPresetCreateBody, emitButtonGroupPreset } from "../button-emitter.js";
21
- import { loadRegistry } from "../registry.js";
22
- const __dirname = dirname(fileURLToPath(import.meta.url));
23
- const SERVER_ROOT = join(__dirname, "..", "..", "..");
24
- test("CLI posts to the same /preset/create route as diviops_preset_create", () => {
25
- assert.equal(PRESET_CREATE_ROUTE, "/preset/create");
26
- });
27
- test("CLI write body keys are a subset of the diviops_preset_create contract", () => {
28
- // diviops_preset_create accepts: module_name, name, attrs, type,
29
- // group_name, group_id, primary_attr_name, make_default, priority, dry_run.
30
- const allowed = new Set([
31
- "module_name",
32
- "name",
33
- "attrs",
34
- "type",
35
- "group_name",
36
- "group_id",
37
- "primary_attr_name",
38
- "make_default",
39
- "priority",
40
- "dry_run",
41
- ]);
42
- const entry = emitButtonGroupPreset({ name: "P", bg_color: "#111" }, loadRegistry());
43
- const body = buildPresetCreateBody(entry, { dry_run: true });
44
- for (const key of Object.keys(body)) {
45
- assert.ok(allowed.has(key), `CLI body key "${key}" is not part of the diviops_preset_create contract`);
46
- }
47
- });
48
- test("diviops_preset_create registration in src/index.ts is unchanged vs main", () => {
49
- // Diff src/index.ts against main; the preset_create handler region must
50
- // not appear in the diff. If `main` is unavailable (detached CI checkout)
51
- // the test is skipped rather than failing spuriously.
52
- let diff = "";
53
- try {
54
- diff = execFileSync("git", ["diff", "main", "--", "src/index.ts"], { cwd: SERVER_ROOT, encoding: "utf8" });
55
- }
56
- catch {
57
- // git/main not resolvable in this environment — fall back to a
58
- // presence check that the tool is still registered.
59
- const src = readFileSync(join(SERVER_ROOT, "src", "index.ts"), "utf8");
60
- assert.match(src, /"diviops_preset_create"/);
61
- return;
62
- }
63
- assert.equal(diff.includes("diviops_preset_create"), false, "src/index.ts diff vs main must not touch the diviops_preset_create tool");
64
- });
@@ -1,5 +0,0 @@
1
- /**
2
- * Registry-gating coverage: the min() rule, missing-applicability → 0,
3
- * and threshold refusal with attr + level + source in the error.
4
- */
5
- export {};
@@ -1,175 +0,0 @@
1
- /**
2
- * Registry-gating coverage: the min() rule, missing-applicability → 0,
3
- * and threshold refusal with attr + level + source in the error.
4
- */
5
- import { test } from "node:test";
6
- import assert from "node:assert/strict";
7
- import { loadRegistry, resolveEvidence, gateWriteAttr, EvidenceGateError, writeThresholdNumber, } from "../registry.js";
8
- /** A minimal synthetic registry exercising every branch of the min() rule. */
9
- function syntheticRegistry() {
10
- return {
11
- evidence_level_ordering: {
12
- UNVERIFIED: 0,
13
- SCHEMA_OBSERVED: 1,
14
- RUNTIME_VERIFIED: 2,
15
- VB_ROUNDTRIP_VERIFIED: 3,
16
- VB_PRESET_STORAGE_VERIFIED: 4,
17
- CROSS_VERSION_STABLE: 5,
18
- },
19
- tier2: [
20
- {
21
- // pattern 4, cell 4 -> effective 4 (clears threshold)
22
- pattern_family: "synthetic/clears",
23
- pattern_evidence_level: "VB_PRESET_STORAGE_VERIFIED",
24
- pattern_evidence_source: "docs/synthetic/clears.json",
25
- applicability: {
26
- "divi/widget": {
27
- cell_evidence_level: "VB_PRESET_STORAGE_VERIFIED",
28
- source: "docs/synthetic/clears-cell.json",
29
- },
30
- },
31
- },
32
- {
33
- // pattern 4, cell 1 -> effective 1 (min picks the cell, below threshold)
34
- pattern_family: "synthetic/cell-low",
35
- pattern_evidence_level: "VB_PRESET_STORAGE_VERIFIED",
36
- pattern_evidence_source: "docs/synthetic/cell-low-pattern.json",
37
- applicability: {
38
- "divi/widget": {
39
- cell_evidence_level: "SCHEMA_OBSERVED",
40
- source: "docs/synthetic/cell-low-cell.json",
41
- },
42
- },
43
- },
44
- {
45
- // pattern 1, cell 4 -> effective 1 (min picks the pattern, below threshold)
46
- pattern_family: "synthetic/pattern-low",
47
- pattern_evidence_level: "SCHEMA_OBSERVED",
48
- pattern_evidence_source: "docs/synthetic/pattern-low-pattern.json",
49
- applicability: {
50
- "divi/widget": {
51
- cell_evidence_level: "VB_PRESET_STORAGE_VERIFIED",
52
- source: "docs/synthetic/pattern-low-cell.json",
53
- },
54
- },
55
- },
56
- {
57
- // pattern 4, NO applicability cell for divi/widget -> effective 0
58
- pattern_family: "synthetic/no-cell",
59
- pattern_evidence_level: "VB_PRESET_STORAGE_VERIFIED",
60
- pattern_evidence_source: "docs/synthetic/no-cell.json",
61
- applicability: {
62
- "divi/other": {
63
- cell_evidence_level: "VB_PRESET_STORAGE_VERIFIED",
64
- source: "docs/synthetic/no-cell-other.json",
65
- },
66
- },
67
- },
68
- ],
69
- };
70
- }
71
- test("min() rule picks the smaller of pattern and cell", () => {
72
- const reg = syntheticRegistry();
73
- const clears = resolveEvidence(reg, "divi/widget", "synthetic/clears");
74
- assert.equal(clears.effectiveLevel, 4);
75
- assert.equal(clears.effectiveLevelName, "VB_PRESET_STORAGE_VERIFIED");
76
- const cellLow = resolveEvidence(reg, "divi/widget", "synthetic/cell-low");
77
- assert.equal(cellLow.patternLevel, 4);
78
- assert.equal(cellLow.cellLevel, 1);
79
- assert.equal(cellLow.effectiveLevel, 1, "min picks the cell level");
80
- const patternLow = resolveEvidence(reg, "divi/widget", "synthetic/pattern-low");
81
- assert.equal(patternLow.patternLevel, 1);
82
- assert.equal(patternLow.cellLevel, 4);
83
- assert.equal(patternLow.effectiveLevel, 1, "min picks the pattern level");
84
- });
85
- test("missing applicability[<module>] resolves to UNVERIFIED (0)", () => {
86
- const reg = syntheticRegistry();
87
- const r = resolveEvidence(reg, "divi/widget", "synthetic/no-cell");
88
- assert.equal(r.applicabilityMissing, true);
89
- assert.equal(r.cellLevel, 0, "no invisible inheritance from the pattern");
90
- assert.equal(r.effectiveLevel, 0);
91
- assert.equal(r.effectiveLevelName, "UNVERIFIED");
92
- });
93
- test("gateWriteAttr passes when effective evidence clears the threshold", () => {
94
- const reg = syntheticRegistry();
95
- const r = gateWriteAttr(reg, "divi/widget", "synthetic/clears");
96
- assert.equal(r.effectiveLevel, 4);
97
- });
98
- test("gateWriteAttr refuses below threshold, naming attr + level + source", () => {
99
- const reg = syntheticRegistry();
100
- assert.throws(() => gateWriteAttr(reg, "divi/widget", "synthetic/cell-low"), (err) => {
101
- assert.ok(err instanceof EvidenceGateError);
102
- assert.match(err.message, /synthetic\/cell-low/, "names the attr family");
103
- assert.match(err.message, /SCHEMA_OBSERVED/, "names the effective level");
104
- assert.match(err.message, /docs\/synthetic\/cell-low-cell\.json/, "names the registry source");
105
- return true;
106
- });
107
- });
108
- test("gateWriteAttr refusal on missing applicability cites UNVERIFIED + absent cell", () => {
109
- const reg = syntheticRegistry();
110
- assert.throws(() => gateWriteAttr(reg, "divi/widget", "synthetic/no-cell"), (err) => {
111
- assert.ok(err instanceof EvidenceGateError);
112
- assert.match(err.message, /UNVERIFIED \(0\)/);
113
- assert.match(err.message, /absent from the registry entry/);
114
- return true;
115
- });
116
- });
117
- test("resolveEvidence throws for an entirely-absent pattern family", () => {
118
- const reg = syntheticRegistry();
119
- assert.throws(() => resolveEvidence(reg, "divi/widget", "synthetic/does-not-exist"), /absent from verified-attrs\.json/);
120
- });
121
- test("real registry: divi/button styling families clear the write threshold", () => {
122
- const reg = loadRegistry();
123
- assert.equal(writeThresholdNumber(reg), 4);
124
- for (const fam of [
125
- "divi/button.background",
126
- "divi/button.border",
127
- "divi/button.font",
128
- ]) {
129
- const r = resolveEvidence(reg, "divi/button", fam);
130
- assert.equal(r.effectiveLevel, 4, `${fam} must be at effective level 4 (VB_PRESET_STORAGE_VERIFIED)`);
131
- // gateWriteAttr must not throw.
132
- gateWriteAttr(reg, "divi/button", fam);
133
- }
134
- });
135
- test("variant-aware lookup selects the correct entry by pattern_variant", () => {
136
- // The real registry carries TWO `divi/font` entries that share a
137
- // pattern_family but differ on pattern_variant. resolveEvidence must
138
- // honor the variant — a Pattern A query must NOT match the Pattern B
139
- // entry (and vice versa), even though both share the `divi/font` family.
140
- const reg = loadRegistry();
141
- const a = resolveEvidence(reg, "divi/heading", "divi/font", "google_fonts_pattern_a");
142
- assert.equal(a.patternVariant, "google_fonts_pattern_a");
143
- assert.equal(a.effectiveLevel, 4, "Pattern A on divi/heading must clear VB_PRESET_STORAGE_VERIFIED in the shipped registry");
144
- const b = resolveEvidence(reg, "divi/heading", "divi/font", "local_hosted_pattern_b");
145
- assert.equal(b.patternVariant, "local_hosted_pattern_b");
146
- assert.equal(b.effectiveLevel, 4, "Pattern B on divi/heading must clear VB_PRESET_STORAGE_VERIFIED in the shipped registry");
147
- // A bogus variant under a known family throws — no silent fallback.
148
- assert.throws(() => resolveEvidence(reg, "divi/heading", "divi/font", "no_such_variant"), /absent from verified-attrs\.json/);
149
- });
150
- test("variant-aware lookup: divi/font-body Pattern A on divi/text clears the write threshold", () => {
151
- // Track 6 contract: the shipped registry MUST carry one cleared
152
- // `divi/font-body` + `google_fonts_pattern_a` entry on `divi/text`
153
- // (effective level = VB_PRESET_STORAGE_VERIFIED). If a registry edit
154
- // ever drops it, this canary fires.
155
- const reg = loadRegistry();
156
- const a = resolveEvidence(reg, "divi/text", "divi/font-body", "google_fonts_pattern_a");
157
- assert.equal(a.patternVariant, "google_fonts_pattern_a");
158
- assert.equal(a.effectiveLevel, 4, "divi/font-body Pattern A on divi/text must clear VB_PRESET_STORAGE_VERIFIED");
159
- // gateWriteAttr must not throw at this cell.
160
- gateWriteAttr(reg, "divi/text", "divi/font-body", "google_fonts_pattern_a");
161
- });
162
- test("variant-aware lookup: divi/font-body Pattern B is absent from the registry (refused)", () => {
163
- // Track 6 contract: the shipped registry MUST NOT carry a
164
- // `divi/font-body` + `local_hosted_pattern_b` entry. Pattern B for
165
- // body-text was deliberately deferred per round-2 _meta — there is no
166
- // captured evidence. resolveEvidence MUST throw the standard
167
- // registry-absence error.
168
- const reg = loadRegistry();
169
- assert.throws(() => resolveEvidence(reg, "divi/text", "divi/font-body", "local_hosted_pattern_b"), (err) => {
170
- assert.match(err.message, /absent from verified-attrs\.json/);
171
- assert.match(err.message, /divi\/font-body/);
172
- assert.match(err.message, /local_hosted_pattern_b/);
173
- return true;
174
- });
175
- });
@@ -1,20 +0,0 @@
1
- /**
2
- * `divi/spacing` section emitter shape + gating coverage (Track 7b).
3
- *
4
- * Pins the load-bearing shape contract from the Track 7a canonical
5
- * capture (`round-5-spacing-section.json`):
6
- * - Sparse-emit per axis (only user-touched corners emit; others absent);
7
- * - Paired sync flags per axis (BOTH `syncVertical` AND `syncHorizontal`
8
- * emit when an axis is touched; default `"off"` unless explicitly set);
9
- * - Padding and margin are INDEPENDENT bags (passing only padding flags
10
- * omits the margin bag entirely, and vice versa);
11
- * - `groupId: "designSpacing"` + `primaryAttrName: "module"` baked in
12
- * (NOT inferred from a dotted attr path — Track 7a load-bearing finding);
13
- * - `attrs`-only entry + request body (no `styleAttrs` / `renderAttrs` —
14
- * mirror happens at the route layer per Tracks 4/5/6 contract);
15
- * - Variable-token rejection on length flags (`$variable(...)` and bare
16
- * `gvid-*` / `gcid-*` forms) — deferred until canonical capture lands;
17
- * - Registry gate refusal for every non-section module (heading, text,
18
- * button — all SCHEMA_OBSERVED today).
19
- */
20
- export {};