jig-ui 0.12.0 → 0.14.0
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/CHANGELOG.md +105 -0
- package/README.md +221 -5
- package/dist/index.js +907 -97
- package/package.json +2 -2
- package/rules/00-anti-patterns.md +115 -0
- package/rules/01-modes.md +1 -1
- package/rules/03-patterns.md +34 -0
- package/rules/05-copy.md +1 -1
- package/rules.index.json +112 -0
- package/templates/COMMAND.md.tmpl +272 -17
- package/templates/command-metadata.json +7 -2
|
@@ -6,7 +6,7 @@ these, say so, list them, and stop.
|
|
|
6
6
|
|
|
7
7
|
There are two kinds of subcommand, and they are not run the same way.
|
|
8
8
|
|
|
9
|
-
**CLI-backed** — `install`, `update`, `init`, `check`, `explain`, `verdicts`, `gate`, `probe`. Run the
|
|
9
|
+
**CLI-backed** — `install`, `update`, `init`, `check`, `explain`, `verdicts`, `gate`, `probe`, `seo`. Run the
|
|
10
10
|
matching command with `{{scripts_path}}`, passing the flags through unchanged,
|
|
11
11
|
then do the work below for that subcommand. Read the command's full output —
|
|
12
12
|
findings are ordered by severity, not position, so `head`, `tail`, `grep` and
|
|
@@ -218,6 +218,33 @@ Read `DECISIONS.md` before asking anything. Do not re-ask what it already record
|
|
|
218
218
|
cite it instead. If the screen touches an item in its **Unresolved** section, ask the user
|
|
219
219
|
about it by name; do not settle it in the spec.
|
|
220
220
|
|
|
221
|
+
### 0. If the page already exists, describe it before you change anything
|
|
222
|
+
|
|
223
|
+
An existing page is specified from what is there, not redesigned on the way past.
|
|
224
|
+
Read the page and its stylesheet, render it at each size the spec will name, and
|
|
225
|
+
write the frontmatter from what you find: the regions in their real order, the
|
|
226
|
+
hierarchy as built, the navigation at each width. Then ask the user the questions
|
|
227
|
+
below only where the page does not answer them — what it is for, who arrives, what
|
|
228
|
+
is deferred — and show them the description to confirm or correct.
|
|
229
|
+
|
|
230
|
+
Two things this is not. It is not a redesign: a region you would have arranged
|
|
231
|
+
differently is still recorded as it is, and `critique` is where that argument
|
|
232
|
+
belongs, with the rules behind it. And it is not a rubber stamp: a page that
|
|
233
|
+
contradicts `DECISIONS.md` is described accurately and the contradiction noted in
|
|
234
|
+
the prose, because the spec is a record of what is, and the review is what says
|
|
235
|
+
whether that is right.
|
|
236
|
+
|
|
237
|
+
**Asked to redesign it instead, run this command normally.** The difference is
|
|
238
|
+
what the old page counts as: its content is the brief — the copy, the real data,
|
|
239
|
+
the questions its FAQ answers — and its structure is not. Decide the composition
|
|
240
|
+
from the rules and the answers, not from what the markup happens to do now, or
|
|
241
|
+
the redesign is the same page in new colours. Anything that genuinely must
|
|
242
|
+
survive is a constraint and belongs in the spec by name: a URL linked from
|
|
243
|
+
elsewhere, a field order the back end depends on, wording somebody signed off.
|
|
244
|
+
Ask which of those exist; do not assume the whole page is one.
|
|
245
|
+
|
|
246
|
+
The commands after this run unchanged.
|
|
247
|
+
|
|
221
248
|
### 1. Ask, two or three questions at a time
|
|
222
249
|
|
|
223
250
|
**This is a required interaction, not a suggestion.** Ask two or three questions,
|
|
@@ -231,9 +258,17 @@ settled at `init` and live in `{{config_file}}` and the token layer; re-deciding
|
|
|
231
258
|
them per screen creates a second source of truth for the values Jig exists to
|
|
232
259
|
centralise. If the user raises appearance, record it and move on.
|
|
233
260
|
|
|
261
|
+
**Every question carries an example answer**, for the same reason `decide`'s do: a
|
|
262
|
+
question someone has to decode gets a worse answer than one they can react to, and
|
|
263
|
+
the example shows the shape. Say it is an example, not a suggestion.
|
|
264
|
+
|
|
234
265
|
- Round 1 — what is it for? What does the user need to do there ("create an
|
|
235
266
|
invoice")? Who arrives, and what is the single most important thing they must be
|
|
236
267
|
able to see or do?
|
|
268
|
+
> *For example: "comprehension first, sign-up second. They arrive from the
|
|
269
|
+
> product page having already decided to try it, so no explainer above the
|
|
270
|
+
> cards. The most important thing is the limits — the price matters equally and
|
|
271
|
+
> must never dominate."*
|
|
237
272
|
- **Then scope it — be a pessimist.** Of everything this could include,
|
|
238
273
|
what is the smallest version that is useful on its own? Ask that plainly and
|
|
239
274
|
expect to cut. "Comments with attachments, mentions, reactions, editing,
|
|
@@ -242,11 +277,20 @@ centralise. If the user raises appearance, record it and move on.
|
|
|
242
277
|
forgotten. Everything after this designs V1 only.
|
|
243
278
|
- Round 2 — what content and states does it carry? Empty, loading, error,
|
|
244
279
|
first-run, and the realistic range (0 items, 5, 500)?
|
|
280
|
+
> *For example: "three plans, a monthly/annual choice, four FAQ questions, a
|
|
281
|
+
> footer. The billing choice is the only state — the page is static, nothing
|
|
282
|
+
> loads, nothing fails."*
|
|
245
283
|
- Round 3 — only what is still unresolved: entry points, where it leads, what
|
|
246
284
|
must not happen here.
|
|
285
|
+
> *For example: "they arrive from the product page or a docs search, and every
|
|
286
|
+
> plan goes straight to sign-up. Nothing on this page may ask for a sales
|
|
287
|
+
> call."*
|
|
247
288
|
|
|
248
289
|
**Ask about the phone by name.** What does the reader see first on a phone, and
|
|
249
|
-
what moves, stacks, or goes behind a control?
|
|
290
|
+
what moves, stacks, or goes behind a control?
|
|
291
|
+
> *For example: "the plans come first on the phone" — that is the answer this
|
|
292
|
+
> question wants, and everything else about the narrow composition is the
|
|
293
|
+
> system's to settle.* If the conversation only ever
|
|
250
294
|
describes the wide screen, the phone composition will be derived from it rather
|
|
251
295
|
than designed — and a derived phone composition is a squeezed desktop.
|
|
252
296
|
|
|
@@ -296,9 +340,15 @@ sizes: # phone first. Each size is a whole composition, not
|
|
|
296
340
|
grouping: { form: proximity, brand: common region }
|
|
297
341
|
grid: { columns: 12, form: 5, brand: 6 }
|
|
298
342
|
nav: none on this screen
|
|
343
|
+
wide: # judged at 1600px — where a layout with no upper bound shows itself
|
|
344
|
+
same-as: desktop
|
|
345
|
+
why: "content is capped at 1200px, so 1600 adds margin and nothing else"
|
|
299
346
|
states: [default, loading, error, success]
|
|
300
347
|
decisions: [The Engineer Reads First] # every DECISIONS.md entry this screen implements, by name
|
|
301
348
|
later: [social sign-in, remember this device] # cut from V1, by name — the next specs start here
|
|
349
|
+
indexable: true # from the mode — editorial yes, product/operator no. Say why when you override it.
|
|
350
|
+
title: "Pricing — Hoistline" # ≤ 60 characters, the search result's first line
|
|
351
|
+
description: "Three plans, every price visible. No sales call." # ≤ 155
|
|
302
352
|
mockup: pending # `mockup` sets approved or skipped, from the user's own words
|
|
303
353
|
mockup_at: # where the approved drawing is: a .jig/mockups path, or a Figma or Stitch link
|
|
304
354
|
deviations: [] # `make` writes here; `spec` leaves it empty
|
|
@@ -307,6 +357,24 @@ deviations: [] # `make` writes here; `spec` leaves it empty
|
|
|
307
357
|
|
|
308
358
|
- **`phone` is written first, and in full.** It is the most common screen, and the
|
|
309
359
|
one a derived composition fails on worst.
|
|
360
|
+
- **`indexable:`, `title:` and `description:` are copy, decided here.** The mode
|
|
361
|
+
sets the default — `editorial` is first-visit content and is indexable,
|
|
362
|
+
`product` and `operator` are what somebody reaches after signing in and are
|
|
363
|
+
not — and a page overrides it with a reason: a CV shared by link, a
|
|
364
|
+
confirmation page, a page that exists for one recipient. A page that is not
|
|
365
|
+
indexable needs no title or description here and must carry `noindex` when
|
|
366
|
+
built (`J-123`).
|
|
367
|
+
- **`wide` is required too, and is where an unbounded layout shows itself.** A
|
|
368
|
+
page judged only to 1280 never reveals that its content has no cap: at 1600 a
|
|
369
|
+
three-column grid becomes four thin ones, a header's logo and nav drift apart,
|
|
370
|
+
and a line of prose runs past its measure (`B-11`). `same-as: desktop` with a
|
|
371
|
+
`why:` is the right answer for a page that caps its content — and saying so is
|
|
372
|
+
the point.
|
|
373
|
+
- **`landscape` is optional, judged at 900px.** Write it only when the
|
|
374
|
+
composition genuinely changes between tablet and desktop — a navigation that
|
|
375
|
+
expands there, a grid that goes three to two. Most pages do not, and a size
|
|
376
|
+
written to be written costs a composition, a frame, a render and a row of
|
|
377
|
+
verdicts on every page afterwards.
|
|
310
378
|
- **`tablet` and `desktop` are written in full too.** `same-as: <size>` is allowed
|
|
311
379
|
when nothing changes, but only with `why:`. It is a claim that the composition
|
|
312
380
|
holds at that width — `critique` checks it on a render — not a way to leave the
|
|
@@ -318,6 +386,11 @@ deviations: [] # `make` writes here; `spec` leaves it empty
|
|
|
318
386
|
- **No shell before there are features.** If the product has no navigation yet,
|
|
319
387
|
`nav:` says `none yet` — it is decided once there is more than one feature to
|
|
320
388
|
move between, not invented for the first one.
|
|
389
|
+
- **Regions say what the content is, not what it looks like.** `- plans: three
|
|
390
|
+
cards` describes a shape; `- plans: a comparable set, one per tier` describes
|
|
391
|
+
content, and the second is what `make` can choose an element from. `H-119` runs
|
|
392
|
+
its decision order against these lines, so a region named only by its
|
|
393
|
+
appearance leaves the element to chance.
|
|
321
394
|
- **`nav:` at every size, on any screen with navigation — decided by `P-14`'s table
|
|
322
395
|
at that width, not copied.** It is the field most likely to be written once and
|
|
323
396
|
copied, and most likely to be genuinely different at every size on a page that
|
|
@@ -428,7 +501,7 @@ before drawing. Do not switch to HTML on their behalf; offer it, and let them
|
|
|
428
501
|
choose.
|
|
429
502
|
|
|
430
503
|
- **Drawing in Figma.** One page named for the feature, one frame per size at the
|
|
431
|
-
spec's widths — 360, 768, 1280 — phone first. Grey fills and strokes only; no
|
|
504
|
+
spec's widths — 360, 768, 1280, 1600 — phone first. Grey fills and strokes only; no
|
|
432
505
|
library components, styles or variables from the product's design system, for
|
|
433
506
|
the same reason the HTML carries no tokens.
|
|
434
507
|
- **Drawing in Stitch.** Ask for a low-fidelity grayscale wireframe of this one
|
|
@@ -483,7 +556,7 @@ look. So the drawing contains:
|
|
|
483
556
|
what goes there. An icon is its name in brackets: `[search]`.
|
|
484
557
|
- **Real words, not lorem ipsum.** Labels, headings and button text are part of the
|
|
485
558
|
structure — "Create invoice" versus "Submit" is a decision worth reviewing.
|
|
486
|
-
- **Every size the spec names, as its own frame** — phone, then tablet, then desktop,
|
|
559
|
+
- **Every size the spec names, as its own frame** — phone, then tablet, then desktop, then wide,
|
|
487
560
|
stacked top to bottom, each drawn with its own markup. Size is structure, not
|
|
488
561
|
detail: a phone composition that is only the desktop one narrower is the failure
|
|
489
562
|
specs are written per size to prevent. A `same-as:` size is drawn anyway, so the
|
|
@@ -510,6 +583,8 @@ look. So the drawing contains:
|
|
|
510
583
|
.frame[data-size="phone"] { width: 360px; }
|
|
511
584
|
.frame[data-size="tablet"] { width: 768px; }
|
|
512
585
|
.frame[data-size="desktop"] { width: 1280px; }
|
|
586
|
+
.frame[data-size="wide"] { width: 1600px; }
|
|
587
|
+
.frame[data-size="landscape"] { width: 900px; }
|
|
513
588
|
.region { border: 1px dashed #aaa; padding: 12px; margin: 0 0 12px; }
|
|
514
589
|
.region > .name { display: block; font-size: 12px; color: #777; margin: 0 0 8px; }
|
|
515
590
|
.media { background: #ddd; color: #555; display: grid; place-items: center; min-height: 120px; }
|
|
@@ -641,6 +716,20 @@ change. Write base styles for the phone and add width as it is available — not
|
|
|
641
716
|
the desktop layout with overrides that take it apart again. A layout built wide
|
|
642
717
|
and subtracted from ends up correct at exactly the widths somebody checked.
|
|
643
718
|
|
|
719
|
+
**Choose every element from what its content is, before styling any of it**
|
|
720
|
+
(`H-119`). For each region in the spec, run the decision order: what is this
|
|
721
|
+
content — a heading, a list, a sequence, a quotation, tabular data, navigation,
|
|
722
|
+
a control, a landmark? Is there a native element whose meaning is that? Does it
|
|
723
|
+
describe the content accurately? Only when nothing fits is a generic container
|
|
724
|
+
right. The page must read as what it is with the stylesheet off, in an order
|
|
725
|
+
that makes sense; CSS then arranges it, and never the reverse.
|
|
726
|
+
|
|
727
|
+
**Write the metadata in this change, not the next one** (`J-120`). The title,
|
|
728
|
+
description and preview text are copy: they come from the spec, they are written
|
|
729
|
+
by whoever wrote the headline, and they move when it moves. A page that is not
|
|
730
|
+
indexable carries `noindex` instead, from its own metadata rather than from
|
|
731
|
+
`robots.txt`, which is public and advisory and protects nothing.
|
|
732
|
+
|
|
644
733
|
Then the ordinary rules apply: tokens by semantic name, the relevant
|
|
645
734
|
`03-patterns.md` section for each component, `05-copy.md` for every string.
|
|
646
735
|
|
|
@@ -675,7 +764,7 @@ one. A report with no `JIG_CHECK:` line, or one you typed yourself, is not a fin
|
|
|
675
764
|
build.
|
|
676
765
|
|
|
677
766
|
**2. The build matches the approved mockup at each size.** Skip only when
|
|
678
|
-
`mockup: skipped`. Render the page at 360px, 768px and
|
|
767
|
+
`mockup: skipped`. Render the page at 360px, 768px, 1280px and 1600px — with a browser, not in
|
|
679
768
|
your head — and set each render beside the mockup at the same width. Go region by
|
|
680
769
|
region, in the mockup's order:
|
|
681
770
|
|
|
@@ -742,7 +831,9 @@ conversation that built the page. Running them in one head anchors them to each
|
|
|
742
831
|
other; do not shortcut it for cost, speed or context size.
|
|
743
832
|
|
|
744
833
|
- **A — the reader.** Delegate to a subagent. Give it the URL or file, the spec,
|
|
745
|
-
the approved mockup (from `mockup_at:`), `DECISIONS.md`, and the corpus. It
|
|
834
|
+
the approved mockup (from `mockup_at:`), `DECISIONS.md`, and the corpus. It also
|
|
835
|
+
judges every decision in `DECISIONS.md` against the page and writes
|
|
836
|
+
`decisions.json` (step 1c). It judges the `pass: screen` rules against the render, **and the `P-` patterns the spec's regions use** — a navigation region means `P-14`. Those patterns are not in `rules.index.json`, so walking the index alone never reaches them. It writes its verdicts to `.jig/critique/<surface>/screen.json` (step 1c). Do
|
|
746
837
|
**not** give it the build conversation. If it needs that conversation, it is
|
|
747
838
|
not reviewing the page, it is agreeing with itself.
|
|
748
839
|
- **B — the machine.** Run `check`. Deterministic, same input same output. It
|
|
@@ -837,9 +928,9 @@ Each reader arm writes its own file. The arm reports back only that it wrote it.
|
|
|
837
928
|
```json
|
|
838
929
|
{
|
|
839
930
|
"rendered": true,
|
|
840
|
-
"artefacts": [".jig/critique/pricing/360.png", ".jig/critique/pricing/768.png", ".jig/critique/pricing/1280.png"],
|
|
931
|
+
"artefacts": [".jig/critique/pricing/360.png", ".jig/critique/pricing/768.png", ".jig/critique/pricing/1280.png", ".jig/critique/pricing/1600.png"],
|
|
841
932
|
"verdicts": [
|
|
842
|
-
{ "id": "D-115", "verdict": "ok", "reason": "no sideways scroll at 360, 768 or
|
|
933
|
+
{ "id": "D-115", "verdict": "ok", "reason": "no sideways scroll at 360, 768, 1280 or 1600" },
|
|
843
934
|
{ "id": "P-14", "verdict": "finding", "reason": "menu button at 360 does not open; aria-expanded never set" },
|
|
844
935
|
{ "id": "A-60", "verdict": "n/a", "reason": "A-60 is about competing icons; this page has none" }
|
|
845
936
|
]
|
|
@@ -848,16 +939,44 @@ Each reader arm writes its own file. The arm reports back only that it wrote it.
|
|
|
848
939
|
|
|
849
940
|
`screen.json` carries `rendered` and `artefacts`; `code.json` needs only `verdicts`.
|
|
850
941
|
|
|
851
|
-
**
|
|
942
|
+
**And the project's own decisions, one verdict each**, in
|
|
943
|
+
`.jig/critique/<surface>/decisions.json`:
|
|
944
|
+
|
|
945
|
+
```json
|
|
946
|
+
{
|
|
947
|
+
"verdicts": [
|
|
948
|
+
{ "decision": "Voice", "verdict": "finding", "reason": "headings and buttons are Title Case; the decision says lowercase" },
|
|
949
|
+
{ "decision": "Trial and onboarding", "verdict": "finding", "reason": "Team and Scale say \"start free trial\"; the trial was replaced by the free Solo plan" },
|
|
950
|
+
{ "decision": "Navigation", "verdict": "ok", "reason": "menu button top right at 360, five links at 768 and above" }
|
|
951
|
+
]
|
|
952
|
+
}
|
|
953
|
+
```
|
|
954
|
+
|
|
955
|
+
Every `##` and `###` heading in `DECISIONS.md` is a decision and gets exactly one
|
|
956
|
+
verdict, judged against the built page. `Unresolved` is not a decision. The rules
|
|
957
|
+
are the system's; these are the project's, and a page can satisfy all 115 rules
|
|
958
|
+
while breaking the file written to hold what this project chose. In a live round
|
|
959
|
+
one page shipped "start free trial" two commands after the owner had killed
|
|
960
|
+
trials, and another used Title Case against a lowercase decision. Both passed
|
|
961
|
+
every rule.
|
|
962
|
+
|
|
963
|
+
**A rendered review is measured, not only described.** At 360, 768, 1280 and 1600px, run the
|
|
852
964
|
probe in the browser and save what it returns beside the verdicts:
|
|
853
965
|
|
|
854
966
|
```
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
967
|
+
# If a browser is on this machine, one command renders and records all three:
|
|
968
|
+
{{scripts_path}} probe --run <page> --save <surface>
|
|
969
|
+
|
|
970
|
+
# Otherwise, at each width: open the page in whatever browser you have, evaluate
|
|
971
|
+
# what `{{scripts_path}} probe` prints, and pipe exactly what it returned into:
|
|
858
972
|
{{scripts_path}} probe --save <surface>
|
|
859
973
|
```
|
|
860
974
|
|
|
975
|
+
`--run` drives a headless Chrome, Chromium or Edge it finds for itself — a
|
|
976
|
+
project with Playwright or Puppeteer already has one — and needs nothing
|
|
977
|
+
installed. The Stop hook runs it too, before it judges a review, so a render is
|
|
978
|
+
not a step anyone can forget.
|
|
979
|
+
|
|
861
980
|
**The CLI writes the probe file, you do not.** `--save` reads the probe's output,
|
|
862
981
|
stamps it with the page's checksum, and stores it. That is what makes the file a
|
|
863
982
|
measurement: `verdicts` recomputes the checksum, so a file written by hand, or one
|
|
@@ -907,17 +1026,37 @@ source and imagining the page is not a render.
|
|
|
907
1026
|
that works: in a live run four of six pages shipped a phone menu that did not open,
|
|
908
1027
|
or no menu at all, and every review that only looked passed them.
|
|
909
1028
|
- **Render at every size the spec names** — phone at 360px, tablet at 768px,
|
|
910
|
-
desktop at 1280px — and judge each size's fields against its own render. A
|
|
1029
|
+
desktop at 1280px, wide at 1600px — and judge each size's fields against its own render. A
|
|
911
1030
|
`same-as:` claim is checked like any other field: render that size and see
|
|
912
1031
|
whether the composition really does hold.
|
|
913
1032
|
- At every size, measure `document.documentElement.scrollWidth` against
|
|
914
1033
|
`clientWidth`. Greater means the page scrolls sideways at that width, which is a
|
|
915
1034
|
finding however good the rest of it looks.
|
|
1035
|
+
- **Judge the collapse, not only the sizes** (`L-01` step 6). Between one width
|
|
1036
|
+
and the next: did the column count drop rather than the columns shrink, did the
|
|
1037
|
+
reading order hold, did an emphasised item stay distinguishable once everything
|
|
1038
|
+
was in one column, did anything genuinely wider scroll inside itself rather
|
|
1039
|
+
than taking the page with it?
|
|
916
1040
|
- Compare the sizes. Did the phone change the **composition**, or only the
|
|
917
1041
|
**dimensions**? Reordering, stacking, collapsing, a different control — that is
|
|
918
1042
|
composition. The same layout with smaller numbers is not.
|
|
919
1043
|
- Check the page is styled at all. A stylesheet that 404s renders a page that
|
|
920
1044
|
passes every file-based check ever written.
|
|
1045
|
+
- **Read what a stranger meets first** (`J-120`). Open the head: does the title
|
|
1046
|
+
still describe this page, does the description still say what the page now
|
|
1047
|
+
says, and would either have survived the last copy change? A page whose
|
|
1048
|
+
metadata contradicts its own headline is the commonest drift there is, because
|
|
1049
|
+
a copy pass reads pages and nobody reads the head. Run `{{scripts_path}} seo`
|
|
1050
|
+
for what one page cannot see: a route that says `noindex` and sits in the
|
|
1051
|
+
sitemap, two pages claiming the same title, a site with neither sitemap nor
|
|
1052
|
+
robots.
|
|
1053
|
+
- **Read the markup as a document, not a layout** (`H-119`). For each region:
|
|
1054
|
+
does its element say what the content is? Would the page still read correctly
|
|
1055
|
+
with the stylesheet off, in this order? A row of links that is not a `<nav>`, a
|
|
1056
|
+
heading that is a styled `<div>`, steps in no particular element, a comparison
|
|
1057
|
+
of values that is not a table: each is a finding, and each names the element
|
|
1058
|
+
the content asks for. A `<div>` is right where nothing more specific is true;
|
|
1059
|
+
say so rather than inventing a fault.
|
|
921
1060
|
- Run the squint test from `L-01` against the render, not the analogue: render
|
|
922
1061
|
each size once more with `filter: grayscale(1)` on the root, and check that the
|
|
923
1062
|
primary action, the headings and the groups still read in order without colour.
|
|
@@ -957,10 +1096,10 @@ preserve while fixing them.
|
|
|
957
1096
|
Then the attestation:
|
|
958
1097
|
|
|
959
1098
|
```text
|
|
960
|
-
JIG_CRITIQUE: version=<version> mode=<mode> surface=<name> spec=<confirmed|missing|unconfirmed> mockup=<approved|skipped|missing> rendered=<yes|no> screen=<ran|skipped>:<n> code=<ran|skipped>:<n> mechanical=<pass|fail|skipped>:<n> warnings=<n>
|
|
1099
|
+
JIG_CRITIQUE: version=<version> mode=<mode> surface=<name> spec=<confirmed|missing|unconfirmed> mockup=<approved|skipped|missing> rendered=<yes|no> screen=<ran|skipped>:<n> code=<ran|skipped>:<n> decisions=<ran|incomplete|missing>:<n> mechanical=<pass|fail|skipped>:<n> warnings=<n>
|
|
961
1100
|
```
|
|
962
1101
|
|
|
963
|
-
**Take `screen=`, `code=` and `rendered=` from `jig verdicts`, never from your own
|
|
1102
|
+
**Take `screen=`, `code=`, `decisions=` and `rendered=` from `jig verdicts`, never from your own
|
|
964
1103
|
count.** If it reported an arm incomplete, that arm is `skipped` here with the reason,
|
|
965
1104
|
until it is re-run and passes.
|
|
966
1105
|
|
|
@@ -1015,6 +1154,22 @@ Run `{{scripts_path}} probe`. It prints one JavaScript expression — the render
|
|
|
1015
1154
|
for the critique's screen arm to evaluate in a browser at each width. See step 1c of
|
|
1016
1155
|
`critique`. It changes nothing on its own.
|
|
1017
1156
|
|
|
1157
|
+
## seo
|
|
1158
|
+
|
|
1159
|
+
Run `{{scripts_path}} seo` and report what it says. It needs no `DECISIONS.md`,
|
|
1160
|
+
no spec and no confirmation: it reads what the project already serves, so it is
|
|
1161
|
+
safe to run on the first day and on somebody else's codebase. It audits the whole project,
|
|
1162
|
+
which is what one file cannot: a route whose metadata says `noindex` sitting in
|
|
1163
|
+
the sitemap, two pages claiming the same title, indexable pages with no sitemap,
|
|
1164
|
+
a sitemap that lists nothing.
|
|
1165
|
+
|
|
1166
|
+
It is not a phase. The per-page half belongs where the copy is decided and
|
|
1167
|
+
written — `spec` records whether a page is indexable and what it claims, `make`
|
|
1168
|
+
writes the metadata in the same change as the headline, `check` enforces the
|
|
1169
|
+
budgets — because a pass you run afterwards is a pass that gets skipped on the
|
|
1170
|
+
day the copy changes, which is exactly how a live site ended up serving
|
|
1171
|
+
positioning it had already dropped.
|
|
1172
|
+
|
|
1018
1173
|
## gate
|
|
1019
1174
|
|
|
1020
1175
|
Not a command to run. In Claude Code, `install` adds a Stop hook that runs
|
|
@@ -1045,7 +1200,10 @@ wherever `brand` in `{{config_file}}` puts the token files.
|
|
|
1045
1200
|
|
|
1046
1201
|
`spec`, `mockup`, `make` and `critique` are blocked until this exists: `spec` and
|
|
1047
1202
|
`critique` refuse to start without it, and `mockup` and `make` need a confirmed spec.
|
|
1048
|
-
Nothing else is blocked — `install`, `init`, `check`
|
|
1203
|
+
Nothing else is blocked — `install`, `init`, `check`, `explain`, `seo`, `probe`
|
|
1204
|
+
and `verdicts` all run without it. `seo` in particular is an audit of what is
|
|
1205
|
+
already there, and a project with no decisions yet still has a sitemap that
|
|
1206
|
+
either contradicts its pages or does not.
|
|
1049
1207
|
`init` comes first, because this file sits beside the token files `init` writes.
|
|
1050
1208
|
|
|
1051
1209
|
### What belongs in it, and what does not
|
|
@@ -1089,14 +1247,87 @@ at all. If the design system has a position, it is in the rules; if it has none,
|
|
|
1089
1247
|
you are looking for is the places where this project has already chosen
|
|
1090
1248
|
something, or will have to:
|
|
1091
1249
|
|
|
1250
|
+
**Ask every question with an example answer attached.** A question someone has to
|
|
1251
|
+
decode gets a worse answer than one they can react to, and the example shows the
|
|
1252
|
+
shape you need — a sentence about this product, not an adjective. Say plainly
|
|
1253
|
+
that it is an example, not a suggestion.
|
|
1254
|
+
|
|
1092
1255
|
- Round 1 — what is this product, and who is it for in a sentence the team would
|
|
1093
1256
|
recognise? What should it never look like? Name real products, not adjectives.
|
|
1257
|
+
> *For example: "hosted log search — you point it at your cluster and ask in
|
|
1258
|
+
> plain text. For a backend engineer who has tried grep over SSH. It must never
|
|
1259
|
+
> look like an enterprise console: no SKU matrix, no 'contact us' pricing.
|
|
1260
|
+
> Closer to Fly.io than to a Datadog dashboard."*
|
|
1261
|
+
- Round 1b — **what the product is for, and what you give up for it.** This file
|
|
1262
|
+
is project-wide, so both answers are about the product, not a page. A page's
|
|
1263
|
+
own purpose is `spec`'s question, and it inherits its direction from here.
|
|
1264
|
+
- *"If this product works, what can someone do that they could not before?"*
|
|
1265
|
+
That is the north star: one outcome, in the product's own words, that every
|
|
1266
|
+
later argument is settled against. A design direction without one drifts
|
|
1267
|
+
screen by screen, each change defensible on its own.
|
|
1268
|
+
> *For example: "an engineer finds the line in their logs without learning a
|
|
1269
|
+
> query language" — not "be the best log tool", which no screen can serve or
|
|
1270
|
+
> fail. The test: could you look at a design and say whether it moves toward
|
|
1271
|
+
> that sentence?*
|
|
1272
|
+
- *"When two good options conflict here, which side do you take?"* Fast over
|
|
1273
|
+
complete, plain over clever, fewer choices over more, density over comfort.
|
|
1274
|
+
> *For example: "comprehension wins over persuasion — if the clearer page
|
|
1275
|
+
> converts worse, we ship the clearer page", or "recovery wins over speed:
|
|
1276
|
+
> every destructive action is undoable, even where that costs a step".*
|
|
1277
|
+
Jig's own tiebreakers settle the system's conflicts; this settles the
|
|
1278
|
+
project's, and without it the agent settles them alone. In a live run a
|
|
1279
|
+
decision about where the accent may appear collided with a component rule
|
|
1280
|
+
and the agent resolved it on its own, correctly as it happens, and recorded
|
|
1281
|
+
the collision as a deviation. One line here would have decided it.
|
|
1282
|
+
|
|
1283
|
+
**Refuse an adjective, ask again.** "Bold", "human", "trustworthy" and
|
|
1284
|
+
"premium" cannot be checked against a page, and every decision in this file is
|
|
1285
|
+
now judged against one. Push for the form that can: what it is closer to, what
|
|
1286
|
+
it would rather be than, what it refuses.
|
|
1287
|
+
- Round 1c — **the personality, asked through the four things that produce it.**
|
|
1288
|
+
Every interface has one whether it was chosen or not, and it is not a mood
|
|
1289
|
+
board: it is decided by type, colour, corners and language, each of which is a
|
|
1290
|
+
value this project is about to set. Ask about all four, one question each, and
|
|
1291
|
+
say what each answer will become.
|
|
1292
|
+
- **Type.** Serif reads classic or editorial; a rounded sans reads friendly; a
|
|
1293
|
+
neutral sans stays out of the way and lets everything else carry the
|
|
1294
|
+
personality. Which is this? *(Becomes `--font-text` and `--font-display`.)*
|
|
1295
|
+
- **Colour.** Which colour is this product's, and what does it mean here? The
|
|
1296
|
+
psychology matters less than the reason: blue is familiar and safe, gold
|
|
1297
|
+
reads expensive, pink reads unserious. *(Becomes `--color-brand`, and the
|
|
1298
|
+
contrast floors still apply — a palette that fails them is not a
|
|
1299
|
+
personality.)*
|
|
1300
|
+
- **Corners.** Square reads formal or technical, a small radius is neutral,
|
|
1301
|
+
a large one reads playful. One answer for the whole interface: mixing square
|
|
1302
|
+
and round in one screen looks worse than either. *(Becomes the `--radius-*`
|
|
1303
|
+
scale.)*
|
|
1304
|
+
- **Language.** Official and impersonal, or plain and conversational? Words
|
|
1305
|
+
appear in every screen, so this decides more of the personality than any
|
|
1306
|
+
single visual choice. *(Becomes a voice decision here, applied by
|
|
1307
|
+
`05-copy.md`.)*
|
|
1308
|
+
> *For example: "neutral sans — the type should disappear. One colour, a muted
|
|
1309
|
+
> olive, because it is the one thing on the page that is not a plan or a price.
|
|
1310
|
+
> Square corners; this is a tool, not a toy. Lowercase and technical: ingest,
|
|
1311
|
+
> retention, seat — plain words, undefined, no throat-clearing."*
|
|
1312
|
+
|
|
1313
|
+
**When they have no gut feeling, ask what the reader already uses.** The sites
|
|
1314
|
+
this audience spends its day in set the expectation; matching that is cheaper
|
|
1315
|
+
than teaching a new one. Take the direction, never the design: a project that
|
|
1316
|
+
borrows from a direct competitor looks like a second-rate version of it, which
|
|
1317
|
+
is also `A-01`'s point about invented decoration seen from the other side.
|
|
1094
1318
|
- Round 2 — for each thing the tokens already set, is there a rule about *how* it
|
|
1095
1319
|
is used? Where does the brand colour go, and where is it forbidden? What earns
|
|
1096
1320
|
emphasis?
|
|
1321
|
+
> *For example: "the accent appears exactly twice per page, both clickable —
|
|
1322
|
+
> the primary action and the active side of a control. Never on a checkmark, a
|
|
1323
|
+
> limit number or a feature list. Emphasis is earned by the one thing the
|
|
1324
|
+
> reader came to do."*
|
|
1097
1325
|
- Round 3 — what has the team already argued about, or reversed? A decision with
|
|
1098
1326
|
a history is the one most worth recording, because it is the one most likely to
|
|
1099
1327
|
be re-made wrongly.
|
|
1328
|
+
> *For example: "we shipped a 'most popular' badge for six weeks. Conversion
|
|
1329
|
+
> rose slightly; wrong-plan signups and support tickets rose more. We pulled
|
|
1330
|
+
> it, and that covers the effect, not the widget — no raised card either."*
|
|
1100
1331
|
|
|
1101
1332
|
**All three rounds run.** Do not write the file after round 2 because the answers
|
|
1102
1333
|
look complete. Round 3 asks for the one kind of decision a user does not volunteer
|
|
@@ -1105,7 +1336,10 @@ reason. In the arm that did ask, round 3 is where the unresolved items surfaced.
|
|
|
1105
1336
|
|
|
1106
1337
|
**Round 3 also asks, by name, what is still undecided.** "Is there anything the team
|
|
1107
1338
|
has not settled yet — something still argued about, or waiting on someone?" Ask it
|
|
1108
|
-
even when nothing so far suggests there is.
|
|
1339
|
+
even when nothing so far suggests there is.
|
|
1340
|
+
> *For example: "whether we publish a public status page. It is between showing
|
|
1341
|
+
> uptime on the pricing page and not mentioning it at all, and it waits on
|
|
1342
|
+
> whether support can staff it."* Whatever the answer names goes in the
|
|
1109
1343
|
**Unresolved** section below, not into a decision the user did not make. Four of six
|
|
1110
1344
|
files in a live run dropped the one open item the owner had named.
|
|
1111
1345
|
|
|
@@ -1127,6 +1361,27 @@ wrong.
|
|
|
1127
1361
|
A name gives the team something to cite in review. A reason lets a future agent
|
|
1128
1362
|
tell when the rule does not apply, which a bare instruction never can.
|
|
1129
1363
|
|
|
1364
|
+
**The north star and the tiebreaker are decisions like any other**, written with
|
|
1365
|
+
the same shape and judged against every page:
|
|
1366
|
+
|
|
1367
|
+
```markdown
|
|
1368
|
+
### What this page is for
|
|
1369
|
+
|
|
1370
|
+
A reader leaves knowing which plan fits them and which limit they would hit
|
|
1371
|
+
first. Nothing else on the page outranks that.
|
|
1372
|
+
|
|
1373
|
+
**Why:** they arrive from the product page already interested; the job is to
|
|
1374
|
+
remove the last doubt, not to sell again.
|
|
1375
|
+
|
|
1376
|
+
### When two options conflict
|
|
1377
|
+
|
|
1378
|
+
Comprehension wins over persuasion. If a clearer page converts worse, we ship
|
|
1379
|
+
the clearer page.
|
|
1380
|
+
|
|
1381
|
+
**Why:** the reader is an engineer who has been sold to badly before, and the
|
|
1382
|
+
support cost of a wrong-plan signup outweighs the signup.
|
|
1383
|
+
```
|
|
1384
|
+
|
|
1130
1385
|
**The reason is the owner's, or it is not written.** Write the `Why:` the user gave,
|
|
1131
1386
|
in their words or close to them. If they gave none, ask for it. If they still give
|
|
1132
1387
|
none, write `**Why:** not given` — never a reason you supplied. A live run invented
|
|
@@ -24,14 +24,19 @@
|
|
|
24
24
|
"argumentHint": "[--all] [--ci] [--json]",
|
|
25
25
|
"status": "available"
|
|
26
26
|
},
|
|
27
|
+
"seo": {
|
|
28
|
+
"description": "Audit what a search engine and a link preview read, across the whole project: a noindex route in the sitemap, two pages with one title, indexable pages with no sitemap, a sitemap that lists nothing. The per-page half lives in spec, make and check.",
|
|
29
|
+
"argumentHint": "[--json]",
|
|
30
|
+
"status": "available"
|
|
31
|
+
},
|
|
27
32
|
"verdicts": {
|
|
28
33
|
"description": "Verify a critique's verdict files: every rule in each pass judged once, no id that does not exist, no rule in the wrong arm, rendered only with a screenshot. Computes the counts the critique reports, so the agent never writes them.",
|
|
29
34
|
"argumentHint": "<surface>",
|
|
30
35
|
"status": "available"
|
|
31
36
|
},
|
|
32
37
|
"probe": {
|
|
33
|
-
"description": "Print the render probe
|
|
34
|
-
"argumentHint": "[--save <surface>]",
|
|
38
|
+
"description": "Print the render probe, or run it here. `--run <page> --save <surface>` drives a headless browser it finds for itself and records the measurement at 360, 768 and 1280, stamped with the page's checksum, so `verdicts` can tell a measurement from a claim.",
|
|
39
|
+
"argumentHint": "[--run <page>] [--save <surface>]",
|
|
35
40
|
"status": "available"
|
|
36
41
|
},
|
|
37
42
|
"gate": {
|