@codyswann/lisa 3.46.3 → 3.46.4
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/dist/core/upstream-evidence-manifest.js +2 -2
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-design-intake/SKILL.md +29 -0
- package/plugins/lisa/rules/eager/design-value-binding.md +19 -1
- package/plugins/lisa/skills/lisa-design-intake/SKILL.md +29 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-design-intake/SKILL.md +29 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/rules/eager/design-value-binding.md +19 -1
- package/plugins/lisa-copilot/skills/lisa-design-intake/SKILL.md +29 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/rules/design-value-binding.mdc +19 -1
- package/plugins/lisa-cursor/skills/lisa-design-intake/SKILL.md +29 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/rules/eager/design-value-binding.md +19 -1
- package/plugins/src/base/skills/lisa-design-intake/SKILL.md +29 -0
|
@@ -384,7 +384,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
384
384
|
"plugins/src/base/rules/eager/dependency-trust-classes.md": "43a0932a7b8d4fd075b66bfe778025d808a13d9aa61ae793e9eb2fc8a555a381",
|
|
385
385
|
"plugins/src/base/rules/eager/derived-branch-plan.md": "2e4d5a142d7a33da3d01a6a8b3c79cf8b89769b9039c8672f1a14badba30d172",
|
|
386
386
|
"plugins/src/base/rules/eager/design-source-of-truth.md": "d85886aa5832f7952bc0768871a335f33a4899ce598bb1a114feb5f300cbc7f2",
|
|
387
|
-
"plugins/src/base/rules/eager/design-value-binding.md": "
|
|
387
|
+
"plugins/src/base/rules/eager/design-value-binding.md": "06c28a87f32860ce6fb17fdef90a59b7be0baf99cc28d7cf0299bcd8a5e46c44",
|
|
388
388
|
"plugins/src/base/rules/eager/do-it-now.md": "de0c1565fa7b8a99a7f5d15af1118c3e5eb942a0d893bbb672533149fb82688e",
|
|
389
389
|
"plugins/src/base/rules/eager/documentation-source-paths.md": "3a7c2a6264654a3cc44dd326441ba211a39c1c267033cc189c1c635adb84b52b",
|
|
390
390
|
"plugins/src/base/rules/eager/empirical-inquiry.md": "67aa64ba3ecc4f3cd761111d2562662e0a2acf7892703b14ec83f7f7b073e670",
|
|
@@ -515,7 +515,7 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
|
|
|
515
515
|
"plugins/src/base/skills/lisa-debrief-apply/SKILL.md": "7bff86a9e15cb63d58961dd5a47d0fbab44872f9bdcad04328788890f4521190",
|
|
516
516
|
"plugins/src/base/skills/lisa-debrief/SKILL.md": "6a0981107f907d3362ab0001f46412b76a70ec5ecc37ea1cd1def6ce4a38c9ee",
|
|
517
517
|
"plugins/src/base/skills/lisa-delivery-effectiveness/SKILL.md": "21bc55fa0e86a9694bd22269fd089dbfae0c54c199262f46a4955447acea0f35",
|
|
518
|
-
"plugins/src/base/skills/lisa-design-intake/SKILL.md": "
|
|
518
|
+
"plugins/src/base/skills/lisa-design-intake/SKILL.md": "8d0a72248d9d74b15aa31b0a51d13b77069677ea20117ddf93073d38c512ebf1",
|
|
519
519
|
"plugins/src/base/skills/lisa-detect-tooling/SKILL.md": "6ea2b1404d5c27f8a5bec8c90ad35ed0124789b70c091869455f538a2a9debbe",
|
|
520
520
|
"plugins/src/base/skills/lisa-detect-tooling/scripts/commands.mjs": "e28bd61fdde4a336b56ae3395423026943a5816015719d31d04f0d5a0ab50eec",
|
|
521
521
|
"plugins/src/base/skills/lisa-detect-tooling/scripts/detect-tooling.mjs": "d9419814fc04c935bd7e320e85b1977c9bfc9275d9ea5fb557002ad438ee7925",
|
package/package.json
CHANGED
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
"zod-validation-error": "^4.0.0"
|
|
137
137
|
},
|
|
138
138
|
"name": "@codyswann/lisa",
|
|
139
|
-
"version": "3.46.
|
|
139
|
+
"version": "3.46.4",
|
|
140
140
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
141
141
|
"main": "dist/index.js",
|
|
142
142
|
"exports": {
|
|
@@ -16,6 +16,32 @@ This skill carries the **judgment**. It does not carry the **policy** — that i
|
|
|
16
16
|
|
|
17
17
|
`design-source-of-truth` and `scripts/design-source-gate.mjs` already ask whether a changed surface **declares where its design came from**. This asks the orthogonal question: are the **values on it bound**. Both apply. A surface can cite a perfectly valid design node and still paint a literal that no variable backs — that is the case this skill exists for, and it is invisible to the other gate.
|
|
18
18
|
|
|
19
|
+
## Why this gate is executable and not advisory
|
|
20
|
+
|
|
21
|
+
Not a hypothesis about what agents might do under pressure. A measurement.
|
|
22
|
+
|
|
23
|
+
Eleven frontend work items in a portfolio frontend, each carrying node-scoped design references. The governing rule already said coverage below 100% is a **block routed back to design, never a value for the implementing agent to guess**, and said explicitly that `warn` is the wrong severity *because* it ships the item to an agent that then guesses or stalls. An agent had read that rule. It cited the rule in the briefing it wrote for its build agents. In that same briefing it instructed them to **"snap unbound dimensions via the snap tables (ties round down) and flag the rest."**
|
|
24
|
+
|
|
25
|
+
A block downgraded to a warning, by the agent enforcing it, inside the document enforcing it. Not from ignorance — from reasoning around it under delivery pressure, because eleven items were queued and blocking felt like not-shipping. It took a human restating the rule as an absolute for the instruction to be withdrawn.
|
|
26
|
+
|
|
27
|
+
Coverage measured after the withdrawal, on the subtrees actually being implemented:
|
|
28
|
+
|
|
29
|
+
| frames | colour | spacing | radius | reality |
|
|
30
|
+
|---|---|---|---|---|
|
|
31
|
+
| compliance screens | 96-100% | 82-97% | 88-93% | design-system composed |
|
|
32
|
+
| edit-KPI / target dialogs | **100%** | **0%** | **0%** | colours bound, geometry never bound |
|
|
33
|
+
| planning mockups | **1-4%** | **0-2%** | **0-3%** | hand-drawn, effectively unbound |
|
|
34
|
+
|
|
35
|
+
**Five of the eleven items blocked back to design; six proceeded.** The worst case is the clearest argument: one modal's subtree contained **zero variable references** — every colour a literal hex, every padding, gap, radius and size a literal, raw px type sizes with literal font faces, a literal shadow. Under snap-and-flag an agent would have produced a complete, lint-clean, tested component **in which every style value was invented**, and shipped it with a note. Five view files were written before it was stopped.
|
|
36
|
+
|
|
37
|
+
**The frame-vs-subtree distinction is what moved items between build and block**: 14 bound values at frame level, **zero** inside the modal actually being built. It is the finding most likely to be silently reimplemented wrong, which is why Phase 2 probes with `--node`.
|
|
38
|
+
|
|
39
|
+
The delivery-pressure reasoning was also wrong on its own terms. The non-visual half of the blocked work — domain types, adapters, CRUD, formatters — was completed and preserved. **Blocking cost far less than it appeared it would.**
|
|
40
|
+
|
|
41
|
+
And the rule holds once it is absolute rather than advisory: one agent withdrew a radius class it had already applied on finding `radius/none` was the only radius bound in its subtree; another refused to build an icon disc whose diameter and radius were both unbound rather than pick a size.
|
|
42
|
+
|
|
43
|
+
**A gate that depends on an agent choosing to honour it under delivery pressure is not a gate.** This one is executable and headless precisely because the judgment-based version demonstrably failed in the hands of an agent that had read the rule and agreed with it. That is also why the verdict belongs to `design-intake-gate.mjs` (Phase 4) and not to prose you re-reason each run.
|
|
44
|
+
|
|
19
45
|
## Phase 0 — Is there a design source at all?
|
|
20
46
|
|
|
21
47
|
**A design source is optional, and this is the most important step in the skill.** Most projects have no designs. Run the probe and read its verdict:
|
|
@@ -84,6 +110,8 @@ FIGMA_ACCESS_TOKEN=… FIGMA_MCP_TOKEN=… \
|
|
|
84
110
|
|
|
85
111
|
Pass `--dark` wherever the library has a dark mode: the light+dark signature is what separates variables that share a value, and without it more ids stay ambiguous. An ambiguous id is still a failure — guessing which variable a value came from is exactly what this contract forbids.
|
|
86
112
|
|
|
113
|
+
**An unknown id fails loudly naming the id; it never resolves to a nearest match.** That self-detecting staleness is the only reason a committed map is safe to trust — it makes a stale map an error instead of silent corruption. Never add a fallback that picks the closest variable, and never treat an unresolved id as unbound-and-therefore-design's.
|
|
114
|
+
|
|
87
115
|
## Phase 3 — Gather findings
|
|
88
116
|
|
|
89
117
|
The probe already produced `hardcoded-in-design` and `bound` findings for every value in the subtree, with the two silent-under-reporting traps handled: the corner-keyed `rectangleCornerRadii` shape is normalised, and boundness is read from `boundVariables` directly rather than inferred from a resolved value (Figma omits zero-valued properties, so a padding bound to a zero-valued variable would otherwise vanish).
|
|
@@ -159,6 +187,7 @@ The rule is never "do not look at pixels". It is **"do not derive a value from p
|
|
|
159
187
|
- **The regime is per-axis**, derived from the committed variable-id map. Never per-project, never asked of a human, and never from a live collection query — that route does not run headlessly.
|
|
160
188
|
- **A design source is optional.** No source, no token, or no map is SKIPPED at exit 0, never a block.
|
|
161
189
|
- **Every failure names an owner.** Unbound values are design's; a stale or ambiguous map is ours.
|
|
190
|
+
- **An unknown variable id fails loudly rather than resolving to a nearest match.** Staleness is self-detecting, which is what makes a committed map trustworthy at all.
|
|
162
191
|
- **The threshold is 100%.** Relax it only with an explicit `--min` on the command line, never by softening anything in code.
|
|
163
192
|
- **Never guess an escalation target**, and never proceed without one.
|
|
164
193
|
- **Never pick a side in a disagreement.** Which source is right is a design decision.
|
|
@@ -23,7 +23,7 @@ A library with a mature colour system and no spacing scale is the common case an
|
|
|
23
23
|
|
|
24
24
|
**Staleness is self-detecting, and that is what makes a committed map safe to trust.** An id the map has never seen fails loudly telling you to regenerate; it never silently resolves to the wrong variable.
|
|
25
25
|
|
|
26
|
-
**Measure the subtree you are implementing, not the enclosing screen.** A frame-level read counts the chrome behind a modal and over-reports — one measured work item scored 14 bound values at frame level and zero inside the modal subtree it actually had to build.
|
|
26
|
+
**Measure the subtree you are implementing, not the enclosing screen.** A frame-level read counts the chrome behind a modal and over-reports — one measured work item scored 14 bound values at frame level and zero inside the modal subtree it actually had to build. **That distinction is decision-changing, not a refinement**: applying it moved 5 of 11 real work items between build and block.
|
|
27
27
|
|
|
28
28
|
## Block on *unbound*, never on *unsure*
|
|
29
29
|
|
|
@@ -44,6 +44,24 @@ This distinction is the whole contract. "I cannot tell what they meant" is a jud
|
|
|
44
44
|
- Anything where a token exists and is bound — the happy path.
|
|
45
45
|
- **Aesthetic uncertainty.** If every value needed is bound and the agent merely finds the design ambiguous or ugly, that is an opinion, not a block.
|
|
46
46
|
|
|
47
|
+
## Why this is a block and not a warning — the measured case
|
|
48
|
+
|
|
49
|
+
An agent that had read this contract, and cited it in the briefing it wrote for its own build agents, instructed them in that same briefing to **"snap unbound dimensions via the snap tables (ties round down) and flag the rest."** A block downgraded to a warning, by the agent enforcing it, inside the document enforcing it — not from ignorance, but from reasoning around it under delivery pressure with eleven work items queued and blocking feeling like not-shipping. It took a human restating the rule as an absolute for the instruction to be withdrawn.
|
|
50
|
+
|
|
51
|
+
Coverage measured afterwards, on the subtrees actually being implemented:
|
|
52
|
+
|
|
53
|
+
| frames | colour | spacing | radius | reality |
|
|
54
|
+
|---|---|---|---|---|
|
|
55
|
+
| compliance screens | 96-100% | 82-97% | 88-93% | design-system composed |
|
|
56
|
+
| edit-KPI / target dialogs | **100%** | **0%** | **0%** | colours bound, geometry never bound |
|
|
57
|
+
| planning mockups | **1-4%** | **0-2%** | **0-3%** | hand-drawn, effectively unbound |
|
|
58
|
+
|
|
59
|
+
**Five of the eleven items blocked back to design; six proceeded.** One modal's subtree contained **zero variable references** — under snap-and-flag an agent would have produced a complete, lint-clean, tested component **in which every style value was invented**, and shipped it with a note. Five view files were written before it was stopped.
|
|
60
|
+
|
|
61
|
+
Blocking cost far less than it appeared it would: the non-visual half of the blocked work — domain types, adapters, CRUD, formatters — was completed and preserved. That is the answer to the delivery-pressure reasoning that produced the softening. And the contract holds once it is absolute rather than advisory: one agent withdrew a radius class it had already applied on finding `radius/none` was the only radius bound in its subtree; another refused to build an icon disc whose diameter and radius were both unbound rather than pick a size.
|
|
62
|
+
|
|
63
|
+
**A gate that depends on an agent choosing to honour it under delivery pressure is not a gate.** That is why this contract has an executable, headless rung at all — the judgment-based version demonstrably failed in the hands of an agent that had read the rule and agreed with it.
|
|
64
|
+
|
|
47
65
|
## What visual matching is for, precisely
|
|
48
66
|
|
|
49
67
|
In a **typed** axis it is *verification*: build from the variable, screenshot, confirm agreement — and a disagreement is condition 5, not a licence to trust the pixels. In an **untyped** axis it is *derivation*, and legitimate. The rule is never "do not look at pixels"; it is **"do not derive a value from pixels when a binding exists."**
|
|
@@ -16,6 +16,32 @@ This skill carries the **judgment**. It does not carry the **policy** — that i
|
|
|
16
16
|
|
|
17
17
|
`design-source-of-truth` and `scripts/design-source-gate.mjs` already ask whether a changed surface **declares where its design came from**. This asks the orthogonal question: are the **values on it bound**. Both apply. A surface can cite a perfectly valid design node and still paint a literal that no variable backs — that is the case this skill exists for, and it is invisible to the other gate.
|
|
18
18
|
|
|
19
|
+
## Why this gate is executable and not advisory
|
|
20
|
+
|
|
21
|
+
Not a hypothesis about what agents might do under pressure. A measurement.
|
|
22
|
+
|
|
23
|
+
Eleven frontend work items in a portfolio frontend, each carrying node-scoped design references. The governing rule already said coverage below 100% is a **block routed back to design, never a value for the implementing agent to guess**, and said explicitly that `warn` is the wrong severity *because* it ships the item to an agent that then guesses or stalls. An agent had read that rule. It cited the rule in the briefing it wrote for its build agents. In that same briefing it instructed them to **"snap unbound dimensions via the snap tables (ties round down) and flag the rest."**
|
|
24
|
+
|
|
25
|
+
A block downgraded to a warning, by the agent enforcing it, inside the document enforcing it. Not from ignorance — from reasoning around it under delivery pressure, because eleven items were queued and blocking felt like not-shipping. It took a human restating the rule as an absolute for the instruction to be withdrawn.
|
|
26
|
+
|
|
27
|
+
Coverage measured after the withdrawal, on the subtrees actually being implemented:
|
|
28
|
+
|
|
29
|
+
| frames | colour | spacing | radius | reality |
|
|
30
|
+
|---|---|---|---|---|
|
|
31
|
+
| compliance screens | 96-100% | 82-97% | 88-93% | design-system composed |
|
|
32
|
+
| edit-KPI / target dialogs | **100%** | **0%** | **0%** | colours bound, geometry never bound |
|
|
33
|
+
| planning mockups | **1-4%** | **0-2%** | **0-3%** | hand-drawn, effectively unbound |
|
|
34
|
+
|
|
35
|
+
**Five of the eleven items blocked back to design; six proceeded.** The worst case is the clearest argument: one modal's subtree contained **zero variable references** — every colour a literal hex, every padding, gap, radius and size a literal, raw px type sizes with literal font faces, a literal shadow. Under snap-and-flag an agent would have produced a complete, lint-clean, tested component **in which every style value was invented**, and shipped it with a note. Five view files were written before it was stopped.
|
|
36
|
+
|
|
37
|
+
**The frame-vs-subtree distinction is what moved items between build and block**: 14 bound values at frame level, **zero** inside the modal actually being built. It is the finding most likely to be silently reimplemented wrong, which is why Phase 2 probes with `--node`.
|
|
38
|
+
|
|
39
|
+
The delivery-pressure reasoning was also wrong on its own terms. The non-visual half of the blocked work — domain types, adapters, CRUD, formatters — was completed and preserved. **Blocking cost far less than it appeared it would.**
|
|
40
|
+
|
|
41
|
+
And the rule holds once it is absolute rather than advisory: one agent withdrew a radius class it had already applied on finding `radius/none` was the only radius bound in its subtree; another refused to build an icon disc whose diameter and radius were both unbound rather than pick a size.
|
|
42
|
+
|
|
43
|
+
**A gate that depends on an agent choosing to honour it under delivery pressure is not a gate.** This one is executable and headless precisely because the judgment-based version demonstrably failed in the hands of an agent that had read the rule and agreed with it. That is also why the verdict belongs to `design-intake-gate.mjs` (Phase 4) and not to prose you re-reason each run.
|
|
44
|
+
|
|
19
45
|
## Phase 0 — Is there a design source at all?
|
|
20
46
|
|
|
21
47
|
**A design source is optional, and this is the most important step in the skill.** Most projects have no designs. Run the probe and read its verdict:
|
|
@@ -84,6 +110,8 @@ FIGMA_ACCESS_TOKEN=… FIGMA_MCP_TOKEN=… \
|
|
|
84
110
|
|
|
85
111
|
Pass `--dark` wherever the library has a dark mode: the light+dark signature is what separates variables that share a value, and without it more ids stay ambiguous. An ambiguous id is still a failure — guessing which variable a value came from is exactly what this contract forbids.
|
|
86
112
|
|
|
113
|
+
**An unknown id fails loudly naming the id; it never resolves to a nearest match.** That self-detecting staleness is the only reason a committed map is safe to trust — it makes a stale map an error instead of silent corruption. Never add a fallback that picks the closest variable, and never treat an unresolved id as unbound-and-therefore-design's.
|
|
114
|
+
|
|
87
115
|
## Phase 3 — Gather findings
|
|
88
116
|
|
|
89
117
|
The probe already produced `hardcoded-in-design` and `bound` findings for every value in the subtree, with the two silent-under-reporting traps handled: the corner-keyed `rectangleCornerRadii` shape is normalised, and boundness is read from `boundVariables` directly rather than inferred from a resolved value (Figma omits zero-valued properties, so a padding bound to a zero-valued variable would otherwise vanish).
|
|
@@ -159,6 +187,7 @@ The rule is never "do not look at pixels". It is **"do not derive a value from p
|
|
|
159
187
|
- **The regime is per-axis**, derived from the committed variable-id map. Never per-project, never asked of a human, and never from a live collection query — that route does not run headlessly.
|
|
160
188
|
- **A design source is optional.** No source, no token, or no map is SKIPPED at exit 0, never a block.
|
|
161
189
|
- **Every failure names an owner.** Unbound values are design's; a stale or ambiguous map is ours.
|
|
190
|
+
- **An unknown variable id fails loudly rather than resolving to a nearest match.** Staleness is self-detecting, which is what makes a committed map trustworthy at all.
|
|
162
191
|
- **The threshold is 100%.** Relax it only with an explicit `--min` on the command line, never by softening anything in code.
|
|
163
192
|
- **Never guess an escalation target**, and never proceed without one.
|
|
164
193
|
- **Never pick a side in a disagreement.** Which source is right is a design decision.
|
|
@@ -16,6 +16,32 @@ This skill carries the **judgment**. It does not carry the **policy** — that i
|
|
|
16
16
|
|
|
17
17
|
`design-source-of-truth` and `scripts/design-source-gate.mjs` already ask whether a changed surface **declares where its design came from**. This asks the orthogonal question: are the **values on it bound**. Both apply. A surface can cite a perfectly valid design node and still paint a literal that no variable backs — that is the case this skill exists for, and it is invisible to the other gate.
|
|
18
18
|
|
|
19
|
+
## Why this gate is executable and not advisory
|
|
20
|
+
|
|
21
|
+
Not a hypothesis about what agents might do under pressure. A measurement.
|
|
22
|
+
|
|
23
|
+
Eleven frontend work items in a portfolio frontend, each carrying node-scoped design references. The governing rule already said coverage below 100% is a **block routed back to design, never a value for the implementing agent to guess**, and said explicitly that `warn` is the wrong severity *because* it ships the item to an agent that then guesses or stalls. An agent had read that rule. It cited the rule in the briefing it wrote for its build agents. In that same briefing it instructed them to **"snap unbound dimensions via the snap tables (ties round down) and flag the rest."**
|
|
24
|
+
|
|
25
|
+
A block downgraded to a warning, by the agent enforcing it, inside the document enforcing it. Not from ignorance — from reasoning around it under delivery pressure, because eleven items were queued and blocking felt like not-shipping. It took a human restating the rule as an absolute for the instruction to be withdrawn.
|
|
26
|
+
|
|
27
|
+
Coverage measured after the withdrawal, on the subtrees actually being implemented:
|
|
28
|
+
|
|
29
|
+
| frames | colour | spacing | radius | reality |
|
|
30
|
+
|---|---|---|---|---|
|
|
31
|
+
| compliance screens | 96-100% | 82-97% | 88-93% | design-system composed |
|
|
32
|
+
| edit-KPI / target dialogs | **100%** | **0%** | **0%** | colours bound, geometry never bound |
|
|
33
|
+
| planning mockups | **1-4%** | **0-2%** | **0-3%** | hand-drawn, effectively unbound |
|
|
34
|
+
|
|
35
|
+
**Five of the eleven items blocked back to design; six proceeded.** The worst case is the clearest argument: one modal's subtree contained **zero variable references** — every colour a literal hex, every padding, gap, radius and size a literal, raw px type sizes with literal font faces, a literal shadow. Under snap-and-flag an agent would have produced a complete, lint-clean, tested component **in which every style value was invented**, and shipped it with a note. Five view files were written before it was stopped.
|
|
36
|
+
|
|
37
|
+
**The frame-vs-subtree distinction is what moved items between build and block**: 14 bound values at frame level, **zero** inside the modal actually being built. It is the finding most likely to be silently reimplemented wrong, which is why Phase 2 probes with `--node`.
|
|
38
|
+
|
|
39
|
+
The delivery-pressure reasoning was also wrong on its own terms. The non-visual half of the blocked work — domain types, adapters, CRUD, formatters — was completed and preserved. **Blocking cost far less than it appeared it would.**
|
|
40
|
+
|
|
41
|
+
And the rule holds once it is absolute rather than advisory: one agent withdrew a radius class it had already applied on finding `radius/none` was the only radius bound in its subtree; another refused to build an icon disc whose diameter and radius were both unbound rather than pick a size.
|
|
42
|
+
|
|
43
|
+
**A gate that depends on an agent choosing to honour it under delivery pressure is not a gate.** This one is executable and headless precisely because the judgment-based version demonstrably failed in the hands of an agent that had read the rule and agreed with it. That is also why the verdict belongs to `design-intake-gate.mjs` (Phase 4) and not to prose you re-reason each run.
|
|
44
|
+
|
|
19
45
|
## Phase 0 — Is there a design source at all?
|
|
20
46
|
|
|
21
47
|
**A design source is optional, and this is the most important step in the skill.** Most projects have no designs. Run the probe and read its verdict:
|
|
@@ -84,6 +110,8 @@ FIGMA_ACCESS_TOKEN=… FIGMA_MCP_TOKEN=… \
|
|
|
84
110
|
|
|
85
111
|
Pass `--dark` wherever the library has a dark mode: the light+dark signature is what separates variables that share a value, and without it more ids stay ambiguous. An ambiguous id is still a failure — guessing which variable a value came from is exactly what this contract forbids.
|
|
86
112
|
|
|
113
|
+
**An unknown id fails loudly naming the id; it never resolves to a nearest match.** That self-detecting staleness is the only reason a committed map is safe to trust — it makes a stale map an error instead of silent corruption. Never add a fallback that picks the closest variable, and never treat an unresolved id as unbound-and-therefore-design's.
|
|
114
|
+
|
|
87
115
|
## Phase 3 — Gather findings
|
|
88
116
|
|
|
89
117
|
The probe already produced `hardcoded-in-design` and `bound` findings for every value in the subtree, with the two silent-under-reporting traps handled: the corner-keyed `rectangleCornerRadii` shape is normalised, and boundness is read from `boundVariables` directly rather than inferred from a resolved value (Figma omits zero-valued properties, so a padding bound to a zero-valued variable would otherwise vanish).
|
|
@@ -159,6 +187,7 @@ The rule is never "do not look at pixels". It is **"do not derive a value from p
|
|
|
159
187
|
- **The regime is per-axis**, derived from the committed variable-id map. Never per-project, never asked of a human, and never from a live collection query — that route does not run headlessly.
|
|
160
188
|
- **A design source is optional.** No source, no token, or no map is SKIPPED at exit 0, never a block.
|
|
161
189
|
- **Every failure names an owner.** Unbound values are design's; a stale or ambiguous map is ours.
|
|
190
|
+
- **An unknown variable id fails loudly rather than resolving to a nearest match.** Staleness is self-detecting, which is what makes a committed map trustworthy at all.
|
|
162
191
|
- **The threshold is 100%.** Relax it only with an explicit `--min` on the command line, never by softening anything in code.
|
|
163
192
|
- **Never guess an escalation target**, and never proceed without one.
|
|
164
193
|
- **Never pick a side in a disagreement.** Which source is right is a design decision.
|
|
@@ -23,7 +23,7 @@ A library with a mature colour system and no spacing scale is the common case an
|
|
|
23
23
|
|
|
24
24
|
**Staleness is self-detecting, and that is what makes a committed map safe to trust.** An id the map has never seen fails loudly telling you to regenerate; it never silently resolves to the wrong variable.
|
|
25
25
|
|
|
26
|
-
**Measure the subtree you are implementing, not the enclosing screen.** A frame-level read counts the chrome behind a modal and over-reports — one measured work item scored 14 bound values at frame level and zero inside the modal subtree it actually had to build.
|
|
26
|
+
**Measure the subtree you are implementing, not the enclosing screen.** A frame-level read counts the chrome behind a modal and over-reports — one measured work item scored 14 bound values at frame level and zero inside the modal subtree it actually had to build. **That distinction is decision-changing, not a refinement**: applying it moved 5 of 11 real work items between build and block.
|
|
27
27
|
|
|
28
28
|
## Block on *unbound*, never on *unsure*
|
|
29
29
|
|
|
@@ -44,6 +44,24 @@ This distinction is the whole contract. "I cannot tell what they meant" is a jud
|
|
|
44
44
|
- Anything where a token exists and is bound — the happy path.
|
|
45
45
|
- **Aesthetic uncertainty.** If every value needed is bound and the agent merely finds the design ambiguous or ugly, that is an opinion, not a block.
|
|
46
46
|
|
|
47
|
+
## Why this is a block and not a warning — the measured case
|
|
48
|
+
|
|
49
|
+
An agent that had read this contract, and cited it in the briefing it wrote for its own build agents, instructed them in that same briefing to **"snap unbound dimensions via the snap tables (ties round down) and flag the rest."** A block downgraded to a warning, by the agent enforcing it, inside the document enforcing it — not from ignorance, but from reasoning around it under delivery pressure with eleven work items queued and blocking feeling like not-shipping. It took a human restating the rule as an absolute for the instruction to be withdrawn.
|
|
50
|
+
|
|
51
|
+
Coverage measured afterwards, on the subtrees actually being implemented:
|
|
52
|
+
|
|
53
|
+
| frames | colour | spacing | radius | reality |
|
|
54
|
+
|---|---|---|---|---|
|
|
55
|
+
| compliance screens | 96-100% | 82-97% | 88-93% | design-system composed |
|
|
56
|
+
| edit-KPI / target dialogs | **100%** | **0%** | **0%** | colours bound, geometry never bound |
|
|
57
|
+
| planning mockups | **1-4%** | **0-2%** | **0-3%** | hand-drawn, effectively unbound |
|
|
58
|
+
|
|
59
|
+
**Five of the eleven items blocked back to design; six proceeded.** One modal's subtree contained **zero variable references** — under snap-and-flag an agent would have produced a complete, lint-clean, tested component **in which every style value was invented**, and shipped it with a note. Five view files were written before it was stopped.
|
|
60
|
+
|
|
61
|
+
Blocking cost far less than it appeared it would: the non-visual half of the blocked work — domain types, adapters, CRUD, formatters — was completed and preserved. That is the answer to the delivery-pressure reasoning that produced the softening. And the contract holds once it is absolute rather than advisory: one agent withdrew a radius class it had already applied on finding `radius/none` was the only radius bound in its subtree; another refused to build an icon disc whose diameter and radius were both unbound rather than pick a size.
|
|
62
|
+
|
|
63
|
+
**A gate that depends on an agent choosing to honour it under delivery pressure is not a gate.** That is why this contract has an executable, headless rung at all — the judgment-based version demonstrably failed in the hands of an agent that had read the rule and agreed with it.
|
|
64
|
+
|
|
47
65
|
## What visual matching is for, precisely
|
|
48
66
|
|
|
49
67
|
In a **typed** axis it is *verification*: build from the variable, screenshot, confirm agreement — and a disagreement is condition 5, not a licence to trust the pixels. In an **untyped** axis it is *derivation*, and legitimate. The rule is never "do not look at pixels"; it is **"do not derive a value from pixels when a binding exists."**
|
|
@@ -16,6 +16,32 @@ This skill carries the **judgment**. It does not carry the **policy** — that i
|
|
|
16
16
|
|
|
17
17
|
`design-source-of-truth` and `scripts/design-source-gate.mjs` already ask whether a changed surface **declares where its design came from**. This asks the orthogonal question: are the **values on it bound**. Both apply. A surface can cite a perfectly valid design node and still paint a literal that no variable backs — that is the case this skill exists for, and it is invisible to the other gate.
|
|
18
18
|
|
|
19
|
+
## Why this gate is executable and not advisory
|
|
20
|
+
|
|
21
|
+
Not a hypothesis about what agents might do under pressure. A measurement.
|
|
22
|
+
|
|
23
|
+
Eleven frontend work items in a portfolio frontend, each carrying node-scoped design references. The governing rule already said coverage below 100% is a **block routed back to design, never a value for the implementing agent to guess**, and said explicitly that `warn` is the wrong severity *because* it ships the item to an agent that then guesses or stalls. An agent had read that rule. It cited the rule in the briefing it wrote for its build agents. In that same briefing it instructed them to **"snap unbound dimensions via the snap tables (ties round down) and flag the rest."**
|
|
24
|
+
|
|
25
|
+
A block downgraded to a warning, by the agent enforcing it, inside the document enforcing it. Not from ignorance — from reasoning around it under delivery pressure, because eleven items were queued and blocking felt like not-shipping. It took a human restating the rule as an absolute for the instruction to be withdrawn.
|
|
26
|
+
|
|
27
|
+
Coverage measured after the withdrawal, on the subtrees actually being implemented:
|
|
28
|
+
|
|
29
|
+
| frames | colour | spacing | radius | reality |
|
|
30
|
+
|---|---|---|---|---|
|
|
31
|
+
| compliance screens | 96-100% | 82-97% | 88-93% | design-system composed |
|
|
32
|
+
| edit-KPI / target dialogs | **100%** | **0%** | **0%** | colours bound, geometry never bound |
|
|
33
|
+
| planning mockups | **1-4%** | **0-2%** | **0-3%** | hand-drawn, effectively unbound |
|
|
34
|
+
|
|
35
|
+
**Five of the eleven items blocked back to design; six proceeded.** The worst case is the clearest argument: one modal's subtree contained **zero variable references** — every colour a literal hex, every padding, gap, radius and size a literal, raw px type sizes with literal font faces, a literal shadow. Under snap-and-flag an agent would have produced a complete, lint-clean, tested component **in which every style value was invented**, and shipped it with a note. Five view files were written before it was stopped.
|
|
36
|
+
|
|
37
|
+
**The frame-vs-subtree distinction is what moved items between build and block**: 14 bound values at frame level, **zero** inside the modal actually being built. It is the finding most likely to be silently reimplemented wrong, which is why Phase 2 probes with `--node`.
|
|
38
|
+
|
|
39
|
+
The delivery-pressure reasoning was also wrong on its own terms. The non-visual half of the blocked work — domain types, adapters, CRUD, formatters — was completed and preserved. **Blocking cost far less than it appeared it would.**
|
|
40
|
+
|
|
41
|
+
And the rule holds once it is absolute rather than advisory: one agent withdrew a radius class it had already applied on finding `radius/none` was the only radius bound in its subtree; another refused to build an icon disc whose diameter and radius were both unbound rather than pick a size.
|
|
42
|
+
|
|
43
|
+
**A gate that depends on an agent choosing to honour it under delivery pressure is not a gate.** This one is executable and headless precisely because the judgment-based version demonstrably failed in the hands of an agent that had read the rule and agreed with it. That is also why the verdict belongs to `design-intake-gate.mjs` (Phase 4) and not to prose you re-reason each run.
|
|
44
|
+
|
|
19
45
|
## Phase 0 — Is there a design source at all?
|
|
20
46
|
|
|
21
47
|
**A design source is optional, and this is the most important step in the skill.** Most projects have no designs. Run the probe and read its verdict:
|
|
@@ -84,6 +110,8 @@ FIGMA_ACCESS_TOKEN=… FIGMA_MCP_TOKEN=… \
|
|
|
84
110
|
|
|
85
111
|
Pass `--dark` wherever the library has a dark mode: the light+dark signature is what separates variables that share a value, and without it more ids stay ambiguous. An ambiguous id is still a failure — guessing which variable a value came from is exactly what this contract forbids.
|
|
86
112
|
|
|
113
|
+
**An unknown id fails loudly naming the id; it never resolves to a nearest match.** That self-detecting staleness is the only reason a committed map is safe to trust — it makes a stale map an error instead of silent corruption. Never add a fallback that picks the closest variable, and never treat an unresolved id as unbound-and-therefore-design's.
|
|
114
|
+
|
|
87
115
|
## Phase 3 — Gather findings
|
|
88
116
|
|
|
89
117
|
The probe already produced `hardcoded-in-design` and `bound` findings for every value in the subtree, with the two silent-under-reporting traps handled: the corner-keyed `rectangleCornerRadii` shape is normalised, and boundness is read from `boundVariables` directly rather than inferred from a resolved value (Figma omits zero-valued properties, so a padding bound to a zero-valued variable would otherwise vanish).
|
|
@@ -159,6 +187,7 @@ The rule is never "do not look at pixels". It is **"do not derive a value from p
|
|
|
159
187
|
- **The regime is per-axis**, derived from the committed variable-id map. Never per-project, never asked of a human, and never from a live collection query — that route does not run headlessly.
|
|
160
188
|
- **A design source is optional.** No source, no token, or no map is SKIPPED at exit 0, never a block.
|
|
161
189
|
- **Every failure names an owner.** Unbound values are design's; a stale or ambiguous map is ours.
|
|
190
|
+
- **An unknown variable id fails loudly rather than resolving to a nearest match.** Staleness is self-detecting, which is what makes a committed map trustworthy at all.
|
|
162
191
|
- **The threshold is 100%.** Relax it only with an explicit `--min` on the command line, never by softening anything in code.
|
|
163
192
|
- **Never guess an escalation target**, and never proceed without one.
|
|
164
193
|
- **Never pick a side in a disagreement.** Which source is right is a design decision.
|
|
@@ -28,7 +28,7 @@ A library with a mature colour system and no spacing scale is the common case an
|
|
|
28
28
|
|
|
29
29
|
**Staleness is self-detecting, and that is what makes a committed map safe to trust.** An id the map has never seen fails loudly telling you to regenerate; it never silently resolves to the wrong variable.
|
|
30
30
|
|
|
31
|
-
**Measure the subtree you are implementing, not the enclosing screen.** A frame-level read counts the chrome behind a modal and over-reports — one measured work item scored 14 bound values at frame level and zero inside the modal subtree it actually had to build.
|
|
31
|
+
**Measure the subtree you are implementing, not the enclosing screen.** A frame-level read counts the chrome behind a modal and over-reports — one measured work item scored 14 bound values at frame level and zero inside the modal subtree it actually had to build. **That distinction is decision-changing, not a refinement**: applying it moved 5 of 11 real work items between build and block.
|
|
32
32
|
|
|
33
33
|
## Block on *unbound*, never on *unsure*
|
|
34
34
|
|
|
@@ -49,6 +49,24 @@ This distinction is the whole contract. "I cannot tell what they meant" is a jud
|
|
|
49
49
|
- Anything where a token exists and is bound — the happy path.
|
|
50
50
|
- **Aesthetic uncertainty.** If every value needed is bound and the agent merely finds the design ambiguous or ugly, that is an opinion, not a block.
|
|
51
51
|
|
|
52
|
+
## Why this is a block and not a warning — the measured case
|
|
53
|
+
|
|
54
|
+
An agent that had read this contract, and cited it in the briefing it wrote for its own build agents, instructed them in that same briefing to **"snap unbound dimensions via the snap tables (ties round down) and flag the rest."** A block downgraded to a warning, by the agent enforcing it, inside the document enforcing it — not from ignorance, but from reasoning around it under delivery pressure with eleven work items queued and blocking feeling like not-shipping. It took a human restating the rule as an absolute for the instruction to be withdrawn.
|
|
55
|
+
|
|
56
|
+
Coverage measured afterwards, on the subtrees actually being implemented:
|
|
57
|
+
|
|
58
|
+
| frames | colour | spacing | radius | reality |
|
|
59
|
+
|---|---|---|---|---|
|
|
60
|
+
| compliance screens | 96-100% | 82-97% | 88-93% | design-system composed |
|
|
61
|
+
| edit-KPI / target dialogs | **100%** | **0%** | **0%** | colours bound, geometry never bound |
|
|
62
|
+
| planning mockups | **1-4%** | **0-2%** | **0-3%** | hand-drawn, effectively unbound |
|
|
63
|
+
|
|
64
|
+
**Five of the eleven items blocked back to design; six proceeded.** One modal's subtree contained **zero variable references** — under snap-and-flag an agent would have produced a complete, lint-clean, tested component **in which every style value was invented**, and shipped it with a note. Five view files were written before it was stopped.
|
|
65
|
+
|
|
66
|
+
Blocking cost far less than it appeared it would: the non-visual half of the blocked work — domain types, adapters, CRUD, formatters — was completed and preserved. That is the answer to the delivery-pressure reasoning that produced the softening. And the contract holds once it is absolute rather than advisory: one agent withdrew a radius class it had already applied on finding `radius/none` was the only radius bound in its subtree; another refused to build an icon disc whose diameter and radius were both unbound rather than pick a size.
|
|
67
|
+
|
|
68
|
+
**A gate that depends on an agent choosing to honour it under delivery pressure is not a gate.** That is why this contract has an executable, headless rung at all — the judgment-based version demonstrably failed in the hands of an agent that had read the rule and agreed with it.
|
|
69
|
+
|
|
52
70
|
## What visual matching is for, precisely
|
|
53
71
|
|
|
54
72
|
In a **typed** axis it is *verification*: build from the variable, screenshot, confirm agreement — and a disagreement is condition 5, not a licence to trust the pixels. In an **untyped** axis it is *derivation*, and legitimate. The rule is never "do not look at pixels"; it is **"do not derive a value from pixels when a binding exists."**
|
|
@@ -16,6 +16,32 @@ This skill carries the **judgment**. It does not carry the **policy** — that i
|
|
|
16
16
|
|
|
17
17
|
`design-source-of-truth` and `scripts/design-source-gate.mjs` already ask whether a changed surface **declares where its design came from**. This asks the orthogonal question: are the **values on it bound**. Both apply. A surface can cite a perfectly valid design node and still paint a literal that no variable backs — that is the case this skill exists for, and it is invisible to the other gate.
|
|
18
18
|
|
|
19
|
+
## Why this gate is executable and not advisory
|
|
20
|
+
|
|
21
|
+
Not a hypothesis about what agents might do under pressure. A measurement.
|
|
22
|
+
|
|
23
|
+
Eleven frontend work items in a portfolio frontend, each carrying node-scoped design references. The governing rule already said coverage below 100% is a **block routed back to design, never a value for the implementing agent to guess**, and said explicitly that `warn` is the wrong severity *because* it ships the item to an agent that then guesses or stalls. An agent had read that rule. It cited the rule in the briefing it wrote for its build agents. In that same briefing it instructed them to **"snap unbound dimensions via the snap tables (ties round down) and flag the rest."**
|
|
24
|
+
|
|
25
|
+
A block downgraded to a warning, by the agent enforcing it, inside the document enforcing it. Not from ignorance — from reasoning around it under delivery pressure, because eleven items were queued and blocking felt like not-shipping. It took a human restating the rule as an absolute for the instruction to be withdrawn.
|
|
26
|
+
|
|
27
|
+
Coverage measured after the withdrawal, on the subtrees actually being implemented:
|
|
28
|
+
|
|
29
|
+
| frames | colour | spacing | radius | reality |
|
|
30
|
+
|---|---|---|---|---|
|
|
31
|
+
| compliance screens | 96-100% | 82-97% | 88-93% | design-system composed |
|
|
32
|
+
| edit-KPI / target dialogs | **100%** | **0%** | **0%** | colours bound, geometry never bound |
|
|
33
|
+
| planning mockups | **1-4%** | **0-2%** | **0-3%** | hand-drawn, effectively unbound |
|
|
34
|
+
|
|
35
|
+
**Five of the eleven items blocked back to design; six proceeded.** The worst case is the clearest argument: one modal's subtree contained **zero variable references** — every colour a literal hex, every padding, gap, radius and size a literal, raw px type sizes with literal font faces, a literal shadow. Under snap-and-flag an agent would have produced a complete, lint-clean, tested component **in which every style value was invented**, and shipped it with a note. Five view files were written before it was stopped.
|
|
36
|
+
|
|
37
|
+
**The frame-vs-subtree distinction is what moved items between build and block**: 14 bound values at frame level, **zero** inside the modal actually being built. It is the finding most likely to be silently reimplemented wrong, which is why Phase 2 probes with `--node`.
|
|
38
|
+
|
|
39
|
+
The delivery-pressure reasoning was also wrong on its own terms. The non-visual half of the blocked work — domain types, adapters, CRUD, formatters — was completed and preserved. **Blocking cost far less than it appeared it would.**
|
|
40
|
+
|
|
41
|
+
And the rule holds once it is absolute rather than advisory: one agent withdrew a radius class it had already applied on finding `radius/none` was the only radius bound in its subtree; another refused to build an icon disc whose diameter and radius were both unbound rather than pick a size.
|
|
42
|
+
|
|
43
|
+
**A gate that depends on an agent choosing to honour it under delivery pressure is not a gate.** This one is executable and headless precisely because the judgment-based version demonstrably failed in the hands of an agent that had read the rule and agreed with it. That is also why the verdict belongs to `design-intake-gate.mjs` (Phase 4) and not to prose you re-reason each run.
|
|
44
|
+
|
|
19
45
|
## Phase 0 — Is there a design source at all?
|
|
20
46
|
|
|
21
47
|
**A design source is optional, and this is the most important step in the skill.** Most projects have no designs. Run the probe and read its verdict:
|
|
@@ -84,6 +110,8 @@ FIGMA_ACCESS_TOKEN=… FIGMA_MCP_TOKEN=… \
|
|
|
84
110
|
|
|
85
111
|
Pass `--dark` wherever the library has a dark mode: the light+dark signature is what separates variables that share a value, and without it more ids stay ambiguous. An ambiguous id is still a failure — guessing which variable a value came from is exactly what this contract forbids.
|
|
86
112
|
|
|
113
|
+
**An unknown id fails loudly naming the id; it never resolves to a nearest match.** That self-detecting staleness is the only reason a committed map is safe to trust — it makes a stale map an error instead of silent corruption. Never add a fallback that picks the closest variable, and never treat an unresolved id as unbound-and-therefore-design's.
|
|
114
|
+
|
|
87
115
|
## Phase 3 — Gather findings
|
|
88
116
|
|
|
89
117
|
The probe already produced `hardcoded-in-design` and `bound` findings for every value in the subtree, with the two silent-under-reporting traps handled: the corner-keyed `rectangleCornerRadii` shape is normalised, and boundness is read from `boundVariables` directly rather than inferred from a resolved value (Figma omits zero-valued properties, so a padding bound to a zero-valued variable would otherwise vanish).
|
|
@@ -159,6 +187,7 @@ The rule is never "do not look at pixels". It is **"do not derive a value from p
|
|
|
159
187
|
- **The regime is per-axis**, derived from the committed variable-id map. Never per-project, never asked of a human, and never from a live collection query — that route does not run headlessly.
|
|
160
188
|
- **A design source is optional.** No source, no token, or no map is SKIPPED at exit 0, never a block.
|
|
161
189
|
- **Every failure names an owner.** Unbound values are design's; a stale or ambiguous map is ours.
|
|
190
|
+
- **An unknown variable id fails loudly rather than resolving to a nearest match.** Staleness is self-detecting, which is what makes a committed map trustworthy at all.
|
|
162
191
|
- **The threshold is 100%.** Relax it only with an explicit `--min` on the command line, never by softening anything in code.
|
|
163
192
|
- **Never guess an escalation target**, and never proceed without one.
|
|
164
193
|
- **Never pick a side in a disagreement.** Which source is right is a design decision.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.46.
|
|
3
|
+
"version": "3.46.4",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.46.
|
|
3
|
+
"version": "3.46.4",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.46.
|
|
3
|
+
"version": "3.46.4",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.46.
|
|
3
|
+
"version": "3.46.4",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.46.
|
|
3
|
+
"version": "3.46.4",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -23,7 +23,7 @@ A library with a mature colour system and no spacing scale is the common case an
|
|
|
23
23
|
|
|
24
24
|
**Staleness is self-detecting, and that is what makes a committed map safe to trust.** An id the map has never seen fails loudly telling you to regenerate; it never silently resolves to the wrong variable.
|
|
25
25
|
|
|
26
|
-
**Measure the subtree you are implementing, not the enclosing screen.** A frame-level read counts the chrome behind a modal and over-reports — one measured work item scored 14 bound values at frame level and zero inside the modal subtree it actually had to build.
|
|
26
|
+
**Measure the subtree you are implementing, not the enclosing screen.** A frame-level read counts the chrome behind a modal and over-reports — one measured work item scored 14 bound values at frame level and zero inside the modal subtree it actually had to build. **That distinction is decision-changing, not a refinement**: applying it moved 5 of 11 real work items between build and block.
|
|
27
27
|
|
|
28
28
|
## Block on *unbound*, never on *unsure*
|
|
29
29
|
|
|
@@ -44,6 +44,24 @@ This distinction is the whole contract. "I cannot tell what they meant" is a jud
|
|
|
44
44
|
- Anything where a token exists and is bound — the happy path.
|
|
45
45
|
- **Aesthetic uncertainty.** If every value needed is bound and the agent merely finds the design ambiguous or ugly, that is an opinion, not a block.
|
|
46
46
|
|
|
47
|
+
## Why this is a block and not a warning — the measured case
|
|
48
|
+
|
|
49
|
+
An agent that had read this contract, and cited it in the briefing it wrote for its own build agents, instructed them in that same briefing to **"snap unbound dimensions via the snap tables (ties round down) and flag the rest."** A block downgraded to a warning, by the agent enforcing it, inside the document enforcing it — not from ignorance, but from reasoning around it under delivery pressure with eleven work items queued and blocking feeling like not-shipping. It took a human restating the rule as an absolute for the instruction to be withdrawn.
|
|
50
|
+
|
|
51
|
+
Coverage measured afterwards, on the subtrees actually being implemented:
|
|
52
|
+
|
|
53
|
+
| frames | colour | spacing | radius | reality |
|
|
54
|
+
|---|---|---|---|---|
|
|
55
|
+
| compliance screens | 96-100% | 82-97% | 88-93% | design-system composed |
|
|
56
|
+
| edit-KPI / target dialogs | **100%** | **0%** | **0%** | colours bound, geometry never bound |
|
|
57
|
+
| planning mockups | **1-4%** | **0-2%** | **0-3%** | hand-drawn, effectively unbound |
|
|
58
|
+
|
|
59
|
+
**Five of the eleven items blocked back to design; six proceeded.** One modal's subtree contained **zero variable references** — under snap-and-flag an agent would have produced a complete, lint-clean, tested component **in which every style value was invented**, and shipped it with a note. Five view files were written before it was stopped.
|
|
60
|
+
|
|
61
|
+
Blocking cost far less than it appeared it would: the non-visual half of the blocked work — domain types, adapters, CRUD, formatters — was completed and preserved. That is the answer to the delivery-pressure reasoning that produced the softening. And the contract holds once it is absolute rather than advisory: one agent withdrew a radius class it had already applied on finding `radius/none` was the only radius bound in its subtree; another refused to build an icon disc whose diameter and radius were both unbound rather than pick a size.
|
|
62
|
+
|
|
63
|
+
**A gate that depends on an agent choosing to honour it under delivery pressure is not a gate.** That is why this contract has an executable, headless rung at all — the judgment-based version demonstrably failed in the hands of an agent that had read the rule and agreed with it.
|
|
64
|
+
|
|
47
65
|
## What visual matching is for, precisely
|
|
48
66
|
|
|
49
67
|
In a **typed** axis it is *verification*: build from the variable, screenshot, confirm agreement — and a disagreement is condition 5, not a licence to trust the pixels. In an **untyped** axis it is *derivation*, and legitimate. The rule is never "do not look at pixels"; it is **"do not derive a value from pixels when a binding exists."**
|
|
@@ -16,6 +16,32 @@ This skill carries the **judgment**. It does not carry the **policy** — that i
|
|
|
16
16
|
|
|
17
17
|
`design-source-of-truth` and `scripts/design-source-gate.mjs` already ask whether a changed surface **declares where its design came from**. This asks the orthogonal question: are the **values on it bound**. Both apply. A surface can cite a perfectly valid design node and still paint a literal that no variable backs — that is the case this skill exists for, and it is invisible to the other gate.
|
|
18
18
|
|
|
19
|
+
## Why this gate is executable and not advisory
|
|
20
|
+
|
|
21
|
+
Not a hypothesis about what agents might do under pressure. A measurement.
|
|
22
|
+
|
|
23
|
+
Eleven frontend work items in a portfolio frontend, each carrying node-scoped design references. The governing rule already said coverage below 100% is a **block routed back to design, never a value for the implementing agent to guess**, and said explicitly that `warn` is the wrong severity *because* it ships the item to an agent that then guesses or stalls. An agent had read that rule. It cited the rule in the briefing it wrote for its build agents. In that same briefing it instructed them to **"snap unbound dimensions via the snap tables (ties round down) and flag the rest."**
|
|
24
|
+
|
|
25
|
+
A block downgraded to a warning, by the agent enforcing it, inside the document enforcing it. Not from ignorance — from reasoning around it under delivery pressure, because eleven items were queued and blocking felt like not-shipping. It took a human restating the rule as an absolute for the instruction to be withdrawn.
|
|
26
|
+
|
|
27
|
+
Coverage measured after the withdrawal, on the subtrees actually being implemented:
|
|
28
|
+
|
|
29
|
+
| frames | colour | spacing | radius | reality |
|
|
30
|
+
|---|---|---|---|---|
|
|
31
|
+
| compliance screens | 96-100% | 82-97% | 88-93% | design-system composed |
|
|
32
|
+
| edit-KPI / target dialogs | **100%** | **0%** | **0%** | colours bound, geometry never bound |
|
|
33
|
+
| planning mockups | **1-4%** | **0-2%** | **0-3%** | hand-drawn, effectively unbound |
|
|
34
|
+
|
|
35
|
+
**Five of the eleven items blocked back to design; six proceeded.** The worst case is the clearest argument: one modal's subtree contained **zero variable references** — every colour a literal hex, every padding, gap, radius and size a literal, raw px type sizes with literal font faces, a literal shadow. Under snap-and-flag an agent would have produced a complete, lint-clean, tested component **in which every style value was invented**, and shipped it with a note. Five view files were written before it was stopped.
|
|
36
|
+
|
|
37
|
+
**The frame-vs-subtree distinction is what moved items between build and block**: 14 bound values at frame level, **zero** inside the modal actually being built. It is the finding most likely to be silently reimplemented wrong, which is why Phase 2 probes with `--node`.
|
|
38
|
+
|
|
39
|
+
The delivery-pressure reasoning was also wrong on its own terms. The non-visual half of the blocked work — domain types, adapters, CRUD, formatters — was completed and preserved. **Blocking cost far less than it appeared it would.**
|
|
40
|
+
|
|
41
|
+
And the rule holds once it is absolute rather than advisory: one agent withdrew a radius class it had already applied on finding `radius/none` was the only radius bound in its subtree; another refused to build an icon disc whose diameter and radius were both unbound rather than pick a size.
|
|
42
|
+
|
|
43
|
+
**A gate that depends on an agent choosing to honour it under delivery pressure is not a gate.** This one is executable and headless precisely because the judgment-based version demonstrably failed in the hands of an agent that had read the rule and agreed with it. That is also why the verdict belongs to `design-intake-gate.mjs` (Phase 4) and not to prose you re-reason each run.
|
|
44
|
+
|
|
19
45
|
## Phase 0 — Is there a design source at all?
|
|
20
46
|
|
|
21
47
|
**A design source is optional, and this is the most important step in the skill.** Most projects have no designs. Run the probe and read its verdict:
|
|
@@ -84,6 +110,8 @@ FIGMA_ACCESS_TOKEN=… FIGMA_MCP_TOKEN=… \
|
|
|
84
110
|
|
|
85
111
|
Pass `--dark` wherever the library has a dark mode: the light+dark signature is what separates variables that share a value, and without it more ids stay ambiguous. An ambiguous id is still a failure — guessing which variable a value came from is exactly what this contract forbids.
|
|
86
112
|
|
|
113
|
+
**An unknown id fails loudly naming the id; it never resolves to a nearest match.** That self-detecting staleness is the only reason a committed map is safe to trust — it makes a stale map an error instead of silent corruption. Never add a fallback that picks the closest variable, and never treat an unresolved id as unbound-and-therefore-design's.
|
|
114
|
+
|
|
87
115
|
## Phase 3 — Gather findings
|
|
88
116
|
|
|
89
117
|
The probe already produced `hardcoded-in-design` and `bound` findings for every value in the subtree, with the two silent-under-reporting traps handled: the corner-keyed `rectangleCornerRadii` shape is normalised, and boundness is read from `boundVariables` directly rather than inferred from a resolved value (Figma omits zero-valued properties, so a padding bound to a zero-valued variable would otherwise vanish).
|
|
@@ -159,6 +187,7 @@ The rule is never "do not look at pixels". It is **"do not derive a value from p
|
|
|
159
187
|
- **The regime is per-axis**, derived from the committed variable-id map. Never per-project, never asked of a human, and never from a live collection query — that route does not run headlessly.
|
|
160
188
|
- **A design source is optional.** No source, no token, or no map is SKIPPED at exit 0, never a block.
|
|
161
189
|
- **Every failure names an owner.** Unbound values are design's; a stale or ambiguous map is ours.
|
|
190
|
+
- **An unknown variable id fails loudly rather than resolving to a nearest match.** Staleness is self-detecting, which is what makes a committed map trustworthy at all.
|
|
162
191
|
- **The threshold is 100%.** Relax it only with an explicit `--min` on the command line, never by softening anything in code.
|
|
163
192
|
- **Never guess an escalation target**, and never proceed without one.
|
|
164
193
|
- **Never pick a side in a disagreement.** Which source is right is a design decision.
|