synthesisui 0.16.94 → 0.16.96
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/anatomy-read.js +14 -4
- package/dist/commands/import.js +14 -2
- package/dist/commands/mcp.js +83 -0
- package/dist/commands/upgrade.js +21 -0
- package/dist/doctor/sketch.js +17 -2
- package/dist/progress.js +16 -0
- package/dist/skill-import.js +248 -165
- package/package.json +1 -1
package/dist/anatomy-read.js
CHANGED
|
@@ -100,7 +100,10 @@ export function resolveAnatomy(read, declared, deps,
|
|
|
100
100
|
/** Their name → the slug it reaches in this system. See `RefResolver`. */
|
|
101
101
|
resolve,
|
|
102
102
|
/** What the component returns, when the skill said and it is not a plain tag. */
|
|
103
|
-
root
|
|
103
|
+
root,
|
|
104
|
+
/** This component's sketch, so a node can name its classes by INDEX instead
|
|
105
|
+
* of retyping them - see `AnatomyRead.at`. */
|
|
106
|
+
sketch) {
|
|
104
107
|
const parts = {};
|
|
105
108
|
const composes = [];
|
|
106
109
|
const external = [];
|
|
@@ -189,9 +192,16 @@ root) {
|
|
|
189
192
|
if (key !== wanted) {
|
|
190
193
|
notes.push(`two parts named \`${wanted}\` - the second is \`${key}\``);
|
|
191
194
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
+
/**
|
|
196
|
+
* THE CENSUS'S OWN STRING WINS when the node points at it. A retyped
|
|
197
|
+
* list is a copy that can lose a modifier; an index cannot.
|
|
198
|
+
*/
|
|
199
|
+
const raw = typeof node.classes === "string"
|
|
200
|
+
? node.classes
|
|
201
|
+
: typeof node.at === "number"
|
|
202
|
+
? (sketch?.[node.at]?.classes ?? "")
|
|
203
|
+
: "";
|
|
204
|
+
const classes = raw.split(/\s+/).filter(Boolean);
|
|
195
205
|
const t = transcribe(classes, declared);
|
|
196
206
|
parts[key] = { base: t.base, dark: t.dark, states: t.states };
|
|
197
207
|
/**
|
package/dist/commands/import.js
CHANGED
|
@@ -23,7 +23,7 @@ import { buildTable } from "../doctor/tokens.js";
|
|
|
23
23
|
import { readInlineStyle, rootClasses, rootTag, transcribe, } from "../doctor/transcribe.js";
|
|
24
24
|
import { transcribeVariants } from "../doctor/variant-read.js";
|
|
25
25
|
import { body, paint, section } from "../output.js";
|
|
26
|
-
import { startProgress } from "../progress.js";
|
|
26
|
+
import { phase, startProgress } from "../progress.js";
|
|
27
27
|
import { detectStack, resolveDeps } from "../stack.js";
|
|
28
28
|
import { walk, walkAll } from "./doctor.js";
|
|
29
29
|
/**
|
|
@@ -1656,7 +1656,10 @@ async function resolveReadParts(census, root) {
|
|
|
1656
1656
|
for (const [component, entry] of Object.entries(read)) {
|
|
1657
1657
|
const anatomy = entry?.anatomy;
|
|
1658
1658
|
const resolved = Array.isArray(anatomy) && anatomy.length > 0
|
|
1659
|
-
? resolveAnatomy(anatomy, declared, deps, resolveRef, entry?.root
|
|
1659
|
+
? resolveAnatomy(anatomy, declared, deps, resolveRef, entry?.root,
|
|
1660
|
+
// The component's own sketch, so a node naming itself by index reads
|
|
1661
|
+
// the exact class string the census measured (dono, 01/08).
|
|
1662
|
+
looks[component]?.sketch)
|
|
1660
1663
|
: Array.isArray(entry?.parts) && entry.parts.length > 0
|
|
1661
1664
|
? resolveFlatParts(entry.parts, declared)
|
|
1662
1665
|
: null;
|
|
@@ -1797,9 +1800,17 @@ export async function runImport(opts) {
|
|
|
1797
1800
|
* Resolved here rather than at measure time because the reading arrives
|
|
1798
1801
|
* AFTER the census was taken - this is the first moment both halves exist.
|
|
1799
1802
|
*/
|
|
1803
|
+
phase(2, 3, "Resolving the reading against the census");
|
|
1800
1804
|
await resolveReadParts(census, root);
|
|
1801
1805
|
}
|
|
1802
1806
|
else {
|
|
1807
|
+
/**
|
|
1808
|
+
* THE MACRO STEP. Three lines for a run, not thirty: a person watching wants
|
|
1809
|
+
* to know which phase they are in and how far into it, and everything else -
|
|
1810
|
+
* every file opened, every candidate ruled out - is the machine talking to
|
|
1811
|
+
* itself (dono, 01/08).
|
|
1812
|
+
*/
|
|
1813
|
+
phase(1, 3, "Measuring the repository");
|
|
1803
1814
|
console.log(section("Reading your project"));
|
|
1804
1815
|
census = await takeCensus(readFrom, {
|
|
1805
1816
|
...(usage.length > 0 ? { usage } : {}),
|
|
@@ -1882,6 +1893,7 @@ export async function runImport(opts) {
|
|
|
1882
1893
|
console.log("");
|
|
1883
1894
|
return;
|
|
1884
1895
|
}
|
|
1896
|
+
phase(3, 3, "Sending v1 and the v2 proposal");
|
|
1885
1897
|
const res = await fetch(`${base}/api/onboarding/import`, {
|
|
1886
1898
|
method: "POST",
|
|
1887
1899
|
headers: {
|
package/dist/commands/mcp.js
CHANGED
|
@@ -117,6 +117,28 @@ const TOOLS = [
|
|
|
117
117
|
required: ["recipe"],
|
|
118
118
|
},
|
|
119
119
|
},
|
|
120
|
+
{
|
|
121
|
+
name: "validate_recipes",
|
|
122
|
+
description: "The SAME question as validate_recipe, asked once for a whole reading. Send every recipe you are about to write and get one report back: which would arrive whole, and for the rest what would not survive, by name. Validating 35 components one at a time is 35 round trips and none of them thinking - this is one. Prefer it whenever you have more than one recipe in hand.",
|
|
123
|
+
inputSchema: {
|
|
124
|
+
type: "object",
|
|
125
|
+
properties: {
|
|
126
|
+
recipes: {
|
|
127
|
+
type: "array",
|
|
128
|
+
description: "Every candidate, in the document's own shape: [{ name, recipe }].",
|
|
129
|
+
items: {
|
|
130
|
+
type: "object",
|
|
131
|
+
properties: {
|
|
132
|
+
name: { type: "string" },
|
|
133
|
+
recipe: { type: "object" },
|
|
134
|
+
},
|
|
135
|
+
required: ["recipe"],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
required: ["recipes"],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
120
142
|
{
|
|
121
143
|
name: "request_component",
|
|
122
144
|
description: "File a component request when nothing in the catalogue covers what you need. This is the OTHER HALF of the refusal rule: you already say which entry you considered and why it did not fit - said in chat, that reasoning evaporates; filed here, it becomes the queue the system's author works from. File it at the moment you build the workaround, while the reasoning is still yours.",
|
|
@@ -333,6 +355,63 @@ async function validateRecipe(name, recipe) {
|
|
|
333
355
|
lines.push("", "Write this into `_synthesisui/not-expressed.md` - what we cannot hold is the list", "the system's author works from, and said only in chat it evaporates.");
|
|
334
356
|
return lines.join("\n");
|
|
335
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* A WHOLE READING, VALIDATED IN ONE QUESTION.
|
|
360
|
+
*
|
|
361
|
+
* `validate_recipe` is right and it is one round trip per component: a reader
|
|
362
|
+
* with 35 anatomies in hand spent 35 turns asking the same question, and that
|
|
363
|
+
* was the largest single block of wall-clock in a real import with none of it
|
|
364
|
+
* thinking (dono, 01/08). Every answer is independent, so batching costs nothing
|
|
365
|
+
* in fidelity - the server runs the same judgment per recipe.
|
|
366
|
+
*
|
|
367
|
+
* The report leads with the count that decides what to do next, then names only
|
|
368
|
+
* what would NOT survive. A list of 30 "this one is fine" lines is a wall the
|
|
369
|
+
* reader has to skim to find the two that matter.
|
|
370
|
+
*/
|
|
371
|
+
async function validateRecipes(entries) {
|
|
372
|
+
if (entries.length === 0)
|
|
373
|
+
return "Send at least one recipe: { recipes: [{ name, recipe }] }.";
|
|
374
|
+
const answer = await askCatalogue("validate", { recipes: entries });
|
|
375
|
+
if (!answer.ok)
|
|
376
|
+
return answer.because;
|
|
377
|
+
const body = answer.body;
|
|
378
|
+
/**
|
|
379
|
+
* A SERVER THAT DOES NOT KNOW THE BATCH still has to answer usefully - a
|
|
380
|
+
* published CLI meeting an older host must degrade to the singular rather than
|
|
381
|
+
* print a shape error. One round trip per recipe is slower and it is correct.
|
|
382
|
+
*/
|
|
383
|
+
if (!body.batch || !Array.isArray(body.results)) {
|
|
384
|
+
const one = [];
|
|
385
|
+
for (const e of entries)
|
|
386
|
+
one.push(await validateRecipe(String(e.name ?? "this component"), e.recipe));
|
|
387
|
+
return `This host does not answer batches, so each was asked on its own.\n\n${one.join("\n\n")}`;
|
|
388
|
+
}
|
|
389
|
+
const results = body.results;
|
|
390
|
+
const bad = results.filter((r) => r.ok !== true);
|
|
391
|
+
const lines = [
|
|
392
|
+
`${results.length} recipes validated - ${results.length - bad.length} would arrive whole.`,
|
|
393
|
+
];
|
|
394
|
+
if (bad.length === 0) {
|
|
395
|
+
lines.push("", "Nothing to fix. Send them.");
|
|
396
|
+
return lines.join("\n");
|
|
397
|
+
}
|
|
398
|
+
lines.push("", `${bad.length} would NOT arrive whole:`);
|
|
399
|
+
for (const r of bad) {
|
|
400
|
+
lines.push("", `## ${r.name ?? "this component"}`);
|
|
401
|
+
for (const x of r.rejected ?? [])
|
|
402
|
+
lines.push(` REFUSED ${x.at}: ${x.why}`);
|
|
403
|
+
for (const l of r.lost ?? [])
|
|
404
|
+
lines.push(` DROPPED BY THE COMPILER ${l}`);
|
|
405
|
+
for (const m of r.missing ?? [])
|
|
406
|
+
lines.push(` BELOW THE FLOOR ${m}`);
|
|
407
|
+
if (r.deferred)
|
|
408
|
+
lines.push(` floor deferred: ${r.deferred}`);
|
|
409
|
+
for (const c of r.checklist ?? [])
|
|
410
|
+
lines.push(` - go look for: ${c}`);
|
|
411
|
+
}
|
|
412
|
+
lines.push("", "Write these into `_synthesisui/not-expressed.md` - what we cannot hold is the", "list the system's author works from, and said only in chat it evaporates.");
|
|
413
|
+
return lines.join("\n");
|
|
414
|
+
}
|
|
336
415
|
/**
|
|
337
416
|
* The catalogue's door, with the session the device login already granted.
|
|
338
417
|
*
|
|
@@ -585,6 +664,10 @@ async function callTool(root, name, args) {
|
|
|
585
664
|
return text(await recipeVocabulary());
|
|
586
665
|
case "validate_recipe":
|
|
587
666
|
return text(await validateRecipe(String(args.name ?? "this component"), args.recipe));
|
|
667
|
+
case "validate_recipes":
|
|
668
|
+
return text(await validateRecipes(Array.isArray(args.recipes)
|
|
669
|
+
? args.recipes
|
|
670
|
+
: []));
|
|
588
671
|
case "request_component": {
|
|
589
672
|
const r = await fileRequest(root, {
|
|
590
673
|
kind: "component",
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -7,6 +7,7 @@ import { body, section, snippet } from "../output.js";
|
|
|
7
7
|
import { reactMajorOf, readInstalledConvention } from "../project-facts.js";
|
|
8
8
|
import { fetchChangelog, fetchComponent, fetchDesignSystem, RegistryError, } from "../registry.js";
|
|
9
9
|
import { add } from "./add.js";
|
|
10
|
+
import { doctor } from "./doctor.js";
|
|
10
11
|
/**
|
|
11
12
|
* The highest `v<n>` below `installed` among the folder names given, or the one
|
|
12
13
|
* asked for. Pure, so the version arithmetic can be tested without a disk.
|
|
@@ -226,6 +227,26 @@ export async function upgrade(slug, opts) {
|
|
|
226
227
|
console.log("");
|
|
227
228
|
console.log(body(`⚠ Could not regenerate: ${failed.join(", ")} (removed/renamed in v${latest.version}?)`));
|
|
228
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* THE CHECK RUNS ITSELF - closing the loop the owner asked for end to end
|
|
232
|
+
* (dono, 01/08).
|
|
233
|
+
*
|
|
234
|
+
* An upgrade that lands a v2 whose whole point is "these literals are tokens
|
|
235
|
+
* now" and then says "ask your agent to migrate" leaves the person with no
|
|
236
|
+
* way to know whether it worked. The doctor is the answer and it is one
|
|
237
|
+
* command, so it is not somebody's job to remember: it runs here, over the
|
|
238
|
+
* same root, and its report IS the receipt.
|
|
239
|
+
*
|
|
240
|
+
* Never strict. A finding is information, not a failure of the upgrade, and
|
|
241
|
+
* exiting non-zero on somebody's own drift would turn a successful install
|
|
242
|
+
* into a red terminal.
|
|
243
|
+
*/
|
|
244
|
+
console.log(section("Checking the app against v" + latest.version));
|
|
245
|
+
await doctor({ dir: root }).catch((err) => {
|
|
246
|
+
// A doctor that cannot run is not an upgrade that failed - the artifacts
|
|
247
|
+
// are already on disk and correct.
|
|
248
|
+
console.log(body(`The check could not run (${err instanceof Error ? err.message : "unknown"}). Run \`npx synthesisui doctor\` when you can.`));
|
|
249
|
+
});
|
|
229
250
|
console.log(section("Migrate the app"));
|
|
230
251
|
console.log(body(`The migration brief is at _synthesisui/ds/${slug}/UPGRADE.md`));
|
|
231
252
|
console.log("");
|
package/dist/doctor/sketch.js
CHANGED
|
@@ -19,8 +19,23 @@
|
|
|
19
19
|
* same in an atomic library, a feature-foldered app, or a single flat directory.
|
|
20
20
|
*/
|
|
21
21
|
import { scanTags } from "./css-modules.js";
|
|
22
|
-
/**
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Past this a component is a page in disguise, and the gate already said no.
|
|
24
|
+
*
|
|
25
|
+
* 60 was a guess and it cost a real read: measured across the owner's library,
|
|
26
|
+
* exactly ONE component overflows it - `TextEditor`, 67 nodes - and the seven it
|
|
27
|
+
* lost included `EditorContent`, the editable region that is the whole point of
|
|
28
|
+
* the component. So the skill opened TextEditor.tsx by hand at line 353, which
|
|
29
|
+
* is the "a file read whose content the census already carries is a pipeline
|
|
30
|
+
* optimization failure" rule breaking on our own cap (not-expressed.md, dono,
|
|
31
|
+
* 01/08).
|
|
32
|
+
*
|
|
33
|
+
* At 150 nothing in that library truncates (largest after TextEditor: 43), and
|
|
34
|
+
* 200 buys nothing over 150 - so 150 is the measured ceiling, not a rounder
|
|
35
|
+
* guess. The gate upstream is what keeps a page out; this only keeps a big
|
|
36
|
+
* COMPONENT whole.
|
|
37
|
+
*/
|
|
38
|
+
const MAX_NODES = 150;
|
|
24
39
|
/** `className="..."` or className={"..."} / {`...`} with no interpolation. */
|
|
25
40
|
const CLASS_ATTR = /className=\{?["'`]([^"'`{}$]*)["'`]\}?/;
|
|
26
41
|
/**
|
package/dist/progress.js
CHANGED
|
@@ -17,6 +17,22 @@ const BAR_WIDTH = 24;
|
|
|
17
17
|
/** Every this-many items, the non-TTY path prints one line. Enough to see movement in
|
|
18
18
|
* a piped log without turning it into the log. */
|
|
19
19
|
const MILESTONE_EVERY = 25;
|
|
20
|
+
/**
|
|
21
|
+
* THE MACRO STEP - "2/3 Validating recipes", on its own line, once.
|
|
22
|
+
*
|
|
23
|
+
* A run that prints every grep it ran spends the person's attention on
|
|
24
|
+
* mechanics, and one that prints nothing for minutes is indistinguishable from
|
|
25
|
+
* one that hung (dono, 01/08). Three lines for three phases is the whole
|
|
26
|
+
* contract: the bar underneath says how far into THIS phase it is, and nothing
|
|
27
|
+
* else gets a line.
|
|
28
|
+
*
|
|
29
|
+
* Written to stderr on purpose. A phase banner is progress, not output: a caller
|
|
30
|
+
* piping stdout to a file - or the skill reading a census off it - gets the data
|
|
31
|
+
* clean, and a person at the terminal still sees both.
|
|
32
|
+
*/
|
|
33
|
+
export function phase(n, of, title, write = (s) => process.stderr.write(s)) {
|
|
34
|
+
write(`\n${n}/${of} ${title}\n`);
|
|
35
|
+
}
|
|
20
36
|
/** A bar that reads as a bar at any width. Two glyphs, no colour: this line is
|
|
21
37
|
* overwritten dozens of times a second and anything fancier flickers. */
|
|
22
38
|
function bar(done, total) {
|
package/dist/skill-import.js
CHANGED
|
@@ -64,7 +64,9 @@ cannot expire on a host that never issued it. That is how a full read ended in a
|
|
|
64
64
|
twice (dono, 31/07).
|
|
65
65
|
|
|
66
66
|
So if the file says \`"registry": "http://localhost:3000"\`, every command in this skill carries
|
|
67
|
-
\`--registry http://localhost:3000\`. If it says production, pass nothing.
|
|
67
|
+
\`--registry http://localhost:3000\`. If it says production, pass nothing. **Read that file ONCE**
|
|
68
|
+
and carry the answer for the whole run - re-reading it before each command is a round trip that
|
|
69
|
+
answers a question already answered. The CLI now refuses a
|
|
68
70
|
mismatch instead of letting the server reject it, but the point is not to reach that.
|
|
69
71
|
|
|
70
72
|
**Do this silently.** Which host you are pointed at and which flags follow from it is plumbing,
|
|
@@ -143,7 +145,196 @@ The census still lands at \`<root>/_synthesisui/census.json\` and records both w
|
|
|
143
145
|
and where the evidence came from, so you always send the same path and there is never a second
|
|
144
146
|
file to pick between. Nothing is sent by \`--dry\`. Read the file.
|
|
145
147
|
|
|
146
|
-
### 2.
|
|
148
|
+
### 2. Walk them through the decisions - ALL of them, before any reading
|
|
149
|
+
|
|
150
|
+
**Every question this skill asks is answerable from the census you just took.** Source, name,
|
|
151
|
+
primary, theme, canvas, our-kit-or-theirs, architecture - the numbers behind all seven are in
|
|
152
|
+
the file on their disk, and none of them needs the anatomy.
|
|
153
|
+
|
|
154
|
+
They used to be asked AFTER the reading, and that put six hundred lines of work between the
|
|
155
|
+
person's first answer and their second (dono, 01/08). A person who answers seven questions in
|
|
156
|
+
ninety seconds and then watches a bar has had a good four minutes; the same person interrupted
|
|
157
|
+
twice in the middle of a silence has had a bad twenty.
|
|
158
|
+
|
|
159
|
+
So: ask everything here, in one pass, and **do not come back**. If something you learn during
|
|
160
|
+
the reading contradicts an answer, you do not reopen the question - you say so in the report at
|
|
161
|
+
the end and let them change it there.
|
|
162
|
+
|
|
163
|
+
Seven decisions are theirs. **Ask them as separate questions with selectable options** - use your
|
|
164
|
+
question tool, one call per decision, so they pick instead of reading a wall and composing a
|
|
165
|
+
reply. A single block containing everything is a report, and a report gets read, not answered.
|
|
166
|
+
|
|
167
|
+
Every question carries **your recommendation first, marked as such**, and the evidence for it in
|
|
168
|
+
one line. They are choosing, not auditing you.
|
|
169
|
+
|
|
170
|
+
**2a. Source** - the folder you measure the system out of.
|
|
171
|
+
|
|
172
|
+
Options are the candidates the CLI already ranked, each with what it would produce, plus the
|
|
173
|
+
whole repo, plus *somewhere else* for a path they type:
|
|
174
|
+
|
|
175
|
+
\`\`\`
|
|
176
|
+
Where should the system come from?
|
|
177
|
+
|
|
178
|
+
packages/ui (recommended) 114 files · 47% of its values already named ·
|
|
179
|
+
92 tokens of their own · every app imports it
|
|
180
|
+
the whole repo 2797 files · 36% · the average of one dark app
|
|
181
|
+
and two light ones, which is a diagnosis and
|
|
182
|
+
not a system
|
|
183
|
+
apps/web-dashboard the heaviest consumer, if you want one app's
|
|
184
|
+
vocabulary rather than the shared one
|
|
185
|
+
somewhere else a path you name
|
|
186
|
+
\`\`\`
|
|
187
|
+
|
|
188
|
+
If they name a path you cannot use, say which of the two it is and offer the list again:
|
|
189
|
+
|
|
190
|
+
- **it does not exist**, or holds no files you can read
|
|
191
|
+
- **it holds no design values** - no colours, no radii, no tokens. A folder of server code or
|
|
192
|
+
config is not a design system, and measuring it produces a system of nothing.
|
|
193
|
+
|
|
194
|
+
**2b. Name** - suggest theirs, and let Enter take it:
|
|
195
|
+
|
|
196
|
+
\`\`\`
|
|
197
|
+
What should it be called? (SignalUI)
|
|
198
|
+
\`\`\`
|
|
199
|
+
|
|
200
|
+
Say where the suggestion came from - \`Introduction.mdx\`, the package name, the folder. A name
|
|
201
|
+
they recognise is worth more than a clever one. The slug is derived once and never changes, so
|
|
202
|
+
this is the moment.
|
|
203
|
+
|
|
204
|
+
**2c. Primary** - and explain the role before offering the choice, because *primary* is our word
|
|
205
|
+
and not necessarily theirs:
|
|
206
|
+
|
|
207
|
+
\`\`\`
|
|
208
|
+
Primary is the colour that carries action - buttons, links, focus, the thing
|
|
209
|
+
you want pressed. Everything else in the system is measured for contrast
|
|
210
|
+
against it.
|
|
211
|
+
|
|
212
|
+
#059aed ocean-500 (recommended) your ColorPalette.mdx calls Ocean the
|
|
213
|
+
"Primary branding scale"
|
|
214
|
+
#1a4ed8 royal-blue-500 your docs label this one secondary
|
|
215
|
+
#ec4899 vivid-pink-500 half of your signature AI gradient
|
|
216
|
+
#4A90E2 blue-500 the most-painted blue in the folder,
|
|
217
|
+
but it sits after your own
|
|
218
|
+
/* End - Signal UI */ marker
|
|
219
|
+
\`\`\`
|
|
220
|
+
|
|
221
|
+
Always name the hex AND the token, and always give the reason a value is *not* recommended when
|
|
222
|
+
frequency would have picked it. That last row is the whole point of asking.
|
|
223
|
+
|
|
224
|
+
**2d. Default theme** - which face it opens in:
|
|
225
|
+
|
|
226
|
+
\`\`\`
|
|
227
|
+
dark (recommended) data-theme="dark" in the root layout, next-themes
|
|
228
|
+
defaultTheme="dark", enableSystem={false}
|
|
229
|
+
light the other one, also real here
|
|
230
|
+
\`\`\`
|
|
231
|
+
|
|
232
|
+
**Two is all there is** - a SynthesisUI system carries one palette and one alternate, and the
|
|
233
|
+
schema is \`"light" | "dark"\`. So this is not "which themes do you support", it is "which one do
|
|
234
|
+
docs and installs open in". If both are real, both get built from their own tokens; this picks
|
|
235
|
+
the face.
|
|
236
|
+
|
|
237
|
+
**2e. Canvas** - confirm, do not offer a list. The page is measured, not chosen:
|
|
238
|
+
|
|
239
|
+
\`\`\`
|
|
240
|
+
canvas #222326 darkgray-500 - what DashboardLayout paints on <main>
|
|
241
|
+
(your sidebar sits a step deeper at darkgray-700)
|
|
242
|
+
\`\`\`
|
|
243
|
+
|
|
244
|
+
Say it and let them object. Offering five near-blacks to pick between is asking someone to
|
|
245
|
+
re-decide something their code already decided.
|
|
246
|
+
|
|
247
|
+
**2f. Our kit, or theirs alone** - ask this ONLY when they already have a component library,
|
|
248
|
+
which is when \`--scope\` pointed at one and the CLI said so. An app with no components of its own
|
|
249
|
+
has nothing to weigh and should never see the question.
|
|
250
|
+
|
|
251
|
+
\`\`\`
|
|
252
|
+
Every system born here comes with 45 components of ours - button, card, input,
|
|
253
|
+
table, the rest of a working toolkit. You already have 37.
|
|
254
|
+
|
|
255
|
+
add ours to yours (recommended) 68 components, and the 23 governed by your
|
|
256
|
+
own rules are the 23 you wrote
|
|
257
|
+
mine alone 37 components, all of them yours. Our
|
|
258
|
+
layouts that need a component you do not
|
|
259
|
+
have stay out too. Add any of ours later
|
|
260
|
+
from the library, one at a time
|
|
261
|
+
\`\`\`
|
|
262
|
+
|
|
263
|
+
Send it as \`"standards": "none"\` in the reading when they choose the second, and leave the field
|
|
264
|
+
out when they choose the first. **Recommend adding ours**, and say why in one line: a component of
|
|
265
|
+
ours costs nothing until something composes it, and having a \`table\` the day somebody needs a
|
|
266
|
+
table is worth more than a shorter list.
|
|
267
|
+
|
|
268
|
+
Two things to be straight about, because both are true and neither is obvious:
|
|
269
|
+
|
|
270
|
+
- **the foundations come from the seed either way.** Their palette, their scales, their type - all
|
|
271
|
+
theirs - but the seven type slots, the easing and the neutral floor arrive from ours, because
|
|
272
|
+
the alternative to a type scale is no components at all rather than their components.
|
|
273
|
+
- **our layouts are written in our recipes.** A landing that composes \`button\`, \`card\` and
|
|
274
|
+
\`badge\` needs those to exist. Choosing *mine alone* keeps the layouts whose recipes they
|
|
275
|
+
happen to have and drops the rest - a layout pointing at a recipe nobody has renders as holes.
|
|
276
|
+
|
|
277
|
+
**2g. Which architecture has priority** - ask only when the CLI reported more than
|
|
278
|
+
one, which it does under "How this project is organised".
|
|
279
|
+
|
|
280
|
+
A project having two shapes is not a project with a mistake: a library organised
|
|
281
|
+
atomically inside a monorepo whose apps are organised by feature is two true answers.
|
|
282
|
+
Say that before you ask, or the question reads as an accusation.
|
|
283
|
+
|
|
284
|
+
\`\`\`
|
|
285
|
+
Two shapes, and neither is wrong:
|
|
286
|
+
|
|
287
|
+
atomic under packages/ui (recommended) 114 files - atoms/molecules/organisms,
|
|
288
|
+
and this is where the system comes from
|
|
289
|
+
feature under apps/web-dashboard 900 files - features/, where the app
|
|
290
|
+
consumes it
|
|
291
|
+
\`\`\`
|
|
292
|
+
|
|
293
|
+
The one they pick becomes a rule that reaches \`CLAUDE.md\`, so **every prompt after this
|
|
294
|
+
knows where a new component goes** - not what the folders are called, but which rung or
|
|
295
|
+
which feature a new file belongs to. Recommend the one the system came from.
|
|
296
|
+
|
|
297
|
+
**2h. Fonts** - do not ask at all. Report and move on:
|
|
298
|
+
|
|
299
|
+
\`\`\`
|
|
300
|
+
fonts Figtree for both display and body - the only family declared
|
|
301
|
+
(--font-sans, and next/font Figtree in all three root layouts)
|
|
302
|
+
\`\`\`
|
|
303
|
+
|
|
304
|
+
If a project genuinely declares two families, say which is which and why you paired them that
|
|
305
|
+
way. If it declares one, there is nothing to decide.
|
|
306
|
+
|
|
307
|
+
**A note on colour swatches.** You cannot paint a hex in this conversation, so the token name
|
|
308
|
+
carries the weight - \`ocean-500\` tells someone more than a square would. When they run
|
|
309
|
+
\`npx synthesisui import\` themselves in a terminal, the CLI paints them.
|
|
310
|
+
|
|
311
|
+
### 3. Read what arithmetic cannot - and do it in ONE uninterrupted run
|
|
312
|
+
|
|
313
|
+
Every answer is in hand, so nothing below needs the person. **Do not ask, do not confirm, do
|
|
314
|
+
not narrate the mechanics.** Three lines on screen for the whole phase:
|
|
315
|
+
|
|
316
|
+
\`\`\`
|
|
317
|
+
1/3 Measuring the repository... (step 1, already done)
|
|
318
|
+
2/3 Naming the anatomy and validating... (this step)
|
|
319
|
+
3/3 Sending v1 and v2... (step 4)
|
|
320
|
+
\`\`\`
|
|
321
|
+
|
|
322
|
+
**Narrate findings, never mechanics.** A grep, a glob, a file you opened and ruled out, a draft
|
|
323
|
+
you rewrote - none of those is a finding, and a run that prints them reads as a machine talking
|
|
324
|
+
to itself. *"Their docs call Ocean the primary branding scale"* is a finding and earns its line.
|
|
325
|
+
|
|
326
|
+
**The census answers first, always.** Before opening any file, check whether \`signals\`, \`sketch\`,
|
|
327
|
+
\`declared\` or \`coverage\` already hold it - they usually do, and a file read whose content the
|
|
328
|
+
census carries is the failure this pipeline keeps paying for. The sketch now runs to 150 nodes,
|
|
329
|
+
so a big component arrives WHOLE: \`TextEditor\` used to lose its editable region at node 60 and
|
|
330
|
+
that single truncation is what forced a hand-read of the file (dono, 01/08).
|
|
331
|
+
|
|
332
|
+
**Validate in ONE call, at the end.** Build every recipe in memory first, then send them all to
|
|
333
|
+
\`validate_recipes\` - one round trip for the whole reading instead of one per component. A reader
|
|
334
|
+
with 35 anatomies spent 35 turns asking the same question, and none of that was thinking. Only
|
|
335
|
+
after the batch answers do you fix what it named and, if you fixed anything, ask once more.
|
|
336
|
+
|
|
337
|
+
|
|
147
338
|
|
|
148
339
|
Open the project and answer the questions below. Then add a \`reading\` object to
|
|
149
340
|
\`_synthesisui/census.json\`:
|
|
@@ -182,14 +373,14 @@ Open the project and answer the questions below. Then add a \`reading\` object t
|
|
|
182
373
|
"components": {
|
|
183
374
|
"MetricCard": {
|
|
184
375
|
"anatomy": [
|
|
185
|
-
{ "as": "icon", "name": "icon", "
|
|
376
|
+
{ "as": "icon", "name": "icon", "at": 1 },
|
|
186
377
|
{
|
|
187
378
|
"as": "stack",
|
|
188
379
|
"name": "body",
|
|
189
|
-
"
|
|
380
|
+
"at": 2,
|
|
190
381
|
"children": [
|
|
191
|
-
{ "as": "text", "name": "label", "
|
|
192
|
-
{ "as": "heading", "name": "value", "
|
|
382
|
+
{ "as": "text", "name": "label", "at": 3 },
|
|
383
|
+
{ "as": "heading", "name": "value", "at": 4 }
|
|
193
384
|
]
|
|
194
385
|
}
|
|
195
386
|
]
|
|
@@ -197,15 +388,15 @@ Open the project and answer the questions below. Then add a \`reading\` object t
|
|
|
197
388
|
"ArticleCard": {
|
|
198
389
|
"root": "Card",
|
|
199
390
|
"anatomy": [
|
|
200
|
-
{ "as": "image", "name": "cover", "
|
|
201
|
-
{ "as": "heading", "name": "title", "
|
|
391
|
+
{ "as": "image", "name": "cover", "at": 1 },
|
|
392
|
+
{ "as": "heading", "name": "title", "at": 2 },
|
|
202
393
|
{ "as": "component", "ref": "TextEditor" },
|
|
203
394
|
{
|
|
204
395
|
"as": "row",
|
|
205
396
|
"name": "footer",
|
|
206
|
-
"
|
|
397
|
+
"at": 3,
|
|
207
398
|
"children": [
|
|
208
|
-
{ "as": "button", "name": "publish", "
|
|
399
|
+
{ "as": "button", "name": "publish", "at": 4 },
|
|
209
400
|
{ "as": "button", "name": "discard", "classes": "btn btn-ghost" }
|
|
210
401
|
]
|
|
211
402
|
}
|
|
@@ -591,6 +782,16 @@ a recipe before these tools existed, and four separate readings were lost in sil
|
|
|
591
782
|
\`dark:\` inside a variant, a state the compiler cannot spell, a part name with a dot in it,
|
|
592
783
|
a layer with an empty style. Each of those was correct work that arrived as nothing.
|
|
593
784
|
|
|
785
|
+
**In ONE call.** Build every recipe in memory, then send the whole reading to
|
|
786
|
+
\`validate_recipes\` - one round trip for 35 components instead of 35. \`validate_recipe\`
|
|
787
|
+
(singular) is for the one recipe you rewrote after the batch named it; reaching for it in a
|
|
788
|
+
loop is the shape this optimisation exists to kill (dono, 01/08).
|
|
789
|
+
|
|
790
|
+
\`\`\`
|
|
791
|
+
validate_recipes({ recipes: [{ name, recipe }, …] }) the whole reading, one question
|
|
792
|
+
validate_recipe(name, recipe) the one you just fixed
|
|
793
|
+
\`\`\`
|
|
794
|
+
|
|
594
795
|
\`validate_recipe\` never answers yes or no. It answers with what would not survive:
|
|
595
796
|
|
|
596
797
|
\`\`\`
|
|
@@ -701,9 +902,22 @@ file for, minus the one thing that is genuinely yours: the NAMES.
|
|
|
701
902
|
|
|
702
903
|
Read that and write the anatomy: depth 1 is \`trigger\`, the dot is \`status-dot\`, the
|
|
703
904
|
capitalised tag is a frontier. **Do not open the component file** - if the sketch is
|
|
704
|
-
missing or visibly truncated (
|
|
905
|
+
missing or visibly truncated (150-node cap), say so in not-expressed.md and only then
|
|
705
906
|
read, because that gap is the census's to close.
|
|
706
907
|
|
|
908
|
+
**NAME THE NODE BY ITS INDEX - never retype the class string.** Each anatomy node carries
|
|
909
|
+
\`"at": <index into this sketch>\`, and the CLI reads the exact string the census measured:
|
|
910
|
+
|
|
911
|
+
\`\`\`json
|
|
912
|
+
{ "as": "button", "name": "trigger", "at": 1 }
|
|
913
|
+
\`\`\`
|
|
914
|
+
|
|
915
|
+
Retyping is where a modifier goes missing. Six real components arrived with \`hover:\` and
|
|
916
|
+
\`focus:\` in their source and no state at all in the recipe, and the parser was not the
|
|
917
|
+
problem - the copy was (audit, dono, 01/08). An index cannot lose a class. \`classes\` still
|
|
918
|
+
works and still wins when you send both, which is for the one node the sketch could not
|
|
919
|
+
reach; every other node uses \`at\`.
|
|
920
|
+
|
|
707
921
|
### What the CLI now reads for you, so do not spend a turn on it
|
|
708
922
|
|
|
709
923
|
**The rule under all of this: you are given EVIDENCE and asked to DECIDE.** You are never
|
|
@@ -748,7 +962,16 @@ scale and lost the sentence (dono, 01/08).
|
|
|
748
962
|
|
|
749
963
|
When their docs (a \`Geometry.mdx\`, a design-tokens page, comments beside the \`@theme\`)
|
|
750
964
|
state what a step is FOR, send it as system-wide rules - \`applies: []\`, \`fact: true\`, the
|
|
751
|
-
doc as evidence
|
|
965
|
+
doc as evidence.
|
|
966
|
+
|
|
967
|
+
**One doc pass, and only for what the census cannot hold.** A scale's INTENT is prose, so it
|
|
968
|
+
genuinely needs a human sentence somebody wrote - but a scale's VALUES, its token names, the
|
|
969
|
+
theme, the class convention and the architecture are all measured and sitting in \`declared\`,
|
|
970
|
+
\`signals\` and \`conventions\`. Opening a docs page to confirm a number the census already
|
|
971
|
+
carries costs a turn and answers nothing. Read the docs ONCE, for the sentences; never in a
|
|
972
|
+
loop, and never to check arithmetic.
|
|
973
|
+
|
|
974
|
+
Send it like this:
|
|
752
975
|
|
|
753
976
|
\`\`\`
|
|
754
977
|
this system's spacing has exactly three steps - sm for close relationships inside a
|
|
@@ -786,157 +1009,9 @@ The older flat form - \`"parts": [{ "name": "label", "classes": "..." }]\` - sti
|
|
|
786
1009
|
styles correctly. It just carries no shape, so the preview lays the parts out in a row and the
|
|
787
1010
|
spec view has no nesting to draw.
|
|
788
1011
|
|
|
789
|
-
###
|
|
790
|
-
|
|
791
|
-
Six decisions are theirs. **Ask them as separate questions with selectable options** - use your
|
|
792
|
-
question tool, one call per decision, so they pick instead of reading a wall and composing a
|
|
793
|
-
reply. A single block containing everything is a report, and a report gets read, not answered.
|
|
794
|
-
|
|
795
|
-
Every question carries **your recommendation first, marked as such**, and the evidence for it in
|
|
796
|
-
one line. They are choosing, not auditing you.
|
|
797
|
-
|
|
798
|
-
**3a. Source** - the folder you measure the system out of.
|
|
799
|
-
|
|
800
|
-
Options are the candidates the CLI already ranked, each with what it would produce, plus the
|
|
801
|
-
whole repo, plus *somewhere else* for a path they type:
|
|
802
|
-
|
|
803
|
-
\`\`\`
|
|
804
|
-
Where should the system come from?
|
|
805
|
-
|
|
806
|
-
packages/ui (recommended) 114 files · 47% of its values already named ·
|
|
807
|
-
92 tokens of their own · every app imports it
|
|
808
|
-
the whole repo 2797 files · 36% · the average of one dark app
|
|
809
|
-
and two light ones, which is a diagnosis and
|
|
810
|
-
not a system
|
|
811
|
-
apps/web-dashboard the heaviest consumer, if you want one app's
|
|
812
|
-
vocabulary rather than the shared one
|
|
813
|
-
somewhere else a path you name
|
|
814
|
-
\`\`\`
|
|
815
|
-
|
|
816
|
-
If they name a path you cannot use, say which of the two it is and offer the list again:
|
|
817
|
-
|
|
818
|
-
- **it does not exist**, or holds no files you can read
|
|
819
|
-
- **it holds no design values** - no colours, no radii, no tokens. A folder of server code or
|
|
820
|
-
config is not a design system, and measuring it produces a system of nothing.
|
|
821
|
-
|
|
822
|
-
**3b. Name** - suggest theirs, and let Enter take it:
|
|
823
|
-
|
|
824
|
-
\`\`\`
|
|
825
|
-
What should it be called? (SignalUI)
|
|
826
|
-
\`\`\`
|
|
827
|
-
|
|
828
|
-
Say where the suggestion came from - \`Introduction.mdx\`, the package name, the folder. A name
|
|
829
|
-
they recognise is worth more than a clever one. The slug is derived once and never changes, so
|
|
830
|
-
this is the moment.
|
|
831
|
-
|
|
832
|
-
**3c. Primary** - and explain the role before offering the choice, because *primary* is our word
|
|
833
|
-
and not necessarily theirs:
|
|
834
|
-
|
|
835
|
-
\`\`\`
|
|
836
|
-
Primary is the colour that carries action - buttons, links, focus, the thing
|
|
837
|
-
you want pressed. Everything else in the system is measured for contrast
|
|
838
|
-
against it.
|
|
839
|
-
|
|
840
|
-
#059aed ocean-500 (recommended) your ColorPalette.mdx calls Ocean the
|
|
841
|
-
"Primary branding scale"
|
|
842
|
-
#1a4ed8 royal-blue-500 your docs label this one secondary
|
|
843
|
-
#ec4899 vivid-pink-500 half of your signature AI gradient
|
|
844
|
-
#4A90E2 blue-500 the most-painted blue in the folder,
|
|
845
|
-
but it sits after your own
|
|
846
|
-
/* End - Signal UI */ marker
|
|
847
|
-
\`\`\`
|
|
848
|
-
|
|
849
|
-
Always name the hex AND the token, and always give the reason a value is *not* recommended when
|
|
850
|
-
frequency would have picked it. That last row is the whole point of asking.
|
|
851
|
-
|
|
852
|
-
**3d. Default theme** - which face it opens in:
|
|
853
|
-
|
|
854
|
-
\`\`\`
|
|
855
|
-
dark (recommended) data-theme="dark" in the root layout, next-themes
|
|
856
|
-
defaultTheme="dark", enableSystem={false}
|
|
857
|
-
light the other one, also real here
|
|
858
|
-
\`\`\`
|
|
859
|
-
|
|
860
|
-
**Two is all there is** - a SynthesisUI system carries one palette and one alternate, and the
|
|
861
|
-
schema is \`"light" | "dark"\`. So this is not "which themes do you support", it is "which one do
|
|
862
|
-
docs and installs open in". If both are real, both get built from their own tokens; this picks
|
|
863
|
-
the face.
|
|
864
|
-
|
|
865
|
-
**3e. Canvas** - confirm, do not offer a list. The page is measured, not chosen:
|
|
866
|
-
|
|
867
|
-
\`\`\`
|
|
868
|
-
canvas #222326 darkgray-500 - what DashboardLayout paints on <main>
|
|
869
|
-
(your sidebar sits a step deeper at darkgray-700)
|
|
870
|
-
\`\`\`
|
|
871
|
-
|
|
872
|
-
Say it and let them object. Offering five near-blacks to pick between is asking someone to
|
|
873
|
-
re-decide something their code already decided.
|
|
874
|
-
|
|
875
|
-
**3e-bis. Our kit, or theirs alone** - ask this ONLY when they already have a component library,
|
|
876
|
-
which is when \`--scope\` pointed at one and the CLI said so. An app with no components of its own
|
|
877
|
-
has nothing to weigh and should never see the question.
|
|
878
|
-
|
|
879
|
-
\`\`\`
|
|
880
|
-
Every system born here comes with 45 components of ours - button, card, input,
|
|
881
|
-
table, the rest of a working toolkit. You already have 37.
|
|
882
|
-
|
|
883
|
-
add ours to yours (recommended) 68 components, and the 23 governed by your
|
|
884
|
-
own rules are the 23 you wrote
|
|
885
|
-
mine alone 37 components, all of them yours. Our
|
|
886
|
-
layouts that need a component you do not
|
|
887
|
-
have stay out too. Add any of ours later
|
|
888
|
-
from the library, one at a time
|
|
889
|
-
\`\`\`
|
|
890
|
-
|
|
891
|
-
Send it as \`"standards": "none"\` in the reading when they choose the second, and leave the field
|
|
892
|
-
out when they choose the first. **Recommend adding ours**, and say why in one line: a component of
|
|
893
|
-
ours costs nothing until something composes it, and having a \`table\` the day somebody needs a
|
|
894
|
-
table is worth more than a shorter list.
|
|
895
|
-
|
|
896
|
-
Two things to be straight about, because both are true and neither is obvious:
|
|
897
|
-
|
|
898
|
-
- **the foundations come from the seed either way.** Their palette, their scales, their type - all
|
|
899
|
-
theirs - but the seven type slots, the easing and the neutral floor arrive from ours, because
|
|
900
|
-
the alternative to a type scale is no components at all rather than their components.
|
|
901
|
-
- **our layouts are written in our recipes.** A landing that composes \`button\`, \`card\` and
|
|
902
|
-
\`badge\` needs those to exist. Choosing *mine alone* keeps the layouts whose recipes they
|
|
903
|
-
happen to have and drops the rest - a layout pointing at a recipe nobody has renders as holes.
|
|
904
|
-
|
|
905
|
-
**3e-ter. Which architecture has priority** - ask only when the CLI reported more than
|
|
906
|
-
one, which it does under "How this project is organised".
|
|
907
|
-
|
|
908
|
-
A project having two shapes is not a project with a mistake: a library organised
|
|
909
|
-
atomically inside a monorepo whose apps are organised by feature is two true answers.
|
|
910
|
-
Say that before you ask, or the question reads as an accusation.
|
|
911
|
-
|
|
912
|
-
\`\`\`
|
|
913
|
-
Two shapes, and neither is wrong:
|
|
914
|
-
|
|
915
|
-
atomic under packages/ui (recommended) 114 files - atoms/molecules/organisms,
|
|
916
|
-
and this is where the system comes from
|
|
917
|
-
feature under apps/web-dashboard 900 files - features/, where the app
|
|
918
|
-
consumes it
|
|
919
|
-
\`\`\`
|
|
920
|
-
|
|
921
|
-
The one they pick becomes a rule that reaches \`CLAUDE.md\`, so **every prompt after this
|
|
922
|
-
knows where a new component goes** - not what the folders are called, but which rung or
|
|
923
|
-
which feature a new file belongs to. Recommend the one the system came from.
|
|
924
|
-
|
|
925
|
-
**3f. Fonts** - do not ask at all. Report and move on:
|
|
926
|
-
|
|
927
|
-
\`\`\`
|
|
928
|
-
fonts Figtree for both display and body - the only family declared
|
|
929
|
-
(--font-sans, and next/font Figtree in all three root layouts)
|
|
930
|
-
\`\`\`
|
|
931
|
-
|
|
932
|
-
If a project genuinely declares two families, say which is which and why you paired them that
|
|
933
|
-
way. If it declares one, there is nothing to decide.
|
|
934
|
-
|
|
935
|
-
**A note on colour swatches.** You cannot paint a hex in this conversation, so the token name
|
|
936
|
-
carries the weight - \`ocean-500\` tells someone more than a square would. When they run
|
|
937
|
-
\`npx synthesisui import\` themselves in a terminal, the CLI paints them.
|
|
1012
|
+
### 4. Send it
|
|
938
1013
|
|
|
939
|
-
|
|
1014
|
+
Everything is answered and everything is validated:
|
|
940
1015
|
|
|
941
1016
|
\`\`\`
|
|
942
1017
|
npx synthesisui import --census _synthesisui/census.json --name "<the name>" [--registry <from step 0>]
|
|
@@ -947,13 +1022,13 @@ npx synthesisui import --census _synthesisui/census.json --name "<the name>" [--
|
|
|
947
1022
|
Pass every answer explicitly - \`--name\`, \`--scope\` and \`--usage\` if they changed either, and the reading rewritten
|
|
948
1023
|
with their primary and their theme. The CLI cannot ask anything when you are the one running it:
|
|
949
1024
|
it has no terminal, so its own prompts are skipped by design. **Whatever you did not ask, nobody
|
|
950
|
-
asked.** Skip
|
|
1025
|
+
asked.** Skip 2a and the first time they learn where their system came from is when they open
|
|
951
1026
|
it.
|
|
952
1027
|
|
|
953
1028
|
If it refuses, it says which of the three it is: no session, an expired one, or a token issued
|
|
954
1029
|
by a different host. All are recoverable from the census on disk; none need a re-measure.
|
|
955
1030
|
|
|
956
|
-
###
|
|
1031
|
+
### 5. Tell them exactly what they have, and what is theirs to decide
|
|
957
1032
|
|
|
958
1033
|
The import publishes **v1** as a faithful baseline and opens a **v2 draft** holding the
|
|
959
1034
|
proposal. Say all of this in your own words - do not just print the link:
|
|
@@ -984,7 +1059,7 @@ three biggest items so they know whether it is worth opening now.
|
|
|
984
1059
|
found in their code, a token that is not ramp-shaped, a name the document refused. Read them
|
|
985
1060
|
and pass on the ones that matter.
|
|
986
1061
|
|
|
987
|
-
###
|
|
1062
|
+
### 6. Close the loop in their repo
|
|
988
1063
|
|
|
989
1064
|
The system is only worth something once it governs the code it came from:
|
|
990
1065
|
|
|
@@ -1022,6 +1097,14 @@ The user should end up with a system that reads like theirs and not like ours:
|
|
|
1022
1097
|
- **Do not guess \`themes\` to be helpful.** Omitting it falls back to counting rungs, which is
|
|
1023
1098
|
a guess the product labels as a guess. A wrong confident answer does not get labelled.
|
|
1024
1099
|
- **Do not apologise for the empty contracts.** They are the design. Explain them.
|
|
1100
|
+
- **Do not come back with a question once step 3 has started.** Everything answerable was
|
|
1101
|
+
answered in step 2. An interruption in the middle of a silent phase costs the person the
|
|
1102
|
+
whole phase, because they now have to hold what you were doing in their head.
|
|
1103
|
+
- **Do not narrate the mechanics.** No grep, no glob, no "let me check whether X exists", no
|
|
1104
|
+
draft you rewrote. Findings earn a line; the machine talking to itself does not.
|
|
1105
|
+
- **Do not open a file to confirm something the census already carries.** \`signals\`, \`sketch\`,
|
|
1106
|
+
\`declared\`, \`conventions\` and \`coverage\` are the answer to almost every "let me just check".
|
|
1107
|
+
- **Do not validate in a loop.** One \`validate_recipes\` for the whole reading.
|
|
1025
1108
|
|
|
1026
1109
|
## Not yet true
|
|
1027
1110
|
|
package/package.json
CHANGED