jig-ui 0.23.0 → 0.25.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 +203 -0
- package/README.md +112 -62
- package/dist/index.js +1131 -688
- package/examples/J-164.html +7 -0
- package/package.json +4 -3
- package/rules/00-anti-patterns.md +8 -2
- package/rules/01-modes.md +2 -2
- package/rules/03-patterns.md +14 -4
- package/rules/05-copy.md +1 -1
- package/rules.index.json +7 -0
- package/templates/COMMAND.md.tmpl +145 -55
- package/templates/SKILL.md.tmpl +13 -1
- package/templates/command-metadata.json +5 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<!-- J-164 · An indexable page a link preview cannot read -->
|
|
2
|
+
<figure data-example="dont">
|
|
3
|
+
<div style="font-family:system-ui,-apple-system,'Segoe UI',sans-serif;font-size:13px;line-height:1.45;color:#1f1f1f;background:#f5f5f4;padding:16px;min-height:176px;box-sizing:border-box;"><div style="font-size:12px;color:#57534e;margin-bottom:6px">Sam, in a team chat:</div><div style="background:#fff;border:1px solid #d6d3d1;border-radius:8px;padding:10px;"><div style="color:#1a0dab;font-size:13px">tidewell.io/pricing</div><div style="border-left:3px solid #d6d3d1;margin-top:8px;padding-left:8px;font-size:12px;color:#4d5156">Tidewell<br>Home · Product · Docs · Pricing · Sign in</div></div><div style="font-size:11px;color:#5c5c5c;margin-top:8px">No preview tags: the card is the site's name and its navigation.</div></div>
|
|
4
|
+
</figure>
|
|
5
|
+
<figure data-example="do">
|
|
6
|
+
<div style="font-family:system-ui,-apple-system,'Segoe UI',sans-serif;font-size:13px;line-height:1.45;color:#1f1f1f;background:#f5f5f4;padding:16px;min-height:176px;box-sizing:border-box;"><div style="font-size:12px;color:#57534e;margin-bottom:6px">Sam, in a team chat:</div><div style="background:#fff;border:1px solid #d6d3d1;border-radius:8px;padding:10px;"><div style="color:#1a0dab;font-size:13px">tidewell.io/pricing</div><div style="border-left:3px solid #3f6212;margin-top:8px;padding-left:8px;font-size:12px;color:#1f1f1f"><b>Pricing · Tidewell</b><br><span style="color:#4d5156">Three plans, billed monthly or yearly. Cancel any time.</span></div></div><div style="font-size:11px;color:#5c5c5c;margin-top:8px">og:title and og:description: the card says what the page is.</div></div>
|
|
7
|
+
</figure>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jig-ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A design system for coding agents.
|
|
3
|
+
"version": "0.25.0",
|
|
4
|
+
"description": "A design system for coding agents. 160 numbered UI rules, brand x mode design tokens, and an installer for Claude Code, Codex, Cursor and opencode.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"CHANGELOG.md"
|
|
23
23
|
],
|
|
24
24
|
"engines": {
|
|
25
|
-
"node": ">=
|
|
25
|
+
"node": ">=22"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsup",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^22.0.0",
|
|
39
39
|
"tsup": "^8.3.0",
|
|
40
|
+
"tsx": "^4.23.15",
|
|
40
41
|
"typescript": "^5.6.0",
|
|
41
42
|
"vitest": "^2.1.0"
|
|
42
43
|
},
|
|
@@ -365,7 +365,7 @@ Without `viewport-fit=cover`, the browser keeps the page inside the safe area on
|
|
|
365
365
|
|
|
366
366
|
### D-115 The page scrolls sideways on a phone
|
|
367
367
|
❌ At phone width the whole page is wider than the screen — a data table, a long URL, an image, a `width: 100vw` element or a fixed-width block pushes it out, and the reader can drag the page left and right
|
|
368
|
-
✅ At every width, nothing makes the page wider than the screen. Content that is genuinely wider — a data table, a code block — scrolls inside its own container with a visible edge (`E-62`), and the page itself never does. `editorial` goes further and allows no scrolling regions on mobile at all (`M-01`).
|
|
368
|
+
✅ At every width, nothing makes the page wider than the screen. Content that is genuinely wider — a data table, a code block — scrolls inside its own container with a visible edge (`E-62`), and the page itself never does. `editorial` goes further and allows no scrolling regions on mobile at all (`M-01`): there a code block wraps its long lines and a wide table stacks its rows (`L-01`, step 6).
|
|
369
369
|
Judge it on a render, not in the source: at 360px, `document.documentElement.scrollWidth` must not be greater than `document.documentElement.clientWidth`. The usual causes are each one line to fix — `overflow-wrap: anywhere` on text the author does not control, `max-width: 100%` on media, `width: 100%` instead of `100vw` (which includes the scrollbar), and a wrapper with `overflow-x: auto` around anything tabular.
|
|
370
370
|
A page that scrolls sideways is not merely untidy. The reader's vertical swipes drift, the page slides half off the screen, and every line of text needs re-centring before it can be read.
|
|
371
371
|
|
|
@@ -376,6 +376,7 @@ Agents render the happy path. This section exists because that is the single mos
|
|
|
376
376
|
### E-28 Missing states
|
|
377
377
|
❌ A component with only a default appearance
|
|
378
378
|
✅ Every interactive element defines: `hover`, `focus-visible`, `active`, `disabled`. Every data view defines: loading, empty, error, and partial/truncated.
|
|
379
|
+
A browser makes an `<iframe>` a tab stop whether or not anything in it can be used. One that only shows something, with nothing inside to operate or scroll, is taken out of the tab order (`tabindex="-1"`) rather than given states it has no use for; a keyboard reader otherwise meets a stop with nothing to see. One the reader uses (a player, a map, a form) keeps its stop and shows focus.
|
|
379
380
|
|
|
380
381
|
### E-29 Focus removed without replacement
|
|
381
382
|
❌ `outline: none` with nothing in its place
|
|
@@ -411,7 +412,7 @@ Where disabling is genuinely right, put a message beside it or a tooltip on it e
|
|
|
411
412
|
❌ A toolbar of icon-only buttons whose meaning depends on recognising the glyph
|
|
412
413
|
✅ A visible text label beside the icon, or the icon plus label on the primary path.
|
|
413
414
|
This is **not** the same problem as `E-34`. An `aria-label` serves a screen reader and does nothing for a sighted user with low computer literacy, or anyone meeting an unfamiliar glyph. Both are required.
|
|
414
|
-
Icon-only is acceptable for a small set of near-universal glyphs
|
|
415
|
+
Icon-only is acceptable for a small set of near-universal glyphs and in `operator`, where repetition builds recognition — and even there, on first-run surfaces the label stays. Close, search and menu are examples, not the whole list: the test is whether most readers would name the glyph the same way without the label. A sun and moon for the theme, or a service's own widely known mark linking to it, usually pass; a funnel, a sparkle or a lightning bolt does not. An icon-only control still carries its name in text a screen reader reads (`E-34`).
|
|
415
416
|
|
|
416
417
|
### E-52 Unconventional controls
|
|
417
418
|
❌ A bespoke form field, checkbox or select that looks and behaves unlike every other one the user has met
|
|
@@ -713,6 +714,11 @@ A second copy of the positioning is a second thing to keep in step by hand, whic
|
|
|
713
714
|
✅ Concatenate the static routes unconditionally, and let a failed read yield the stale list rather than an empty one. When a list is genuinely empty, prove it before shipping.
|
|
714
715
|
An empty sitemap is not a missing sitemap. It is a positive claim, and the crawler believes it.
|
|
715
716
|
|
|
717
|
+
### J-164 An indexable page a link preview cannot read
|
|
718
|
+
❌ A link pasted into a message or a post that shows a bare address, or the site's name over a line of its navigation
|
|
719
|
+
✅ Every indexable page states its own preview: `og:title` and `og:description`, beside its `<title>` and meta description, and `og:type` for what it is (`article` for a post). An image and `og:url` take the page's full address: add them once the site has one, never as a relative path.
|
|
720
|
+
The card is met before the page, often by someone who never opens it. Without these tags each service builds the card from whatever it finds, and they find different things. A preview is copy, like the title and description: the change that rewrites the page rewrites its card (`J-120`), and a value the page already holds is read, not typed again (`J-126`).
|
|
721
|
+
|
|
716
722
|
---
|
|
717
723
|
|
|
718
724
|
## K. Safety at the interface
|
package/rules/01-modes.md
CHANGED
|
@@ -85,7 +85,7 @@ Resolved values: `02-tokens.md` — the option sets for type, spacing, radius an
|
|
|
85
85
|
- One hero maximum, at the top. A second full-viewport section is a second hero.
|
|
86
86
|
- Every page states its subject above the fold in text, not only in an image.
|
|
87
87
|
- Prose blocks are measure-capped even when the container is wide.
|
|
88
|
-
- No horizontal scrolling regions on mobile. Reflow instead.
|
|
88
|
+
- No horizontal scrolling regions on mobile. Reflow instead: a code block wraps its long lines, keeping its line breaks and indents, and a table too wide for the screen stacks its rows, each cell with its column's name (`L-01`, step 6).
|
|
89
89
|
- JavaScript: **none by default.** A content page ships no script unless a feature on it needs some: a theme toggle, a search box, a filter. That is a default, not an absolute zero. Each script names, in a comment, the feature and the decision or rule that asks for it, and a review judges whether the feature needs it and whether it is as small as the feature allows, not whether it exists. What the budget forbids is script no feature asks for: a framework runtime to render static text, a library for one effect, tracking bundled into the page. **Behaviour a rule requires is not a feature and is not counted**: a few lines whose only job is something a rule asks for and the browser does not give, such as `Escape` closing a `<details>` menu, or a tap outside closing a menu in a fixed header (`P-14`), are allowed in every mode, under 1 KB, with the rule named in a comment. Nothing else rides along in them.
|
|
90
90
|
|
|
91
91
|
---
|
|
@@ -190,7 +190,7 @@ Attempting to vary these by mode is a category error:
|
|
|
190
190
|
- **Accessibility floors.** Contrast, focus indication, target size, semantic markup. Identical in all three. `operator` being dense does not license a 24px tap target or a 3:1 body contrast.
|
|
191
191
|
- **Brand identity.** Palette, typeface, logo, voice.
|
|
192
192
|
- **State completeness.** Every mode renders loading, empty, error and disabled.
|
|
193
|
-
- **The anti-pattern file.** All
|
|
193
|
+
- **The anti-pattern file.** All 129 rules in it apply everywhere.
|
|
194
194
|
|
|
195
195
|
---
|
|
196
196
|
|
package/rules/03-patterns.md
CHANGED
|
@@ -564,10 +564,20 @@ everything else follows from.
|
|
|
564
564
|
fluid: they track the viewport between a floor and a ceiling with no media
|
|
565
565
|
query at all. A hand-written ladder of sizes per breakpoint reintroduces the
|
|
566
566
|
jumps the scale exists to remove, and goes stale the moment the scale changes.
|
|
567
|
-
5. **Content that is genuinely wider scrolls inside itself
|
|
568
|
-
|
|
569
|
-
(`E-62`), keeps its type size, and
|
|
570
|
-
|
|
567
|
+
5. **Content that is genuinely wider scrolls inside itself, except in
|
|
568
|
+
`editorial` on a phone.** A code block, a wide table, a long identifier: its
|
|
569
|
+
own container scrolls with a visible edge (`E-62`), keeps its type size, and
|
|
570
|
+
the page never scrolls sideways (`D-115`). `editorial` allows no scrolling
|
|
571
|
+
region on mobile (`M-01`), so there each one reflows instead:
|
|
572
|
+
- **A code block** keeps its line breaks and indents and wraps a long line
|
|
573
|
+
(`white-space: pre-wrap`, `overflow-wrap: anywhere`). What is copied is
|
|
574
|
+
the source as written, not the wrapped lines.
|
|
575
|
+
- **A table too wide to fit** stacks its rows: each row a block, each cell a
|
|
576
|
+
line with its column's name in front. It stays a `<table>` with its header
|
|
577
|
+
row, so a screen reader still reads it as one. Where it fits, it stays a
|
|
578
|
+
table: the switch is the width at which its columns stop fitting.
|
|
579
|
+
- **A long identifier** (a URL, a hash, a file path) breaks where it must
|
|
580
|
+
(`overflow-wrap: anywhere`).
|
|
571
581
|
6. **Each transition happens where the content needs it.** The width at which
|
|
572
582
|
three columns stop fitting is a property of the columns, not of a device. Set
|
|
573
583
|
it from the content, and expect the numbers to differ per component.
|
package/rules/05-copy.md
CHANGED
|
@@ -90,7 +90,7 @@ Whichever you choose, be consistent across sibling elements — a list where thr
|
|
|
90
90
|
Interface text is read in fragments, at a glance, in a space someone else's content has to fit too. An em dash is a pause the reader has to interpret: it stands in for a comma, a colon, a bracket or a full stop, and which one it is only becomes clear after reading past it. The punctuation that says exactly one thing is faster.
|
|
91
91
|
It is also the clearest tell of machine-written copy. Generated text reaches for the em dash far more often than a person does, and readers have learned to notice. Copy that reads as generated is copy the reader trusts less, whatever it says.
|
|
92
92
|
This is about interface strings: labels, buttons, headings, errors, empty states, help text, and the prose a page ships. Markdown counts where a framework renders it as a page, which is most of them (`src/content`, `content/`, MDX routes). A repository document does not: `README.md`, `CHANGELOG.md`, `AGENTS.md` and their kin are written for whoever works on the code, and so are your commit messages and this file.
|
|
93
|
-
A verbatim quotation keeps its own punctuation. Text a page quotes from another source (a document, a spec, a person), marked as a quotation with `<blockquote>` or `<q>`, is that source's words, and
|
|
93
|
+
A verbatim quotation keeps its own punctuation. Text a page quotes from another source (a document, a spec, a person), marked as a quotation with `<blockquote>` or `<q>`, is that source's words, and so is a program's own output or code shown as it is, in `<pre>`, `<code>`, `<samp>` or `<kbd>`. Changing either to satisfy this rule would misquote it. The page's own copy around the quotation, and any heading or label it gives it, is still held to the rule.
|
|
94
94
|
The en dash keeps its one job: ranges, where it is read as "to" (`2–10 seats`, `Mon–Fri`). That is not a pause, and it is not affected.
|
|
95
95
|
|
|
96
96
|
### I-87 Inconsistent vocabulary
|
package/rules.index.json
CHANGED
|
@@ -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`, `seo`, `ship`. Run the
|
|
9
|
+
**CLI-backed** — `install`, `update`, `init`, `check`, `explain`, `verdicts`, `gate`, `probe`, `seo`, `ship`, `checksum`. 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
|
|
@@ -261,10 +261,12 @@ The commands after this run unchanged.
|
|
|
261
261
|
### 1. Ask, two or three questions at a time
|
|
262
262
|
|
|
263
263
|
**This is a required interaction, not a suggestion.** Ask two or three questions,
|
|
264
|
-
then **wait**. Do not send a questionnaire. Do not
|
|
265
|
-
for approval on your first response — a spec synthesised from a one-line
|
|
266
|
-
and handed over for a yes is the failure this command exists to prevent,
|
|
267
|
-
looks exactly like the command working.
|
|
264
|
+
then **wait**. Do not send a questionnaire. Do not hand the owner a finished
|
|
265
|
+
spec for approval on your first response — a spec synthesised from a one-line
|
|
266
|
+
prompt and handed over for a yes is the failure this command exists to prevent,
|
|
267
|
+
and it looks exactly like the command working. (This is about what you ask the
|
|
268
|
+
owner, not where you write: when you do write the spec, it goes in
|
|
269
|
+
`.jig/specs/`, step 3.)
|
|
268
270
|
|
|
269
271
|
Ask about **structure**, not appearance. Colour, type scale and density were
|
|
270
272
|
settled at `init` and live in `{{config_file}}` and the token layer; re-deciding
|
|
@@ -320,13 +322,20 @@ Write the answer as `critique: each` or `critique: at-ship`.
|
|
|
320
322
|
|
|
321
323
|
### 2. Run `L-01`
|
|
322
324
|
|
|
323
|
-
Read `L-01 · Layout method` in `{{rules_path}}/03-patterns.md` and run its
|
|
325
|
+
Read `L-01 · Layout method` in `{{rules_path}}/03-patterns.md` and run its six
|
|
324
326
|
steps against the answers. The spec's structural half **is** that output — group,
|
|
325
|
-
order by importance, space from the inside out, align to a grid,
|
|
326
|
-
hierarchy plainly enough that the squint question can be answered against it
|
|
327
|
+
order by importance, space from the inside out, align to a grid, state the
|
|
328
|
+
hierarchy plainly enough that the squint question can be answered against it,
|
|
329
|
+
and decide how it collapses. Step 6 is where the phone composition comes from,
|
|
330
|
+
including what this mode does with a code block or table wider than the screen.
|
|
327
331
|
|
|
328
332
|
### 3. Write it down — one composition per screen size
|
|
329
333
|
|
|
334
|
+
**Write it at `.jig/specs/<name>.spec.md` from the first draft**, with
|
|
335
|
+
`confirmed: false`, never in a scratchpad or a temporary file. Every check the
|
|
336
|
+
owner relies on reads that file, the check in 3c and the gate among them; a
|
|
337
|
+
draft kept elsewhere is checked by nothing, and reaches the owner unchecked.
|
|
338
|
+
|
|
330
339
|
`.jig/specs/<surface-slug>.spec.md`. Frontmatter carries what can be compared to
|
|
331
340
|
a built page; prose carries the reasoning that no check can read.
|
|
332
341
|
|
|
@@ -340,6 +349,7 @@ phone as a squeezed desktop.
|
|
|
340
349
|
---
|
|
341
350
|
feature: sign in to an existing account # the task, not the screen
|
|
342
351
|
surface: login page
|
|
352
|
+
route: /login/ # optional: where the page is served, when the surface's name does not say
|
|
343
353
|
mode: product # from {{config_file}} — state it, never leave it implied
|
|
344
354
|
confirmed: false # only the user sets this to true; see below
|
|
345
355
|
sizes: # phone first. Each size is a whole composition, not a diff.
|
|
@@ -384,6 +394,10 @@ critique: each # optional: each, or at-ship; left out, the project's
|
|
|
384
394
|
---
|
|
385
395
|
```
|
|
386
396
|
|
|
397
|
+
- **Only these fields.** The front matter holds the fields above and nothing
|
|
398
|
+
else; the gate refuses one it does not read, since nothing would check what it
|
|
399
|
+
says. Everything else a spec has to say (where its words come from, a round's
|
|
400
|
+
history, a note for `make`) goes in the prose below it.
|
|
387
401
|
- **`phone` is written first, and in full.** It is the most common screen, and the
|
|
388
402
|
one a derived composition fails on worst.
|
|
389
403
|
- **`motion:` lists every movement, and why it moves.** Each names what moves, its
|
|
@@ -471,7 +485,7 @@ to the spec, never the spec to the decisions.
|
|
|
471
485
|
- **"The design system decides" is not a value.** When the user defers — "you decide",
|
|
472
486
|
"whatever Jig says", "leave it to the system" — that is an instruction to look the
|
|
473
487
|
answer up, not a phrase to copy into the spec. Search in this order: the `P-`
|
|
474
|
-
pattern for the component (`{{command_prefix}}explain <component>`), `L-01`'s
|
|
488
|
+
pattern for the component (`{{command_prefix}}explain <component>`), `L-01`'s six
|
|
475
489
|
steps, then the rules. Write the composition that lookup gives — "stacked, Pro
|
|
476
490
|
first", "Menu button, links in a vertical list" — and cite the id it came from.
|
|
477
491
|
Three of six specs in a live run wrote "arrangement decided by design system" into
|
|
@@ -489,10 +503,9 @@ to the spec, never the spec to the decisions.
|
|
|
489
503
|
Step 3b checks the spec against the decisions. Nothing after it checks the spec
|
|
490
504
|
against the owner's words or against the facts it states: `make` builds a
|
|
491
505
|
confirmed spec as written, and `critique` compares the page to the spec, never
|
|
492
|
-
the spec to what is true.
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
cannot catch these, for the same reason a builder cannot review its own page.
|
|
506
|
+
the spec to what is true. An error a confirmed spec carries (a feature the
|
|
507
|
+
product does not have, a step in the wrong order) is built into the page, and the
|
|
508
|
+
owner confirmed it without knowing. The writer cannot catch these, for the same reason a builder cannot review its own page.
|
|
496
509
|
|
|
497
510
|
**Delegate to a subagent.** Give it the spec, `DECISIONS.md`, the owner's messages
|
|
498
511
|
from this conversation, and read access to the project. Not your reasoning, and
|
|
@@ -501,7 +514,7 @@ not your summary of what the owner meant. It writes
|
|
|
501
514
|
|
|
502
515
|
```json
|
|
503
516
|
{
|
|
504
|
-
"spec": "
|
|
517
|
+
"spec": "`{{scripts_path}} checksum <name>` prints it: the spec with its confirmed: and mockup: lines emptied",
|
|
505
518
|
"quotes": [
|
|
506
519
|
{ "quote": "Only where the steps differ by path", "said": "the owner, this conversation", "holds": true }
|
|
507
520
|
],
|
|
@@ -538,8 +551,8 @@ reader the earlier record and the spec's diff since it. A quotation or fact whos
|
|
|
538
551
|
line did not change, and whose source has not changed since (`git diff` on that
|
|
539
552
|
file), carries over as recorded; the reader checks the changed lines, the new
|
|
540
553
|
ones, and any fact whose source moved. It still writes the whole record, with
|
|
541
|
-
the new checksum.
|
|
542
|
-
|
|
554
|
+
the new checksum. Opening every source again on each round costs the most on
|
|
555
|
+
exactly the lines nobody touched. **If you cannot
|
|
543
556
|
delegate, write the record yourself, with `"reader": "the writer"` in it, and say
|
|
544
557
|
in the confirmation message that nobody else checked the spec.** That is an
|
|
545
558
|
honest gap the owner can close by reading more closely; a check you ran on your
|
|
@@ -584,9 +597,37 @@ they confirm.
|
|
|
584
597
|
renames a region at any size, the approved drawing no longer shows the page: set
|
|
585
598
|
`mockup: pending`, and `{{command_prefix}}mockup` redraws it for the owner. The gate
|
|
586
599
|
checks it for a drawing in frames: while `mockup: approved`, each region the spec lists must be labelled in
|
|
587
|
-
its size's frame of the drawing at `mockup_at:`.
|
|
588
|
-
|
|
589
|
-
|
|
600
|
+
its size's frame of the drawing at `mockup_at:`. Otherwise a spec can go through
|
|
601
|
+
several confirmed rounds after its drawing was approved and still say `approved`
|
|
602
|
+
over a drawing of none of them.
|
|
603
|
+
|
|
604
|
+
### Before you stop: what the gate holds a spec to
|
|
605
|
+
|
|
606
|
+
With the Stop hook, each of these keeps the session open until it is fixed. Get
|
|
607
|
+
them right as you write, not after the gate names them:
|
|
608
|
+
|
|
609
|
+
- **A spec writes under `.jig/` only.** A change to `DECISIONS.md`, a stylesheet or
|
|
610
|
+
a page is for `{{command_prefix}}decide`, `make` or `tweak`: record in the spec
|
|
611
|
+
what the owner said, and hand the change on.
|
|
612
|
+
- **The front matter holds Jig's fields and nothing else.** Anything more goes in
|
|
613
|
+
the prose below it.
|
|
614
|
+
- **Quotation marks within a few words after "owner", or after "the user said",
|
|
615
|
+
mark the owner's words.** What is inside them must be what the owner said: in
|
|
616
|
+
this conversation, in `DECISIONS.md`, or already in the spec. To name a
|
|
617
|
+
decision, a heading or a file near the word "owner", leave the quotation marks
|
|
618
|
+
off.
|
|
619
|
+
- **`<name>.checked.json` is of the spec as it stands.** Its `spec` value matches
|
|
620
|
+
`{{scripts_path}} checksum <name>`, so an edit after the check means a second
|
|
621
|
+
check, of what changed.
|
|
622
|
+
- **Each fact has one `source`:** a path from the project root, `path:line`, or a
|
|
623
|
+
URL, and nothing else. The file exists and the line is in it. Two places are two
|
|
624
|
+
facts; what you found at the source goes in `note`.
|
|
625
|
+
- **`holds` is `true`, `false` or `"unchecked"`, with a `note` for anything but
|
|
626
|
+
`true`.** A fact found false is fixed in the spec before the owner is asked.
|
|
627
|
+
- **`confirmed: true` only on the owner's yes, in this session, to the sheet you
|
|
628
|
+
showed them.** Words in the request that started the session came before the
|
|
629
|
+
sheet existed, and are not a reply to it, even when they say the owner
|
|
630
|
+
confirms. Show the sheet and stop.
|
|
590
631
|
|
|
591
632
|
## mockup
|
|
592
633
|
|
|
@@ -828,9 +869,9 @@ confirmed again.
|
|
|
828
869
|
Record where the approved drawing is in `mockup_at:` — the file path, or the
|
|
829
870
|
Figma or Stitch link. Only their own response counts; your summary of the
|
|
830
871
|
drawing, or no objection, does not. **A condition they attach goes into the
|
|
831
|
-
spec first**, in their words, on the lines it governs
|
|
832
|
-
|
|
833
|
-
|
|
872
|
+
spec first**, in their words, on the lines it governs. An approval "with three
|
|
873
|
+
columns at 1280 and wider" whose condition lives only in the conversation is
|
|
874
|
+
not what `make` builds: it reads the spec, and may put the switch anywhere.
|
|
834
875
|
- The user says to skip it → `mockup: skipped — "<their words>"`. For a change
|
|
835
876
|
small enough that drawing it costs more than building it, that is a reasonable
|
|
836
877
|
call; it is still theirs to make.
|
|
@@ -838,6 +879,22 @@ confirmed again.
|
|
|
838
879
|
With the Stop hook, the gate reads the quotation after `approved` or `skipped` and
|
|
839
880
|
holds it to what the owner said in this session.
|
|
840
881
|
|
|
882
|
+
### Before you stop: what the gate holds a mockup to
|
|
883
|
+
|
|
884
|
+
- **A mockup writes under `.jig/` only:** the drawing in `.jig/mockups/`, and the
|
|
885
|
+
spec's `mockup:` and `mockup_at:` lines.
|
|
886
|
+
- **The drawing shows what the spec lists**, at every size, and either side of each
|
|
887
|
+
switch the page crosses. The gate checks this before you put it to the owner.
|
|
888
|
+
- **`mockup:` stays `pending` until the owner answers.** Then it is `approved` or
|
|
889
|
+
`skipped`, followed by their words in quotation marks: the reply that approves
|
|
890
|
+
this drawing, or says to skip it. Words that only say they looked at it, or
|
|
891
|
+
that are about something else, are not their word on the drawing. Ask them
|
|
892
|
+
whether they approve it for `make`, and quote the answer.
|
|
893
|
+
- **`mockup_at:` names the approved drawing**, a file under `.jig/mockups/` that
|
|
894
|
+
exists, or a link.
|
|
895
|
+
- **An approved drawing does not change.** Changing it after approval sets
|
|
896
|
+
`mockup: pending` and puts it to the owner again.
|
|
897
|
+
|
|
841
898
|
Then stop. The next step is `{{command_prefix}}make`.
|
|
842
899
|
|
|
843
900
|
## make
|
|
@@ -887,10 +944,9 @@ and `make` is where decisions are carried out, not taken. When the build shows a
|
|
|
887
944
|
decision is wrong, missing or in conflict with the spec, stop and say so: the owner
|
|
888
945
|
changes it through `{{command_prefix}}decide`, or `{{command_prefix}}tweak` for a
|
|
889
946
|
small change to a built page. A note that a decision has been carried out belongs in
|
|
890
|
-
the spec, under `deviations:` or its as-built notes, not in the decision.
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
written. The gate stops a `make` session that changed `DECISIONS.md`.
|
|
947
|
+
the spec, under `deviations:` or its as-built notes, not in the decision. A build
|
|
948
|
+
that adds a decision, or rewrites one to match what it just built, leaves the next
|
|
949
|
+
critique judging the page against text the build wrote. The gate stops a `make` session that changed `DECISIONS.md`.
|
|
894
950
|
|
|
895
951
|
Build V1 only. Nothing in `later:`, however little extra it looks.
|
|
896
952
|
|
|
@@ -1086,11 +1142,10 @@ refuses a `ruled` with no ruling or with one the file does not hold. A ruled
|
|
|
1086
1142
|
verdict is reported, and counted apart (`ruled=`); it is not a finding, and it
|
|
1087
1143
|
does not go back to `make`, which could only leave it as it is. **Put the field
|
|
1088
1144
|
in each arm's brief, with its shape:** `{ "id": …, "verdict": "ruled", "ruling":
|
|
1089
|
-
"<the decision's heading>", "reason": … }`.
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
in prose: every count was inflated, and the real findings were harder to see.
|
|
1145
|
+
"<the decision's heading>", "reason": … }`. An arm that names the decision only
|
|
1146
|
+
in `reason` has every ruled verdict refused by `verdicts`, and the parent is left
|
|
1147
|
+
to write the field into a file it is not to edit. Rulings counted as findings
|
|
1148
|
+
inflate every count, and the real findings are harder to see.
|
|
1094
1149
|
A rule the page breaks with no decision behind it is a `finding`, however sure
|
|
1095
1150
|
you are the owner would agree.
|
|
1096
1151
|
|
|
@@ -1168,11 +1223,17 @@ Each reader arm writes its own file. The arm reports back only that it wrote it.
|
|
|
1168
1223
|
**`differences` holds what no rule names:** the page against its spec (step 3),
|
|
1169
1224
|
or against a source it quotes or teaches. Each counts as a finding. Never carry
|
|
1170
1225
|
one as a verdict under an id you made up: `verdicts` refuses an id the corpus
|
|
1171
|
-
does not hold, and
|
|
1172
|
-
(`page-vs-corpus-1`), which the parent then had to move by hand. `ruled` is for a
|
|
1226
|
+
does not hold (`page-vs-corpus-1`), and the parent is left to move it by hand. `ruled` is for a
|
|
1173
1227
|
rule a decision overrides; a decision in `decisions.json` is judged `ok`,
|
|
1174
1228
|
`finding` or `n/a`, never ruled.
|
|
1175
1229
|
|
|
1230
|
+
**One problem, one entry.** A difference goes under `differences` only when no
|
|
1231
|
+
rule and no decision holds it. A stale figure is `I-148`'s finding; a page
|
|
1232
|
+
paraphrasing a source the owner decided it quotes is that decision's. File it
|
|
1233
|
+
there and nowhere else, and let the two arms' entries agree rather than repeat.
|
|
1234
|
+
One stale number filed as a rule's finding, a decision's, and a difference in
|
|
1235
|
+
each arm is four findings for one problem, and every count built on them is wrong.
|
|
1236
|
+
|
|
1176
1237
|
**And the project's own decisions, one verdict each**, in
|
|
1177
1238
|
`.jig/critique/<surface>/decisions.json`:
|
|
1178
1239
|
|
|
@@ -1227,9 +1288,13 @@ The probe opens the phone menu, presses Escape, measures sideways scroll, and re
|
|
|
1227
1288
|
whether the styles and tokens actually applied. `verdicts` refuses `rendered: true`
|
|
1228
1289
|
without a probe at each width, and refuses an `ok` the probe contradicts — a `P-14`
|
|
1229
1290
|
"ok" on a menu that did not open, a `D-115` "ok" on a page that scrolls sideways. It
|
|
1230
|
-
also
|
|
1231
|
-
|
|
1232
|
-
|
|
1291
|
+
also names what the page fails whatever the verdicts say: it renders in the
|
|
1292
|
+
browser's default font, shows `${` or an em dash to readers, or serves a missing
|
|
1293
|
+
or overlong title or description. Those are findings on the page, listed under
|
|
1294
|
+
`!` and counted as `measured=` on the `JIG_VERDICTS` line. Put each in the report;
|
|
1295
|
+
make or tweak fixes them, and the critique leaves the page as it is.
|
|
1296
|
+
Never write a probe file yourself: when it disagrees with you, it is the review
|
|
1297
|
+
that is wrong.
|
|
1233
1298
|
Then run:
|
|
1234
1299
|
|
|
1235
1300
|
```
|
|
@@ -1328,9 +1393,8 @@ control or claim the page carries that no line of the spec asks for is a
|
|
|
1328
1393
|
difference too, however accurate it is. The owner confirmed the spec, not the
|
|
1329
1394
|
page; content the spec never held is content they never approved, and every
|
|
1330
1395
|
later tweak and critique works from a spec that no longer says what is there.
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
no rule. Report it among the findings as a difference from the spec, naming the
|
|
1396
|
+
An accurate paragraph with no spec line and no deviation breaks no rule, so a
|
|
1397
|
+
critique that reads only the spec against the page sees it and files nothing. Report it among the findings as a difference from the spec, naming the
|
|
1334
1398
|
content and where it is: the fix is a tweak that writes it into the spec for the
|
|
1335
1399
|
owner to confirm, or removes it.
|
|
1336
1400
|
|
|
@@ -1462,10 +1526,10 @@ its own line, `B-106`), there is nothing to decide; do not invent a decision.
|
|
|
1462
1526
|
|
|
1463
1527
|
The owner's words are the ones tweak.json records as `change`. A `**Why:**` you
|
|
1464
1528
|
add quotes them, in quotation marks, and nothing else; what you read into them,
|
|
1465
|
-
or into a screenshot the owner pointed at, goes under `**Why (inferred):**`.
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1529
|
+
or into a screenshot the owner pointed at, goes under `**Why (inferred):**`. A
|
|
1530
|
+
tweak shown a picture that writes an exception "given directly by the owner … by
|
|
1531
|
+
reference rather than words" records a ruling nobody gave, and every later
|
|
1532
|
+
critique judges the page by it. The gate stops a
|
|
1469
1533
|
tweak whose new `**Why:**` quotes words tweak.json does not hold.
|
|
1470
1534
|
|
|
1471
1535
|
**A decision you record or amend covers the whole page, not the one place it
|
|
@@ -1473,9 +1537,9 @@ was raised.** The owner rules on an instance ("keep this apostrophe curly") and
|
|
|
1473
1537
|
the decision states a kind ("every apostrophe, curly"). Before you build, look
|
|
1474
1538
|
through the page, and the chrome it shares, for every other instance of that
|
|
1475
1539
|
kind, and bring each one in line in the same change; name them in your report.
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1540
|
+
A tweak that records "every apostrophe and quotation mark curly" and fixes only
|
|
1541
|
+
the one the critique named leaves the rest of the page's straight marks for the
|
|
1542
|
+
next critique to find.
|
|
1479
1543
|
|
|
1480
1544
|
### 2. Bring the spec in line, if it disagrees
|
|
1481
1545
|
|
|
@@ -1526,6 +1590,21 @@ judges it, and leave the verdicts alone. The page then owes a critique, and
|
|
|
1526
1590
|
`{{command_prefix}}ship` will not pass until one has judged it. Never defer it
|
|
1527
1591
|
on your own account: the gate refuses a deferral nobody gave.
|
|
1528
1592
|
|
|
1593
|
+
### Before you stop: what the gate holds a tweak to
|
|
1594
|
+
|
|
1595
|
+
- **The spec is confirmed, and its mockup approved or skipped.** A page that has
|
|
1596
|
+
neither goes through `mockup` and `make`.
|
|
1597
|
+
- **The page's regions and its approved drawing stay as they were.** A change to
|
|
1598
|
+
what the page holds goes through `spec` and `mockup`.
|
|
1599
|
+
- **`tweak.json` holds `at`, `change`, `ids` and, when the owner said so,
|
|
1600
|
+
`deferred`, and no other field.**
|
|
1601
|
+
- **`change` is the owner's words for the change, exactly as they gave them in
|
|
1602
|
+
this session.** Nothing goes before or after them, not even a label such as
|
|
1603
|
+
"the owner's words:". What you made of them goes in the spec's Tweak entry.
|
|
1604
|
+
- **`ids` names at least one rule or decision**, and each is re-judged by a reader
|
|
1605
|
+
that did not make the change, unless `deferred` quotes the owner telling you to
|
|
1606
|
+
leave it for later (or is `true` where the page's critique waits for ship).
|
|
1607
|
+
|
|
1529
1608
|
### Finish
|
|
1530
1609
|
|
|
1531
1610
|
Report what changed, in the owner's words; the decision and spec lines you
|
|
@@ -1547,7 +1626,9 @@ Build the project the way it ships, then run `{{scripts_path}} ship`. It runs
|
|
|
1547
1626
|
`check --all --ci` and `seo`, and reads every confirmed spec's critique: a page
|
|
1548
1627
|
is owed a critique when it has none, when its page changed after it was judged,
|
|
1549
1628
|
when a tweak deferred its re-judge, when its verdicts are incomplete, or when a
|
|
1550
|
-
finding stands that the owner has not ruled on.
|
|
1629
|
+
finding stands that the owner has not ruled on. A page that is as judged but
|
|
1630
|
+
whose stylesheets or scripts changed is owed only a re-probe
|
|
1631
|
+
(`{{scripts_path}} verdicts <page> --reprobe`), not a critique. It prints each page's state and
|
|
1551
1632
|
a `JIG_SHIP:` line, and exits non-zero until the project is ready. A spec that
|
|
1552
1633
|
is not confirmed yet is in progress and not held against the ship. Nor is a spec
|
|
1553
1634
|
another has replaced: `superseded_by: <spec>` in its front matter leaves its page
|
|
@@ -1617,14 +1698,15 @@ review, never by the builder editing the last one.
|
|
|
1617
1698
|
When a `critique` or `tweak` session stops, the gate records its verdicts in
|
|
1618
1699
|
`.jig/critique/<surface>/verdicts.lock`. That happens after you have committed, so
|
|
1619
1700
|
if your verdicts are committed it stops you once more to commit the lock on its
|
|
1620
|
-
own; the next stop passes.
|
|
1701
|
+
own; the next stop passes. The same goes for a page's probe files: when the page
|
|
1702
|
+
changed since they were taken, the gate renders it again as you stop, and if
|
|
1703
|
+
your work is committed it stops you once to commit them on their own.
|
|
1621
1704
|
|
|
1622
1705
|
**The lock is the gate's to write, never yours.** When the gate is wrong (it
|
|
1623
1706
|
misreads the session, or asks for something the procedure does not), say what it
|
|
1624
|
-
got wrong, leave the lock as it is, and report the work as held by the gate.
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
agent can write is still a record of nothing.
|
|
1707
|
+
got wrong, leave the lock as it is, and report the work as held by the gate. A
|
|
1708
|
+
lock computed by hand may even be right, and a lock an agent can write is still a
|
|
1709
|
+
record of nothing.
|
|
1628
1710
|
|
|
1629
1711
|
It judges the critiques this session touched: any with a file changed since the
|
|
1630
1712
|
session began, and the current spec's surface when you ran `critique`. Another
|
|
@@ -1645,6 +1727,14 @@ It exists because every "run check" and "run verdicts" step in these procedures
|
|
|
1645
1727
|
skipped in a live run, and pages that did not render their styles were reported
|
|
1646
1728
|
clean.
|
|
1647
1729
|
|
|
1730
|
+
## checksum
|
|
1731
|
+
|
|
1732
|
+
Run `{{scripts_path}} checksum <spec>`, passing the argument through, and report the
|
|
1733
|
+
value it prints. It is the `spec` field of `.jig/specs/<name>.checked.json`: the
|
|
1734
|
+
spec's checksum with its `confirmed:` and `mockup:` lines emptied, which the owner's
|
|
1735
|
+
answer sets after the check. Never compute it yourself: a hand-computed value is
|
|
1736
|
+
easily wrong, and a wrong one reads as a spec that changed after its check.
|
|
1737
|
+
|
|
1648
1738
|
## verdicts
|
|
1649
1739
|
|
|
1650
1740
|
Run `{{scripts_path}} verdicts <surface>`, passing the argument through, and report its
|
|
@@ -1852,9 +1942,9 @@ weighs it as the team's and applies the rule where the team never would.
|
|
|
1852
1942
|
|
|
1853
1943
|
What you add to explain a reason, work out from it, or carry over from what the
|
|
1854
1944
|
owner said elsewhere goes in its own paragraph, `**Why (inferred):**`, which says it
|
|
1855
|
-
is yours.
|
|
1856
|
-
|
|
1857
|
-
|
|
1945
|
+
is yours. A reason that reads "given directly by the owner" and goes on in the
|
|
1946
|
+
agent's own sentences leaves a later agent unable to tell which half to weigh as
|
|
1947
|
+
the team's. The gate checks each decision this session wrote or changed: its
|
|
1858
1948
|
`Why` is quoted, `not given`, or labelled inferred.
|
|
1859
1949
|
|
|
1860
1950
|
**Record what is still open in its own section, at the end:**
|
package/templates/SKILL.md.tmpl
CHANGED
|
@@ -36,8 +36,20 @@ cite the number when you follow or deliberately break one.
|
|
|
36
36
|
`.jig/specs/<name>.checked.json` (its step 4), `{{command_prefix}}mockup`'s
|
|
37
37
|
size by size against the spec (its step 3, rendering every frame). Confirm
|
|
38
38
|
only what you checked yourself, and name to the owner what you could not.
|
|
39
|
+
**Recording the owner's word?** A spec confirmed, a drawing approved or
|
|
40
|
+
skipped, a re-judge left for later, a tweak's change, a decision's reason:
|
|
41
|
+
each is the owner's words, quoted as they said them, and words that say that
|
|
42
|
+
thing. A remark about the layout is not a skip of the mockup; a yes to one
|
|
43
|
+
question is not a yes to the spec. When they have not said it, ask. What you
|
|
44
|
+
read into their words goes under a label that says it is yours
|
|
45
|
+
(`**Why (inferred):**`), never inside their quotation marks.
|
|
46
|
+
**Recording something Jig has no field for?** Write it in prose, below a
|
|
47
|
+
spec's front matter or in the report. Never invent a front-matter key, a
|
|
48
|
+
verdict id or a JSON field for it: nothing reads an invented one, so what it
|
|
49
|
+
says is lost to every check. If it keeps recurring, tell the owner, since
|
|
50
|
+
Jig may be missing a field.
|
|
39
51
|
**Building a screen rather than a single component?** Read `L-01 · Layout
|
|
40
|
-
method` in `{{rules_path}}/03-patterns.md` and run its
|
|
52
|
+
method` in `{{rules_path}}/03-patterns.md` and run its six steps before
|
|
41
53
|
writing any markup. It is a procedure, not a component, so step 4 never
|
|
42
54
|
selects it and nothing else will. (`explain L-01` prints it too, but the
|
|
43
55
|
file is the source — do not skip the step if the command is unavailable.)
|
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
"argumentHint": "",
|
|
35
35
|
"status": "available"
|
|
36
36
|
},
|
|
37
|
+
"checksum": {
|
|
38
|
+
"description": "Print a spec's checksum: the value its `.checked.json` records as `spec`, so a spec's reader never computes it by hand.",
|
|
39
|
+
"argumentHint": "<spec>",
|
|
40
|
+
"status": "available"
|
|
41
|
+
},
|
|
37
42
|
"verdicts": {
|
|
38
43
|
"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.",
|
|
39
44
|
"argumentHint": "<surface>",
|