@nurix/etna 0.20.4 → 0.20.6
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/data/catalog/rules/frontend.md +6 -4
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/rules/frontend.md +6 -4
- package/data/harnesses/discover/harness.json +2 -2
- package/data/harnesses/discover/rules/ingestion.md +49 -0
- package/package.json +2 -2
|
@@ -4,12 +4,14 @@ Tailwind v4 + shadcn-based UI conventions for **web** apps — styling, the `@nu
|
|
|
4
4
|
|
|
5
5
|
- **Build every UI through the `design` skill** — never hand-roll layouts; the page archetypes, the `design.md` identity system with its token artifact (`design.tokens.css`), and chrome wiring live in the skill.
|
|
6
6
|
- **The brief's stated theme binds** — dark boots dark when the brief says so (recorded in the project's `design.md`); light is the default only when the brief is silent.
|
|
7
|
-
- **`@nurix/components`** is the UI library — import by subpath (`@nurix/components/button`). **Never** `npx shadcn add`, no `components.json` scaffold — consume the prebuilt library (`.tsx`, not `.jsx`).
|
|
7
|
+
- **`@nurix/components`** is the UI library — import by subpath (`@nurix/components/button`). **Never** `npx shadcn add`, no `components.json` scaffold — consume the prebuilt library (`.tsx`, not `.jsx`). *A recorded deviation from canonical shadcn, where primitives are vendored into `components/ui/` and edited in place: this workspace consumes a published library instead, and vendoring would fork it. Deliberate — the design skill does not "converge" it.*
|
|
8
8
|
- Vite: `@tailwindcss/vite` + `@vitejs/plugin-react-swc`. Pin `react` / `react-dom` / `react/jsx-runtime` / `react/jsx-dev-runtime` to the app's single copy via `resolve.alias` — `@nurix/*` ship nested react, and two copies means two hook dispatchers.
|
|
9
9
|
- Tailwind v4 is CSS-only — no `tailwind.config.js`, no PostCSS.
|
|
10
|
-
- CSS entry `globals.css`,
|
|
11
|
-
-
|
|
12
|
-
-
|
|
10
|
+
- **The CSS entry is whatever the manifest names**, not a fixed filename — `globals.css`, `index.css` and `styles.css` are each conventional in different framework guides, so resolve it rather than grep for one. Imports in order: `@import "./fonts.css"` (`@fontsource` registrations) → the project's `design.tokens.css` (derived from the repo-base `design.md` by the design skill's validator — **never** hand-edited) → `@import "tailwindcss"` → the library's `styles.css` — then `@theme inline {…}` mapping vars to tokens.
|
|
11
|
+
- **Dark mode is the `.dark` class on the root element**, bound as `@custom-variant dark (&:is(.dark *))` in the CSS entry — **never** a `data-theme` attribute. `@nurix/components` compiles `.dark` blocks into its distributed CSS and a consumer cannot re-key them, so an app on the attribute renders half-flipped: its own tokens invert, every library surface stays light. The switcher is three parts — a pre-paint inline script in the HTML shell (a mounted provider runs too late and the page flashes), `color-scheme: light dark`, and the persisted-preference module.
|
|
12
|
+
- Tokens: raw values in `:root:not(.dark)` and `:root.dark` — **both specificity (0,2,0), and that armor is load-bearing.** Tailwind compiles `@theme` into `@layer theme` and any unlayered declaration beats a layered one, so the library's unlayered `:root`/`.dark` defeats an app's `@theme` outright and *ties* a bare `:root`/`.dark`, leaving the winner to import order. Values are then consumed as semantic Tailwind tokens (`bg-card`, `text-foreground`) through an `@theme inline` mapping layer. The library bundles `class-variance-authority`, `clsx`, `tailwind-merge` — don't re-add them.
|
|
13
|
+
- **Never retune a Tailwind scale key `@nurix/components` consumes** — `--text-xs`, `--text-sm`, `--text-base`, `--text-lg`, `--text-xl`, `--text-2xl`. Its components carry raw scale utilities and its stylesheet declares no `font-size`, so those compile against *this app's* `@theme`: redefining `--text-sm` moves every button, menu item and table cell in the library at once, with nothing to report it. Change type by changing ramp tokens.
|
|
14
|
+
- Spacing in 2/4/6/8; `object-cover`; `truncate`; color through tokens — **never** hardcoded hex in components, and **never a literal font size**. The type ramp and the family rules are the design skill's [`references/type-system.md`](../skills/design/references/type-system.md); `text-sm` is a scale step and is fine, `text-[13px]` is a component overruling the system.
|
|
13
15
|
|
|
14
16
|
## Component Search Order
|
|
15
17
|
|
package/data/harness-index.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "code",
|
|
3
3
|
"name": "code",
|
|
4
|
-
"version": "3.14.
|
|
4
|
+
"version": "3.14.2",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
}
|
|
26
26
|
],
|
|
27
27
|
"docsRoot": "docs",
|
|
28
|
-
"digest": "
|
|
28
|
+
"digest": "410e5186a8b8"
|
|
29
29
|
}
|
|
@@ -4,12 +4,14 @@ Tailwind v4 + shadcn-based UI conventions for **web** apps — styling, the `@nu
|
|
|
4
4
|
|
|
5
5
|
- **Build every UI through the `design` skill** — never hand-roll layouts; the page archetypes, the `design.md` identity system with its token artifact (`design.tokens.css`), and chrome wiring live in the skill.
|
|
6
6
|
- **The brief's stated theme binds** — dark boots dark when the brief says so (recorded in the project's `design.md`); light is the default only when the brief is silent.
|
|
7
|
-
- **`@nurix/components`** is the UI library — import by subpath (`@nurix/components/button`). **Never** `npx shadcn add`, no `components.json` scaffold — consume the prebuilt library (`.tsx`, not `.jsx`).
|
|
7
|
+
- **`@nurix/components`** is the UI library — import by subpath (`@nurix/components/button`). **Never** `npx shadcn add`, no `components.json` scaffold — consume the prebuilt library (`.tsx`, not `.jsx`). *A recorded deviation from canonical shadcn, where primitives are vendored into `components/ui/` and edited in place: this workspace consumes a published library instead, and vendoring would fork it. Deliberate — the design skill does not "converge" it.*
|
|
8
8
|
- Vite: `@tailwindcss/vite` + `@vitejs/plugin-react-swc`. Pin `react` / `react-dom` / `react/jsx-runtime` / `react/jsx-dev-runtime` to the app's single copy via `resolve.alias` — `@nurix/*` ship nested react, and two copies means two hook dispatchers.
|
|
9
9
|
- Tailwind v4 is CSS-only — no `tailwind.config.js`, no PostCSS.
|
|
10
|
-
- CSS entry `globals.css`,
|
|
11
|
-
-
|
|
12
|
-
-
|
|
10
|
+
- **The CSS entry is whatever the manifest names**, not a fixed filename — `globals.css`, `index.css` and `styles.css` are each conventional in different framework guides, so resolve it rather than grep for one. Imports in order: `@import "./fonts.css"` (`@fontsource` registrations) → the project's `design.tokens.css` (derived from the repo-base `design.md` by the design skill's validator — **never** hand-edited) → `@import "tailwindcss"` → the library's `styles.css` — then `@theme inline {…}` mapping vars to tokens.
|
|
11
|
+
- **Dark mode is the `.dark` class on the root element**, bound as `@custom-variant dark (&:is(.dark *))` in the CSS entry — **never** a `data-theme` attribute. `@nurix/components` compiles `.dark` blocks into its distributed CSS and a consumer cannot re-key them, so an app on the attribute renders half-flipped: its own tokens invert, every library surface stays light. The switcher is three parts — a pre-paint inline script in the HTML shell (a mounted provider runs too late and the page flashes), `color-scheme: light dark`, and the persisted-preference module.
|
|
12
|
+
- Tokens: raw values in `:root:not(.dark)` and `:root.dark` — **both specificity (0,2,0), and that armor is load-bearing.** Tailwind compiles `@theme` into `@layer theme` and any unlayered declaration beats a layered one, so the library's unlayered `:root`/`.dark` defeats an app's `@theme` outright and *ties* a bare `:root`/`.dark`, leaving the winner to import order. Values are then consumed as semantic Tailwind tokens (`bg-card`, `text-foreground`) through an `@theme inline` mapping layer. The library bundles `class-variance-authority`, `clsx`, `tailwind-merge` — don't re-add them.
|
|
13
|
+
- **Never retune a Tailwind scale key `@nurix/components` consumes** — `--text-xs`, `--text-sm`, `--text-base`, `--text-lg`, `--text-xl`, `--text-2xl`. Its components carry raw scale utilities and its stylesheet declares no `font-size`, so those compile against *this app's* `@theme`: redefining `--text-sm` moves every button, menu item and table cell in the library at once, with nothing to report it. Change type by changing ramp tokens.
|
|
14
|
+
- Spacing in 2/4/6/8; `object-cover`; `truncate`; color through tokens — **never** hardcoded hex in components, and **never a literal font size**. The type ramp and the family rules are the design skill's [`references/type-system.md`](../skills/design/references/type-system.md); `text-sm` is a scale step and is fine, `text-[13px]` is a component overruling the system.
|
|
13
15
|
|
|
14
16
|
## Component Search Order
|
|
15
17
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"harness": "discover",
|
|
3
3
|
"name": "discover",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.17",
|
|
5
5
|
"files": [
|
|
6
6
|
".prettierignore",
|
|
7
7
|
".prettierrc.json",
|
|
@@ -10,5 +10,5 @@
|
|
|
10
10
|
"seeds": [],
|
|
11
11
|
"tools": [],
|
|
12
12
|
"docsRoot": "docs",
|
|
13
|
-
"digest": "
|
|
13
|
+
"digest": "15abe8b51cd9"
|
|
14
14
|
}
|
|
@@ -292,6 +292,50 @@ the elements needing redaction change as you navigate.
|
|
|
292
292
|
> redaction was real; the *frame* was never examined. **Never write a zero-PII assertion you did not verify
|
|
293
293
|
> by looking at the image** — and prefer "redacted: <what>" over a blanket claim.)
|
|
294
294
|
|
|
295
|
+
### 5.6 Reconstructing an ALGORITHM — differential testing, in separated layers
|
|
296
|
+
|
|
297
|
+
§5.4 says what a *catalog* capture must dump. This is the extra bar for the harder case: a dimension
|
|
298
|
+
that reconstructs **behaviour** — a scoring formula, a ranking function, a pricing calculation, a
|
|
299
|
+
matcher, a state machine. A word list can be checked by reading it. An algorithm cannot: it is only
|
|
300
|
+
`complete` once it has been **run against the live product on inputs chosen to break it**.
|
|
301
|
+
|
|
302
|
+
**The rule.** A reconstructed algorithm is `partial` until:
|
|
303
|
+
|
|
304
|
+
1. It is **executable** and committed to the corpus, not merely described in prose.
|
|
305
|
+
2. It has been **differentially tested against the live product** on a real corpus plus deliberately
|
|
306
|
+
adversarial inputs — not only on the examples it was derived from.
|
|
307
|
+
3. The test **separates the estimator from the feature extraction**, and the per-layer pass rates are
|
|
308
|
+
recorded separately. One aggregate number is not enough.
|
|
309
|
+
4. The inputs and the product's own observed outputs are frozen in `raw/` (or beside the
|
|
310
|
+
implementation) as **re-runnable fixtures**, so a later run can repeat the comparison.
|
|
311
|
+
|
|
312
|
+
**Why the layer split is the load-bearing part.** Almost every reconstruction has two independent
|
|
313
|
+
places to be wrong — the *computation* and the *inputs fed to it*. Tested together they are
|
|
314
|
+
indistinguishable, and the aggregate score points at whichever one you already suspect:
|
|
315
|
+
|
|
316
|
+
- **Layer A — the estimator.** Feed the algorithm **the product's own reported feature values** and
|
|
317
|
+
check it reproduces the product's own output. This tests the mathematics alone.
|
|
318
|
+
- **Layer B — the feature extraction.** Compare **your** computed features against the product's
|
|
319
|
+
reported features. This tests tokenisation / parsing / counting alone.
|
|
320
|
+
- **Layer C — end-to-end.** Both together. C failing while A passes localises the fault to B in one
|
|
321
|
+
step, with no re-derivation of a formula that was already right.
|
|
322
|
+
|
|
323
|
+
**A residual that survives a correct layer B is a finding about the TARGET, not an error in the
|
|
324
|
+
reconstruction** — and it is often the most valuable thing the dimension produces. Chase it; do not
|
|
325
|
+
tune it away. And when the target's behaviour is a defect, **do not silently replicate it** to make
|
|
326
|
+
the numbers match: implement the correct behaviour by default and put bug-parity behind an explicit,
|
|
327
|
+
documented flag. A reconstruction that quietly reproduces a bug teaches the next reader the bug.
|
|
328
|
+
|
|
329
|
+
> (Origin: hemingway — the run shipped a "validated" reference implementation of the app's readability
|
|
330
|
+
> scorer whose **formula was exactly right** and whose **tokenizer agreed with the target on only 41 of
|
|
331
|
+
> 62 real inputs**. A single end-to-end comparison read as *"the model is wrong"* and would have sent
|
|
332
|
+
> the run back to re-derive a correct formula. Splitting the test localised it immediately: layer A was
|
|
333
|
+
> **62/62** from the first run, and `Δletters = 0` in 20 of the 21 layer-B divergences pinned the fault
|
|
334
|
+
> to word/sentence segmentation, which was then reverse-engineered to **61/62**. The one residual that
|
|
335
|
+
> survived a correct tokenizer was **a defect in the product** — a literal `...` silently deletes the
|
|
336
|
+
> preceding text from every statistic — which no static lane could have found and which only appeared
|
|
337
|
+
> because adversarial inputs were included.)
|
|
338
|
+
|
|
295
339
|
---
|
|
296
340
|
|
|
297
341
|
## 6. The shared-surface seam rule (bundle ↔ session ↔ wire-capture)
|
|
@@ -509,6 +553,11 @@ Downloading a public binary (CRX/`.asar`/`.ipa`/`.apk`) and reading its manifest
|
|
|
509
553
|
|
|
510
554
|
A capture is complete when: `_summary.md` exists with full provenance frontmatter + honest `status`; **every Finding is backed by a `raw/` artifact — an inline value is NOT sufficient (§5.3)**; the dimension's **§5.4 dump manifest is satisfied verbatim**; `raw/` artifacts carry `_meta` (with `verbatim:` and, if bounded, `sampling:`); redaction pass run; `gaps` recorded (never fabricated).
|
|
511
555
|
|
|
556
|
+
> **Algorithm reconstructions have an extra bar (§5.6):** if the dimension reconstructed *behaviour*
|
|
557
|
+
> rather than a catalog, it is `partial` until the implementation is executable, differentially tested
|
|
558
|
+
> against the live product with the **estimator and feature extraction scored as separate layers**, and
|
|
559
|
+
> the inputs + observed outputs are frozen as re-runnable fixtures. Record the per-layer pass rates.
|
|
560
|
+
|
|
512
561
|
> **Self-check before writing `status: complete`:** count the verbatim artifacts in `raw/`. If that count is **zero**, the dimension is `partial` no matter how good the prose is — you have written an essay, not a capture. If a Finding names a number, a path, a version, or a field, open the file that contains it; if no such file exists, write it now (§5.3).
|
|
513
562
|
|
|
514
563
|
**Summary-first + bounded, visible fan-out (never go silent long enough to look dead).** Write a `status: partial` `_summary.md` with whatever is mined so far **before** any long-tail fetch loop (downloading many chunks, crawling many pages, deep pagination), then enrich it in place and flip to `complete`. **Bound** every fan-out — cap it by count / total bytes / wall-time (e.g. ≤40 items or ≤90 s) and record the remainder as a `gap` rather than fetching exhaustively; emit a progress note per batch. A collector that fetches everything before writing anything can run many times longer than its siblings and read as hung to the orchestrator, forcing a needless salvage. Partial-but-finalized beats complete-but-silent.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nurix/etna",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.6",
|
|
4
4
|
"description": "Bootstrap a project with a Nurix harness — copies the harness's CLAUDE.md, skills, agents, rules, and hooks directly into the project from a bundled payload (no marketplace fetch, no network).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,5 +42,5 @@
|
|
|
42
42
|
"agent",
|
|
43
43
|
"plugin"
|
|
44
44
|
],
|
|
45
|
-
"etnaFingerprint": "
|
|
45
|
+
"etnaFingerprint": "5e362f34919c"
|
|
46
46
|
}
|