jig-ui 0.7.1 → 0.8.1

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 ADDED
@@ -0,0 +1,822 @@
1
+ # Changelog
2
+
3
+ ## 0.8.1
4
+
5
+ Both fixes here are the same shape: 0.8.0 corrected the instance it was looking
6
+ at and left the class alone, and in each case the commit message claimed a
7
+ verification that had only checked the file it had just edited. Both were found
8
+ by inspecting the published tarball rather than the working tree.
9
+
10
+ ### Fixed
11
+
12
+ - **Four rule files still shipped author notes.** 0.7.x removed the section
13
+ headed "Notes for the author (not for the agent)" from `00-anti-patterns.md`
14
+ after an agent read it as "license to go tighter than the shared default" and
15
+ halved the radius scale on that authority. `01-modes.md`, `03-patterns.md`,
16
+ `04-principles.md` and `05-copy.md` carried a section under the same heading
17
+ and kept shipping it to every agent that installed Jig.
18
+
19
+ `01-modes.md` was the worst of them: it told the reader to **"Change them in
20
+ `tokens/mode.*.css`"** and closed with **"it is your call"** — an invitation
21
+ to edit the token layer, which is the one thing Jig's architecture reserves
22
+ for a human. `03-patterns.md` described navigation and cards as "deliberately
23
+ absent… add them once you have built enough". `05-copy.md` pointed at
24
+ `03-brand.md`, which does not exist. The content moves to
25
+ `docs/house-positions.md` unchanged; only its audience changes.
26
+
27
+ - **`02-tokens.md` contradicted its own Tailwind fix 352 lines earlier.** The
28
+ compatibility table near the top still read *"Tailwind v4 | Wrap in
29
+ `@theme { }`"* — the exact instruction the section below it retracts with
30
+ *"Earlier versions of this file told you to, and Tailwind rejects it
31
+ outright."* The table is the part an agent reads first, so the correction
32
+ shipped underneath the error it corrected. A second, softer restatement
33
+ ("the same file can be wrapped in `@theme`") is gone too. `@theme` now first
34
+ appears in the section that explains it correctly.
35
+
36
+ - **`jig explain` rendered a dangling `---` in 14 of the 15 pattern and mode
37
+ specs.** `parse.ts` has always dropped bare separator lines; `specs.ts` is a
38
+ separate code path and never did, so every `P-` and `M-` spec that is
39
+ followed by a separator in the source carried it into the rendered body,
40
+ between the last paragraph and the footer. Spotted on `P-12`, but it was
41
+ never about `P-12`. Table separators (`| --- |`) are untouched.
42
+
43
+ - **`jig init`'s refusal without a terminal offered a way out that does not
44
+ work.** The message ended *"(To choose the mode without a terminal, write
45
+ jig.config.json first — init honours it.)"* The guard runs before any config
46
+ is read, so a config alone still exits 1. The sentence was true about mode
47
+ selection and false in a paragraph about not having a terminal, so it read as
48
+ a third alternative when it is a modifier on the first — a cold agent
49
+ followed it literally, hit the identical error, and allocated a
50
+ pseudo-terminal with Python's `pty` to get past it. It now says a config does
51
+ not replace `--yes`, and what the two do together. Nothing covered this path;
52
+ three tests now do.
53
+
54
+ ### Added
55
+
56
+ - **`check-tokens` rule 13 — no shipped rule file addresses the author.** The
57
+ guard that should have existed for the 0.7.x fix. It reads `rules/` from disk
58
+ rather than a hardcoded list, so a rule file added later cannot escape it the
59
+ way those four did, and it checks the second-person tells ("your taste", "it
60
+ is your call", "my inclination is") as well as the heading, because a rename
61
+ would otherwise defeat it. Each tell is verified to fire on reintroduction.
62
+
63
+ ## 0.8.0
64
+
65
+ Everything here was found by handing Jig to agents that had never seen it and
66
+ asking them to build something real — its own documentation site. None of it
67
+ was found by the test suite, which passed throughout.
68
+
69
+ Minor rather than patch: `explain` prints lines it did not print before, and
70
+ `init` can write a file it did not write before.
71
+
72
+ ### Fixed
73
+
74
+ - **`jig explain` discarded most of the reasoning in the rules.** The parser
75
+ kept the first `❌` and the first `✅` and threw away everything else. **41 of
76
+ 104 rules carry prose outside that pair — 103 lines.** `A-04` explains why
77
+ shadow-only definition cannot hold 3:1; `C-22` loses sixteen lines; `C-49`
78
+ leads with a three-row table distinguishing when a link needs colour, when it
79
+ needs an underline, and when neither is required — and its pair alone says
80
+ "keep the underline". All of it shipped in the tarball, installed into every
81
+ skill directory, and was unreachable through the command built to read it.
82
+
83
+ - **`02-tokens.md` documented a Tailwind arrangement that fails.** It told every
84
+ reader to write `@theme { @import "./jig/theme.css"; }` and claimed it yields
85
+ `bg-surface`, `rounded-surface`, `p-card` and `text-body` as utilities.
86
+ Tailwind 4 rejects it: *"@theme blocks must only contain custom properties or
87
+ @keyframes."* The tokens cannot move into `@theme` regardless — it requires
88
+ them top-level and unnested, while they live in `:root` and are redeclared
89
+ under `[data-theme="dark"]` and a `prefers-color-scheme` query, which is what
90
+ makes dark mode work. The flat import is what works, and is what `init`
91
+ already wired without being told.
92
+
93
+ - **`02-tokens.md` named a token location the tool had stopped using.** It said
94
+ tokens live at `.jig/tokens/`, that this was "the only location, in every
95
+ scope and every project", and that "nothing relocates them" — while `init`
96
+ printed `Token layer: src/styles/jig/` in the same run. `check-tokens` rule 4
97
+ hardcoded the same path in a comment anticipating and forbidding this exact
98
+ change, so the guard held the claim in place and would have failed the build
99
+ on anyone correcting it. Shipped in 0.7.1.
100
+
101
+ - **A section headed "Notes for the author (not for the agent)" shipped to every
102
+ agent.** The heading was a label, not a mechanism: the file is in the tarball
103
+ and installs into every skill directory. A probe read it, quoted the
104
+ `A-07`/`A-08` line back as "the author's own notes… license to go tighter than
105
+ the shared default", and halved the radius scale on that authority. Moved to
106
+ `docs/house-positions.md`.
107
+
108
+ - **The published package shipped no changelog**, despite the repo holding 32KB
109
+ of one. Staging and `files` now change together, which the tarball guard
110
+ already enforced.
111
+
112
+ - **`A-01` had no stated scope for "then ask".** Two agents given the same brief
113
+ split on it — one shipped the unbranded default and deferred, the other
114
+ proposed a hue and argued the proposal *was* the ask — both citing the same
115
+ conflict-resolution clause. The rule now says a proposal is a question with a
116
+ suggested answer, not a decision.
117
+
118
+ - **`B-11` capped line length and stated no floor**, so a column could be halved
119
+ indefinitely and still pass. Measured on the documentation site: two
120
+ side-by-side panels rendering prose at 41 characters, in a mode whose
121
+ `--measure-prose` selects 68. The 40–80 band existed in `02-tokens.md` but not
122
+ in the file an agent reads for a line-length decision.
123
+
124
+ ### Added
125
+
126
+ - **Three detectors for the anti-slop rules, and a self-check that names them.**
127
+ Section A is Jig's answer to "what is AI slop" — fourteen rules for the
128
+ defaults a model reaches for when nothing was specified. **Three had
129
+ detectors.** The other eleven were enforced by one self-check question —
130
+ *"would this look different from a generic template?"* — which an agent that
131
+ has just produced a generic template answers yes to, and whose `A-01 → A-10`
132
+ range silently excluded `A-58`, `A-59`, `A-60` and `A-67`.
133
+
134
+ Jig's own documentation site shipped `<span aria-hidden="true">❌</span> Don't`
135
+ in the chrome of all 104 rule pages with `check --all` reporting nothing.
136
+
137
+ `A-05` (emoji as iconography) and `A-10` (placeholder content) were never
138
+ judgment calls — an emoji in a text node is a regex, and so is "lorem ipsum".
139
+ `A-09` (marketing voice) is the first **mode-gated** detector: it fires in
140
+ `product` and `operator`, stays silent in `editorial` where that register
141
+ belongs, and stays silent when no mode is declared rather than guessing.
142
+
143
+ The remaining eight stay judgment. Detecting "a container around every group"
144
+ needs to know what an element is and how heavy it looks, and six noisy checks
145
+ that fire on correct code is how a linter gets switched off. What reaches them
146
+ is the self-check, rewritten to name each tell the way every other item does.
147
+
148
+ - **`check` reports what it examined.** `0 errors · 104 rules, 0 fired` was
149
+ byte-identical whether forty components were examined and found clean or
150
+ nothing was examined at all. It now reads `· 26 files, 4 with styles`, the
151
+ `JIG_CHECK:` record carries `files=` and `styled=`, and a run where nothing
152
+ carried a style region says **"Nothing inspected."** rather than "No
153
+ findings." Both counts, because `.ts` and `.tsx` are style-bearing by
154
+ extension and the file count includes parsers and configs that can never
155
+ produce a finding.
156
+
157
+ Worth knowing: `check` defaults to changed files, so on a clean tree it scans
158
+ almost nothing. That was always true and always looked like a pass.
159
+
160
+ - **`init` offers a Tailwind v4 alias block.** The flat import gives you tokens,
161
+ not utility classes — Tailwind only generates those for names declared in
162
+ `@theme`. `init` can now generate that block, and **asks first**: it changes
163
+ how every component in a project is written, both styles are correct, and
164
+ silence is no. Under `--yes` it declines and says how to get it.
165
+
166
+ Only the 18 Tailwind namespaces are aliased. `--size-*`, `--measure-*`,
167
+ `--focus-ring-*`, `--border-width-*` and `--opacity-*` have none, and aliasing
168
+ one emits a declaration that generates nothing. 189 declared names filter to
169
+ 87 — and the filter also excludes every primitive `02-tokens.md` says never to
170
+ consume directly.
171
+
172
+ The generated file explains why `--radius-surface: var(--radius-surface)`
173
+ appears beside Jig's own declaration in the compiled CSS: Tailwind's lands in
174
+ `@layer theme`, Jig's is unlayered, and unlayered beats layered regardless of
175
+ source order. Without that note, someone finds it and "fixes" it.
176
+
177
+ - **The skill treats Tailwind setup as an ask.** Finding Tailwind in a project
178
+ is not permission to change how every component in it is written. The agent
179
+ reports what it found, shows both arrangements, and waits — and is told never
180
+ to write `@import "tailwindcss"` into a project that does not already have it.
181
+
182
+ - **A guard that staged assets stay out of git.** Adding `CHANGELOG.md` to the
183
+ prepack staging list committed a build artifact, because
184
+ `packages/cli/.gitignore` is a hand-maintained list that nothing forced into
185
+ step. On its first run the new guard caught a second, pre-existing hole:
186
+ `references` had been staged and unignored since it was added, saved only by
187
+ the directory not existing yet.
188
+
189
+ ### Corrected
190
+
191
+ - **0.7.0's notes said the non-TTY refusal "names the three ways out".** It
192
+ names three; two work. The guard runs before any config is read, so
193
+ `jig.config.json` alone still exits 1 — it selects the mode once you are past
194
+ the guard, it does not get you past it. The message is unchanged here and
195
+ recorded in `docs/known-follow-ups.md`, because the right wording depends on
196
+ whether the guard should consult the config first, which is a behaviour
197
+ question rather than a copy one.
198
+
199
+ ## 0.7.1
200
+
201
+ A rule file that contradicted the tool, and the guard that kept it that way.
202
+
203
+ Patch: the shipped rules change, but no command writes anything different.
204
+
205
+ ### Fixed
206
+
207
+ - **`02-tokens.md` named a token location that 0.7.0 stopped using.** It said
208
+ tokens live at `.jig/tokens/`, that this was "the only location, in every
209
+ scope and every project", and that "nothing relocates them". Since 0.7.0 the
210
+ layer follows the project: `init` puts it beside the stylesheet it wires and
211
+ prints the path it chose. So a single `init` run told you `Token layer:
212
+ src/styles/jig/` while the rule file an agent is explicitly told to load
213
+ before writing token code insisted that directory could not exist.
214
+
215
+ The file also disagreed with itself — four absolute examples, and one
216
+ relative one added when the Tailwind section was written.
217
+
218
+ It now describes the real behaviour, says pre-0.7.0 projects keep their
219
+ layout, and points at the `theme.css` barrel so relocating the layer costs
220
+ one line instead of every stylesheet.
221
+
222
+ - **`check-tokens` rule 4 was holding the claim in place.** It hardcoded the
223
+ same path, in a comment that anticipated and forbade this exact change:
224
+ "nothing — including a future `init` — relocates them". It was checking the
225
+ documentation against itself rather than against the CLI, so it could not
226
+ see the drift and would have failed the build on anyone correcting it.
227
+
228
+ It now asserts what stays true as the layer moves: token imports shown in
229
+ the rules must be relative. Mutation-tested in both directions.
230
+
231
+ ### Corrected
232
+
233
+ - **0.7.0's notes said the non-TTY refusal "names the three ways out:
234
+ `--yes`, a real terminal, or writing `jig.config.json` first."** The third
235
+ is not a way out on its own. The guard runs before any config is read, so a
236
+ config alone changes nothing — it selects the *mode* once you are past the
237
+ guard. `jig.config.json` plus `--yes` works; `jig.config.json` alone exits 1
238
+ with the same message that suggested it.
239
+
240
+ The message itself is unchanged in this release and still reads as though
241
+ the config were a third alternative. It is recorded in
242
+ `docs/known-follow-ups.md` rather than reworded here, because the wording
243
+ that is actually right depends on whether the guard should consult the
244
+ config first — a behaviour question, not a copy question.
245
+
246
+ ### How these were found
247
+
248
+ Not by the test suite, which passes 631 tests either way. By handing the rules
249
+ to agents that had never seen this repository and asking them to build
250
+ something real. Everyone who could have caught the token-location defect
251
+ already knew where the tokens were.
252
+
253
+ ## 0.7.0
254
+
255
+ A silent no-op, fixed, plus the missing half of 0.6.0's token-layer move.
256
+
257
+ Minor rather than patch, and the call is arguable. The non-TTY fix is a
258
+ correction; the relocation offer is new behaviour that can move files, though
259
+ only interactively and only with consent. Taking the higher of the two is the
260
+ reading that does not understate the change, and anyone who might be prompted
261
+ to move their token layer should read these notes.
262
+
263
+ ### Fixed
264
+
265
+ - **`jig init` without `--yes` exited 0 having written nothing when stdin was
266
+ not a terminal.** It printed the first prompt, read EOF, and stopped. Every CI
267
+ step, every piped invocation, and every agent shelling out without a TTY got a
268
+ silent no-op — and exit 0 having done nothing is the worst outcome available,
269
+ because the caller cannot tell it from success and then acts on a token layer
270
+ that was never created. Confirmed identical in 0.5.0, so this predates the
271
+ prompts added since. It now fails where the cause is still visible and names
272
+ the three ways out: `--yes`, a real terminal, or writing `jig.config.json`
273
+ first.
274
+
275
+ ### Added
276
+
277
+ - **`init` offers to move a legacy `.jig/tokens/` layout** to the location your
278
+ project's own structure suggests. 0.6.0 deliberately left an upgraded project
279
+ where it was — a silent relocation could break an import you wrote that `init`
280
+ knows nothing about — but that meant the improvement only ever reached new
281
+ projects, and the way out was one line of output that is easy to miss.
282
+
283
+ Interactively it asks; under `--yes` it declines and says how. Moving is all
284
+ four halves or none: write at the new location, remove the old copies, strip
285
+ the stale import before wiring the new one, and update `jig.config.json` so
286
+ the next run does not go back for them. A file you have edited is never
287
+ removed — it is named and left for you to clear.
288
+
289
+ ```text
290
+ BEFORE @import "../../.jig/tokens/brand.acme.css";
291
+ @import "../../.jig/tokens/mode.product.css";
292
+ AFTER @import "./jig/theme.css";
293
+ ```
294
+
295
+ ## 0.6.0
296
+
297
+ The token layer stops hiding in a dotfolder, and `check` starts reading it back.
298
+
299
+ Both came from the same question: who should write the tokens. The answer turned
300
+ out to depend on something that did not exist — **nothing validated the token
301
+ layer's own declarations.** `init` checked a brand colour once, at write time,
302
+ and no command ever looked again. A generated file edited afterwards, by a person
303
+ or an agent, went unexamined: `--color-text-weak` dropped to 22% opacity and
304
+ `check` reported "No findings". With that closed, where the files live and who
305
+ writes them become ordinary decisions rather than load-bearing ones.
306
+
307
+ **Upgrading:** run `npx jig-ui@latest update`, then `npx jig-ui@latest init`.
308
+ Existing installs keep their `.jig/tokens/` layout — nothing moves unless you
309
+ move it, because relocating files could break an import you wrote yourself.
310
+
311
+ ### Added
312
+
313
+ - **`check` validates the token layer** (`check/token-audit.ts`). Text roles to
314
+ 4.5:1, interface strokes to 3:1, **in both themes**, plus `--text-prose` at
315
+ 18px and `--size-touch-target` at 48px. Alpha foregrounds are composited over
316
+ each surface first — Jig's foregrounds are alpha by design, so a ratio before
317
+ compositing means nothing. Only floors, never density: `--size-control` at 28px
318
+ is a deliberate `operator` choice, and reporting it would teach you to ignore
319
+ the ones that matter.
320
+ - **`H-47` enforces the half of itself it only stated.** Its correction always
321
+ read "consume the semantic role, not the primitive"; the detector caught raw
322
+ hex and raw px and nothing else. `color: var(--brand-l)` was silent — and it is
323
+ the worse case, because it looks exactly like correct token usage while reading
324
+ a bare number and bypassing every theme override.
325
+ - **`exempt` in `jig.config.json`.** Some surfaces render outside the cascade: an
326
+ OG card in an SVG `foreignObject` carries no stylesheet, a PDF renderer never
327
+ sees CSS. Those files were in permanent violation, which is an adoption blocker
328
+ — a check that cannot pass is a check people switch off.
329
+
330
+ Nothing is exempt by default and no filename is baked in; this list is the only
331
+ source. Every run reports the **pattern** and what it excused, because an
332
+ over-broad glob does not fail — `check` simply gets quieter, and quieter looks
333
+ like progress:
334
+
335
+ ```text
336
+ 5 file(s) exempt via jig.config.json and not scanned:
337
+ src/*-card.css (5 files — likely too broad, review it)
338
+ src/nope.css (matches nothing — check the path)
339
+ ```
340
+
341
+ Prefer an exact path. An exemption is a claim about one file's rendering
342
+ context, and that is usually literally true of one file. `src/cv/pdf/**` is a
343
+ fact about a tree; `**/*-card.tsx` is a naming coincidence that would also
344
+ excuse every real card component in the project.
345
+ - **One import per surface, through a barrel.** `jig/theme.css` imports the brand
346
+ file and one mode file; your stylesheet imports that single line and then never
347
+ changes again. Switching mode rewrites Jig's file, not yours.
348
+
349
+ ### Changed
350
+
351
+ - **The token layer follows your project's layout.** `.jig/tokens/` was right
352
+ everywhere by being right nowhere — a tool dotdir holding product source,
353
+ reached from a Rails stylesheet by `@import "../../../.jig/tokens/…"`. The
354
+ default is now a `jig/` directory beside the stylesheet being wired, so the
355
+ import is `./jig/theme.css` in every ecosystem. `jig.config.json`'s `brand`
356
+ now decides placement, not just wiring — it previously honoured the path only
357
+ when the file already existed, which is the one case where it does not matter.
358
+ - **The mode is chosen before `init` runs.** It is the most consequential thing
359
+ `init` writes and the thing it is worst at choosing: `--yes` took `'/' → product`
360
+ without reading the project, and that config then outranks every agent's later
361
+ inference. The command file now tells the agent to ask what the product is, map
362
+ each surface, write the config, and only then run. `init` also reports the
363
+ surfaces it **used** rather than the ones it defaulted to — with a config
364
+ present it had been announcing a default it was about to ignore.
365
+ - A barrel holds exactly one mode, never a merge. Importing all three into one
366
+ document leaves only the last; verified in a browser, it yields `operator`
367
+ throughout. `01-modes.md` already said why that is not a loss: density switches
368
+ at the route boundary, never inside one view.
369
+
370
+ ### Fixed
371
+
372
+ - `--text-prose`, `--size-touch-target` and every semantic colour are now held to
373
+ their floors after `init` as well as during it.
374
+ - **`H-47`'s built-in token-layer skip was over-broad.** It matched
375
+ `(brand|mode).*.css` with the directory optional, so a project's own
376
+ `src/legacy/brand.colors.css` was silently exempt from the primitive check
377
+ wherever it sat. Scoped to the token layer's directory now — Jig owns `jig/`
378
+ outright, and a filename that merely looks like one of ours is a coincidence.
379
+ - The token audit follows the token layer instead of reading a fixed directory.
380
+ It was written when `.jig/tokens/` was the only possible answer, so moving the
381
+ layer made it find nothing and report nothing — caught in a pre-release smoke
382
+ test, where a brand file edited to 22% opacity passed cleanly. A check that
383
+ goes quiet when its subject moves is worse than one that was never written,
384
+ because the silence reads as a pass.
385
+ - Tailwind v4 does not scan workspace packages — documented, with the `@source`
386
+ directive it needs. Nothing errors when this bites: the class lands on the
387
+ element, no rule exists to match it, and the style simply does not apply.
388
+
389
+ ## 0.5.0
390
+
391
+ Four things shipped in 0.4.0 were broken in ways that reported success. `/jig
392
+ update` refreshed to the version already installed and said "Updated Jig →
393
+ 0.4.0". Dark mode could not be reached by choosing it. `check` skipped nearly
394
+ every colour in a project that had not run `init`. And the reconciliation of
395
+ every numeric default against an external reference is finished — 0 rows open —
396
+ which is where most of the rest of this release came from.
397
+
398
+ **If you are on 0.4.0, run `npx jig-ui@latest update` from a terminal.** The
399
+ `/jig update` fix cannot deliver itself: your command file is the broken one, so
400
+ the slash command will keep reporting a successful no-op until the CLI replaces
401
+ it. Once, from the terminal, and the slash command works from then on.
402
+
403
+ ### Fixed
404
+
405
+ - **`/jig update` could never upgrade anyone.** Every subcommand is invoked at
406
+ the installed pin so the CLI and the vendored rules agree; `update` is the one
407
+ exception, because its job is to move that pin. The skill file knew that and
408
+ the slash command did not, so it ran `npx jig-ui@<installed> update` — a no-op
409
+ that reports success, which is worse than an error.
410
+ - **Dark mode was unreachable by explicit choice.** `brand.default.css` had one
411
+ dark block, inside `@media (prefers-color-scheme: dark)`. A selector inside a
412
+ media query cannot match when the query is false, so `data-theme="dark"` on a
413
+ light-mode system produced no dark tokens at all. A second, unmediated block
414
+ now carries the same declarations, and a check keeps the two identical.
415
+ - **`check` did not resolve the project's own tokens.** The token map held only
416
+ Jig's vendored `.jig/tokens/*.css`, so any `var(--your-token)` was
417
+ unresolvable and skipped. On a project that had never run `jig init`,
418
+ `contrast-floor` and `violet-band-hue` evaluated very nearly nothing. A name
419
+ declared in two places with different values is still skipped — which value a
420
+ browser uses depends on import order, and a wrong guess reports a finding
421
+ against a value the page never renders.
422
+ - **`operator` prose text was 16px** against the 18px floor `B-75` states and
423
+ `02-tokens.md` repeats. It is the mode most likely to be read for hours.
424
+ - **Rules cited tokens that do not exist** — the `--color-danger` family,
425
+ `--color-surface`, `--leading-heading`, `--leading-display`,
426
+ `--font-weight-body`, `--spacing-unit`. An agent following those wrote a
427
+ `var()` resolving to nothing, with no error anywhere.
428
+ - **`C-19` named the same token twice**, once "for large text only".
429
+ `--color-text-weak` clears 4.5:1 at any size, and there is no lighter grey.
430
+ - **The error message in `P-03` moved above the input**, where autofill menus
431
+ and on-screen keyboards do not cover it.
432
+ - **CSS nesting and line endings.** `install` and `update` had three near-copies
433
+ of one write helper and one had lost its line-ending handling, so `update`
434
+ flattened a CRLF token file to LF while leaving the rule files beside it
435
+ alone. All three now share `install/writer.ts`, and writes are atomic.
436
+
437
+ ### Added
438
+
439
+ - **`P-13 · Ambient motion`** — a third category of motion the system lacked.
440
+ Interaction and transition motion are milliseconds; ambient motion is slow,
441
+ looping and decorative, and must never be noticed. `G-44` forbade all of it by
442
+ stating a 300ms ceiling it had never scoped, so an agent asked for a slow
443
+ decorative loop would have refused, citing a rule about something else.
444
+ - **`--duration-ambient-fast/base/slow`** (3s / 4.7s / 7.1s), `editorial` only.
445
+ The values are mutually prime in tenths of a second so layered loops do not
446
+ re-align into one visible pulse.
447
+ - **Fluid headings.** `--text-h1` and `--text-h2` are `clamp()` in `editorial`
448
+ and `product`, reaching their minimum at a 360px viewport and their maximum at
449
+ 1024px. Every term is `rem`-based: a `px` or bare-`vw` bound ignores a
450
+ reader's font-size preference, which is a WCAG 1.4.4 failure.
451
+ - **`explain` finds rules you cannot name.** A word searches every title and
452
+ body; one match prints in full. `--list` prints every id, or one section's.
453
+ - **Easing direction** — `--ease-out` on entry, `--ease-in-out` on exit, never
454
+ linear for anything that moves. Both tokens shipped with no rule for choosing.
455
+
456
+ ### Changed
457
+
458
+ - **`--text-h1` resolves to 32px on a phone**, not 48px. This is the change most
459
+ likely to be visible in an existing project, and it is the point: at a fixed
460
+ 48px, a 45-character headline set as four lines and 211px of headline on a
461
+ 360px screen.
462
+ - **Line heights are documented per mode.** They always were per mode; the docs
463
+ quoted one mode's values as though they were everyone's, and were wrong for
464
+ two modes out of three.
465
+ - **The README covers installing and using Jig, and nothing else.** How to
466
+ change a rule and how to test that a rule earns its context cost moved to
467
+ `AGENTS.md`, where an agent working on this repository will read them.
468
+
469
+ ### Reconciliation
470
+
471
+ `RECONCILE.md` is at **0 open rows**. Every numeric default is either adopted
472
+ from the reference or deliberately kept with its argument stated in one line.
473
+ The motion rows are the exception worth knowing: that reference has no motion
474
+ chapter, so those values were settled against separate sources, and where no
475
+ source gave a number they are kept as ours on stated reasoning rather than
476
+ adopted.
477
+
478
+ `check-tokens` grew from 5 rules to 12, each mutation-tested. Two of the new
479
+ ones exist because a guard had been passing vacuously: rule 6's regex was
480
+ line-anchored and so covered 103 of 133 tokens while claiming to cover all of
481
+ them, and nothing at all checked that a token cited in the rules exists.
482
+
483
+ ## 0.4.0
484
+
485
+ Jig stops copying itself into your project. It is a skill an agent reads, and
486
+ 0.3.0 wrote 220KB across 17 files into every consuming repo to deliver it —
487
+ roughly 200KB of that Jig's own property, read by an agent that already had it
488
+ from the skill install. A single-mode project now gets **three** files, all of
489
+ them its own.
490
+
491
+ ### Changed
492
+
493
+ - **`install` writes one place: the harness's skill directory.** `SKILL.md`, the
494
+ rules, `rules.index.json` and attribution live beside each other at
495
+ `<harness>/skills/jig/`, project or global. Nothing lands in `.jig/` any more.
496
+
497
+ The rules are not a build input — nothing compiles them, and the agent reading
498
+ them already has them. The genuine exception is CSS: a stylesheet `@import` is
499
+ an edge in a build graph and must resolve inside the project, on every machine
500
+ that builds it. That is the one category `init` still copies.
501
+
502
+ - **`.jig/` holds only what belongs to the project** — the brand file, the mode
503
+ files for the modes actually declared (not all three), and `state.json`.
504
+
505
+ - **A project install refuses when a global one exists.** Two `jig` skills
506
+ registered with the same harness, whose rule paths point at different places,
507
+ is exactly the incoherence this layout exists to prevent.
508
+
509
+ - **Every harness comes from one table.** Five adapters — Claude Code, Cursor,
510
+ opencode, Gemini CLI, and a generic `.agents/skills` — share the
511
+ `<harness>/skills/<name>/` convention, so adding one is a row rather than a
512
+ file. Codex keeps a bespoke implementation because `AGENTS.md` genuinely is a
513
+ different mechanism.
514
+
515
+ ### Fixed
516
+
517
+ - **The skill told agents `check` was planned.** It shipped in 0.3.0. A baseline
518
+ run reported "the CLI is ahead of the doc" and worked by hand rather than
519
+ running it. The guard that should have caught this compared the metadata
520
+ against a hand-maintained list, so when `check` landed and neither was
521
+ updated, the two agreed and the test stayed green. The list is now read out of
522
+ the CLI source, and agreement is asserted in both directions.
523
+
524
+ - **The skill sent agents to an unpinned CLI.** `npx jig-ui` resolves to whatever
525
+ is latest on npm, which need not be the version that wrote the bundle. Two
526
+ baselines hit this and fell back to working by hand. The skill now names the
527
+ version that built it, and `jig update` moves both together.
528
+
529
+ - **`JIG_CHECK:` named two different records.** The CLI emitted `version=
530
+ mechanical= judgment=`; the skill told the agent to emit `version= mode=
531
+ self_check=` — disjoint fields under one label. There is now one field set,
532
+ `version mode mechanical judgment`, asserted against both sources. An emitter
533
+ that cannot determine a field says so in the value rather than dropping it.
534
+
535
+ - **Every vendored file cited a licence path that no longer exists.** The header
536
+ hardcoded `.jig/LICENSE`, true only while install vendored into `.jig/`. In
537
+ the new layout the one line whose job is directing a reader to the licence
538
+ directed them nowhere. It is now computed from the file's depth in the bundle.
539
+
540
+ - **`update` refreshed only one harness.** A project can hold several installs,
541
+ each with its own manifest; the rest stayed pinned at their install version
542
+ with nothing said about them. It now refreshes every one and reports each.
543
+
544
+ - **`update` wrote files before checking the path.** `assertSafeRelPath` covered
545
+ adapter-rendered files but not `referenceDir`, which the harness table derives
546
+ just as directly. The shipped table is asserted at module load, so a bad entry
547
+ fails at import rather than at whichever command writes first.
548
+
549
+ - **A project-scope Codex install wrote Jig's rules into your project's
550
+ `.jig/`.** Codex has no skill-directory convention, so its bundle went to a
551
+ bare `.jig` — the one directory this release reserves for the project's own
552
+ material. Install plus `init` left 13 files there, Jig's rules and manifest
553
+ interleaved with your tokens and state, and the harness that most needed the
554
+ 0.4.0 separation was the only one that did not get it.
555
+
556
+ Two concrete harms beyond the untidiness: `init`'s legacy scanner looks in
557
+ `.jig/` for exactly those artifacts and offers to remove them, so it could
558
+ offer to delete a live install; and one directory holding two manifests is
559
+ what let a stale `.jig/manifest.json` hijack `update` in the first place.
560
+
561
+ Codex now uses `.codex/.jig/` at both scopes, mirroring the path its global
562
+ install already used. Verified against the real Codex CLI: it reads
563
+ `AGENTS.md`, resolves every rule path under the new location, and picks up the
564
+ declared mode. Upgrading leaves the old bundle in place — nothing is deleted —
565
+ and `update` now names it rather than reporting a bare "Jig is not installed"
566
+ to someone looking straight at the files.
567
+
568
+ - **`update` could never move an install forward.** Pinning the CLI fixed version
569
+ skew, but applied to every command it trapped the install: `npx jig-ui@0.4.0
570
+ update` refreshes to 0.4.0, reports success, and changes nothing. `update` is
571
+ the one command whose job is moving the pin, so it is the one that does not
572
+ carry it — the skill renders `npx jig-ui@latest update`.
573
+
574
+ - **`init --yes` chose a mode silently.** It takes `'/' → product` without
575
+ inferring anything, and `01-modes.md` rule 1 makes the config authoritative
576
+ over an agent's own inference — so the default is not a neutral placeholder,
577
+ it binds every agent that reads the project afterwards. Two baseline runs on
578
+ an `ops-console` project read every signal as `operator`, found `product`, and
579
+ correctly deferred to it; one noted the density difference is expensive to
580
+ reverse. The brand colour already stated its default and why. The mode now
581
+ does too, and says where to change it.
582
+
583
+ - **A source build could stamp a pin to a version that was never published**,
584
+ silently, until an agent tried to run it. `install` and `update` now say so
585
+ when the running CLI is not an npm-installed package.
586
+
587
+ - **Concurrent runs lost each other's manifest entries.** Two runs against one
588
+ install — two agents, or a script running `jig init` across a monorepo against
589
+ a shared global install — both read the manifest and both wrote it, and the
590
+ last writer's copy dropped the other's records of "Jig owns this file".
591
+ Losing one makes a later `update` treat that file as the user's and stop
592
+ refreshing it: the safe direction, but silent.
593
+
594
+ Fixed without a lock. The `files` map is additive and per-file — a run only
595
+ records entries for files it actually wrote — so merging against whatever is
596
+ on disk at write time is correct, and needs none of the stale-lock recovery a
597
+ mutex would after a process is killed mid-run. Writes are atomic
598
+ (temp + rename), and verified-and-retried to close the window between the read
599
+ and the rename. Ten parallel processes writing fifty entries now keep all
600
+ fifty; without the merge, one survives.
601
+
602
+ - **An asset could be staged at prepack and left out of the tarball** — correct
603
+ code reading an asset that never shipped. Both lists must now agree, verified
604
+ against the real `npm pack` output.
605
+
606
+ - **A legacy `.jig/manifest.json` hijacked `update`** and resurrected the whole
607
+ vendored layout.
608
+
609
+ - **Agents invented token values when `init` had not been run.** A baseline run
610
+ with the skill installed but the project not initialised authored its own
611
+ `:root` block — "control height (32), row height (48), duration (120ms) and
612
+ the near-black brand default are my resolved values, not the system's" — and
613
+ wrote it into the project's stylesheet. Step 5 said "consume tokens by
614
+ semantic name only", and it complied to the letter by inventing the
615
+ definitions behind the names.
616
+
617
+ `SKILL.md` now opens with the precondition: no `jig.config.json` means no
618
+ token layer, so run `init` and stop, and do not author a `:root` block of your
619
+ own. Step 5 gained the counter — a token with no value is a finding to report,
620
+ not a number to supply. Re-run on the same fixture, the agent invented
621
+ nothing, and reported a real gap in the token contract instead.
622
+
623
+ ### Added
624
+
625
+ - **`/jig` slash commands.** `install` writes a command file for every harness
626
+ that has a slash-command mechanism, so `/jig init`, `/jig check --all` and
627
+ `/jig update` work without leaving the session. One file named `jig`
628
+ dispatching on its arguments, which is what gives `/jig init` with a space
629
+ rather than a separate `/jig-init` per subcommand.
630
+
631
+ The command is not a thin wrapper: `/jig check` runs the CLI for the seven
632
+ mechanical rules, then applies the 97 judgment rules to the same files and
633
+ merges both halves into one report keyed by rule id — the half a CLI cannot
634
+ do, which is the reason the command exists at all.
635
+
636
+ Claude Code, Cursor, opencode and Gemini CLI (which gets its own TOML shape).
637
+ Not Codex: its custom prompts are not expanded by `codex exec` — probed
638
+ directly — so a file there could sit and never fire. Only subcommands the CLI
639
+ actually registers are offered, since a slash command that errors out is worse
640
+ than one that does not exist.
641
+
642
+ - **`check` reads CSS wherever it lives.** It read `.css`/`.scss`/`.less` only,
643
+ which for most projects meant it examined almost nothing — and said nothing
644
+ about that. A Tailwind v4 fixture with `bg-[#6D28D9] p-[13px] rounded-[12px]
645
+ text-[22px] h-[32px]` reported "No findings · 0 errors · mechanical=pass:0". A
646
+ clean bill of health for a project where every value bypassed the token layer.
647
+
648
+ Now covered: `<style>` blocks (HTML, Astro, Vue, Svelte, PHP, ERB, Twig,
649
+ Handlebars, MDX, ASP and ASP.NET, Razor, JSP, Phoenix, EJS, Nunjucks, Liquid,
650
+ Jinja, Velocity, FreeMarker), the indentation-delimited templates (Pug's
651
+ `style.`, Haml's `:css`, Slim's `css:`, each with its own attribute syntax),
652
+ `style="…"` and `style={{ }}`, CSS-in-JS tagged templates
653
+ (`styled.button`, `styled(Link)`, `css`, `createGlobalStyle`, `keyframes`),
654
+ Tailwind arbitrary values, and Tailwind default-palette contrast pairs.
655
+ `@theme` counts as the token layer, so a v4 project that has adopted Jig is
656
+ recognised as having done so.
657
+
658
+ The mechanism is extraction rather than a detector per language: everything
659
+ that is not CSS is blanked, preserving character positions, and the existing
660
+ detectors run unchanged — so a finding in a `.vue` file still points at the
661
+ right line, and all seven rules gained host-language support at once.
662
+
663
+ Two deliberate limits. A bare `p-4` is not a finding: it resolves through a
664
+ scale, and flagging it would mean flagging correct Tailwind. A colour outside
665
+ the default palette is not resolved rather than guessed at.
666
+
667
+ - **Reference files ship beside the skill.** `references/**` in the package
668
+ installs into the bundle with its subdirectory shape preserved, refreshed by
669
+ `update` under the same rule as the rules: replace when untouched, skip when
670
+ you have edited it. Adding one is a file drop, no code change.
671
+
672
+ **No reference ships in 0.4.0.** Four were planned — a procedure for `init`,
673
+ for `check`, for building a component, and one for resolving apparent rule
674
+ conflicts. Each was tested first by running an agent on the task with no
675
+ guidance, and in every case the agent already did the right thing: it inferred
676
+ the mode with signals stated, kept a 32px control inside a 48px target rather
677
+ than reading the two as contradictory, surfaced the brand question instead of
678
+ guessing, and handled loading, error and never-checked states unprompted.
679
+ `04-principles.md`'s tiebreakers were doing the work the references were meant
680
+ to do. Writing them anyway would have added words the rules already carry.
681
+
682
+ ### Migration
683
+
684
+ `install` no longer writes rule files into `.jig/`, so a pre-0.4.0 project has
685
+ rules in two places. The old copies are yours to remove; nothing deletes them
686
+ for you, because you may have edited one and that edit is yours to keep. Run
687
+ `jig install --agent <name>` to place the new bundle, then delete `.jig/rules/`
688
+ and `.jig/rules.index.json` once you have checked them for your own changes.
689
+
690
+ ## 0.3.0
691
+
692
+ Two new commands. `install` and `update` put the system in place; these two make
693
+ it do something.
694
+
695
+ ### Added
696
+
697
+ - **`jig check`** — verifies a consumer's code against the rules. Seven
698
+ detectors, exactly the ones `rules.index.json` already named: `gradient-text`
699
+ (A-02), `backdrop-blur` (A-04), `pure-black-white` (C-18), `contrast-floor`
700
+ (C-19), `focus-removed` (E-29), `hardcoded-value` (H-47), and
701
+ `violet-band-hue` (A-01, hybrid — it asks rather than fails). With `--all`,
702
+ `--ci` (mechanical bucket only, no model, deterministic) and `--json`.
703
+
704
+ H-47 runs only on files that reference a Jig token or import a vendored token
705
+ file. "Hard-coded *past* the token layer" means nothing for a file that never
706
+ adopted it, and without that scope it produced 13 of 13 findings on a 20-line
707
+ stylesheet. A project where nothing participates is told how to start.
708
+
709
+ - **`jig init`** — sets a project up to use the system. Detects the CSS system,
710
+ derives a brand colour from existing code rather than interviewing cold,
711
+ validates it against the contract `brand.default.css` already states, writes
712
+ the brand file and `jig.config.json`, wires the imports, and runs `check` for
713
+ a baseline.
714
+
715
+ This is also what makes global installs coherent. The brand file and config
716
+ always live in the project, and for a global install the one selected mode
717
+ file is copied into the project's `.jig/tokens/` so the import is
718
+ project-relative. A `$HOME`-relative CSS import resolves only on the machine
719
+ that generated it; a stylesheet is committed and must build everywhere.
720
+
721
+ - `oklch()` is parsed. Tailwind v4 and shadcn emit it by default, so skipping it
722
+ meant `init` derived nothing on a large share of new projects and `C-19`
723
+ computed no contrast against Jig's own `--color-bg-base`.
724
+
725
+ ### Fixed
726
+
727
+ - **CSS nesting hid the parent's declarations.** A block containing braces was
728
+ discarded whole, so in `.card { color: red; .h { … } }` the `color: red`
729
+ belonged to no block and was invisible to every detector. On a Sass codebase
730
+ that was most declarations, reported as a clean result.
731
+
732
+ - Comments produced findings, and a commented-out `:focus-visible` silenced
733
+ E-29 for a whole file — a dead detector reporting success.
734
+
735
+ - `var(--x, fallback)` was resolved to the fallback and reported as fact.
736
+
737
+ - The large-text contrast exemption was inert because only `px` font-sizes were
738
+ recognised, so `2rem` at 3.54:1 was flagged as failing a 4.5:1 floor that did
739
+ not apply to it.
740
+
741
+
742
+ ## 0.2.1
743
+
744
+ ### Fixed
745
+
746
+ - **`--color-text-warning` and `--color-text-success` failed their contrast
747
+ floors.** Warning was 3.64:1 and success 4.48:1 against the surfaces they can
748
+ land on, where text requires 4.5:1 and strokes 3:1. The source is explicit:
749
+ system colours used for text need 4.5:1; used for interface elements and
750
+ icons, 3:1. `RECONCILE.md` also lists the contrast floors as the one category
751
+ not up for reconciliation, and `C-19` is a rule about this exact failure — so
752
+ the system was breaking its own hardest rule, in a shipped default that every
753
+ consumer inherits unless they override it.
754
+
755
+ Warning lightness 36% → 29%, success 26% → 23%. Hue and saturation unchanged,
756
+ so both are the same colour, darker. All four semantic colours now clear both
757
+ floors against `bg-base`, `bg-raised`, and `fill` on either.
758
+
759
+ - The system colours now state what a replacement must satisfy. The brand colour
760
+ already carried that contract; these did not, so a user bringing their own
761
+ error or warning colour — the intended workflow — had no floor to hit.
762
+
763
+ ### Added
764
+
765
+ - `scripts/check-tokens.mjs` gains two rules, both mutation-tested. Rule 5
766
+ computes contrast from the token values and fails the build; this is the only
767
+ defect class here that arithmetic can catch, and it shipped twice because
768
+ nobody was doing the arithmetic. Rule 6 fails the build when a token is
769
+ defined but never rendered by the preview.
770
+
771
+ - `packages/preview` — a rendering harness. Every prior check verified the
772
+ system by arithmetic or grep; nothing had looked at it. Plain HTML and CSS,
773
+ no build, not published. It found two gaps on its first run: there is no
774
+ border-width token and no focus-ring geometry tokens.
775
+
776
+ ## 0.2.0
777
+
778
+ The first release that actually works end to end. `0.1.0` shipped rules that
779
+ cited tokens it never installed, and a token name that did not exist.
780
+
781
+ ### Fixed
782
+
783
+ - **`--color-brand` was cited nine times and defined nowhere**, including
784
+ `03-patterns.md`'s primary-button spec. An agent following that rule wrote
785
+ `background: var(--color-brand)` and got an unset custom property.
786
+ - **The design tokens were never installed.** The rules cite tokens 22 times
787
+ and `02-tokens.md` instructs the reader to import them, but `install` only
788
+ wrote the rule markdown. The CSS was in the published tarball the whole time
789
+ and never copied out.
790
+ - **Rule `C-49` had no correction and `I-80` had no anti-pattern**, in a file
791
+ whose own contract states every rule pairs both. The totals hid it — the two
792
+ defects cancelled at 103/103.
793
+ - **Ten values in the mode profile tables contradicted the tokens**, including
794
+ editorial body size, section rhythm, card padding, and a heading step that
795
+ did not exist.
796
+ - **`A-07`'s correction had silently inverted from true to false.** A mechanical
797
+ rename of `--radius-base` to `--radius-sm` turned a correct statement about
798
+ radius derivation into an incorrect one, with nothing to catch it.
799
+ - **`A-07`'s prohibition used a `16px+` threshold** that flagged `--radius-md`,
800
+ the value the rule exists to sanction.
801
+ - **`C-68` depended on a radius token it never named.** "A badge is more rounded
802
+ than a button" now cites `--radius-full`.
803
+
804
+ ### Changed
805
+
806
+ - Tokens vendor to **`.jig/tokens/`** on install, in every scope. That is the
807
+ only location; `update` refreshes them there and nothing relocates them.
808
+ - Editing a vendored token file is expected: `install` and `update` both leave
809
+ files you have changed alone and report them skipped.
810
+ - The mode profile tables cite token names instead of literals; the comparison
811
+ table is ordinal. A number in prose is a call site.
812
+ - The semantic colours are defined once each as `--<name>-h/s/l`. Every variation
813
+ previously repeated the same literal four to five times, so changing a colour
814
+ meant editing every copy or the variations desynchronised.
815
+
816
+ ### Added
817
+
818
+ - `scripts/check-tokens.mjs`, run by `npm test`. Four rules, each mutation-tested
819
+ against the drift it guards: the type table must match its tokens by name, no
820
+ unanchored literal in a prose table, no chosen colour literal repeated in a
821
+ token file, and every token import must use the canonical path.
822
+