@nurix/etna 0.19.23 → 0.19.25

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.
Files changed (35) hide show
  1. package/README.md +20 -14
  2. package/data/harness-index.json +1 -1
  3. package/data/harnesses/code/.vscode/settings.json +9 -0
  4. package/data/harnesses/code/CLAUDE.md +6 -1
  5. package/data/harnesses/code/agents/maintenance.md +2 -2
  6. package/data/harnesses/code/harness.json +2 -2
  7. package/data/harnesses/code/rules/database.md +29 -1
  8. package/data/harnesses/code/skills/boundary/SKILL.md +1 -1
  9. package/data/harnesses/code/skills/brainstorm/SKILL.md +1 -2
  10. package/data/harnesses/code/skills/design/SKILL.md +1 -1
  11. package/data/harnesses/code/skills/documentation/SKILL.md +1 -1
  12. package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +3 -1
  13. package/data/harnesses/code/skills/test-plan/SKILL.md +82 -0
  14. package/data/harnesses/code/skills/workflow-mapper/SKILL.md +1 -1
  15. package/data/harnesses/discover/.vscode/settings.json +9 -0
  16. package/data/harnesses/discover/harness.json +1 -1
  17. package/data/harnesses/discussion/.vscode/settings.json +9 -0
  18. package/data/harnesses/discussion/harness.json +2 -2
  19. package/data/harnesses/discussion/rules/artifacts.md +1 -1
  20. package/data/harnesses/discussion/rules/method.md +2 -0
  21. package/data/harnesses/etna/.vscode/settings.json +9 -0
  22. package/data/harnesses/etna/agents/maintenance.md +2 -2
  23. package/data/harnesses/etna/harness.json +2 -2
  24. package/data/harnesses/etna/skills/boundary/SKILL.md +1 -1
  25. package/data/harnesses/etna/skills/brainstorm/SKILL.md +1 -2
  26. package/data/harnesses/etna/skills/documentation/SKILL.md +1 -1
  27. package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +3 -1
  28. package/data/harnesses/etna/skills/workflow-mapper/SKILL.md +1 -1
  29. package/data/harnesses/hr/.vscode/settings.json +9 -0
  30. package/data/harnesses/hr/harness.json +2 -2
  31. package/data/harnesses/hr/skills/evaluator/SKILL.md +1 -1
  32. package/data/harnesses/hr/skills/interview-eval/SKILL.md +1 -1
  33. package/data/harnesses/kb-agi/.vscode/settings.json +9 -0
  34. package/data/harnesses/kb-agi/harness.json +1 -1
  35. package/package.json +1 -1
package/README.md CHANGED
@@ -20,7 +20,7 @@ npx --yes @nurix/etna --name=code
20
20
 
21
21
  The `--yes` flag skips npx's install confirmation, so the command runs unattended. On startup the CLI also **self-updates**: it checks the registry and, if the copy npx resolved is behind the latest published version, re-execs the latest before installing — so `npx --yes @nurix/etna` always runs the newest harness, even when npx has an older copy cached. The check is quick but synchronous; set `ETNA_NO_SELFUPDATE=1` to skip it (CI, offline, or a pinned local build).
22
22
 
23
- After this completes:
23
+ After this completes (the `code` harness shown — the component folders differ per harness, and one a harness ships nothing for is simply absent):
24
24
 
25
25
  ```
26
26
  your-project/
@@ -30,17 +30,17 @@ your-project/
30
30
  ├── .prettierignore
31
31
  ├── .vscode/settings.json ← editor settings (markdown formatter on save)
32
32
  └── .claude/
33
- ├── skills/ ← documentation, boundary, brainstorm, workflow-mapper, design
34
- ├── agents/ ← lld-mode, brainstorm, maintenance, change-review, performance-review
35
- ├── rules/ ← general.md, frontend.md, express.md, nomenclature.md, database.md, testing.md, pnpm.md
36
- ├── hooks/ ← generate-ownership-index (SessionStart)
33
+ ├── skills/ ← boundary, brainstorm, design, documentation, test-plan, workflow-mapper
34
+ ├── agents/ ← brainstorm, change-review, lld-mode, maintenance, performance-review, retro
35
+ ├── rules/ ← database.md, express.md, frontend.md, general.md, nomenclature.md, pnpm.md, testing.md
36
+ ├── hooks/ ← generate-ownership-index (SessionStart) + hooks.json
37
37
  ├── settings.json ← the hook is deep-merged in here (your existing keys preserved)
38
- └── harness.json ← {version, digest, owned} — the installed-harness sentinel
38
+ └── harness.json ← {harness, name, version, files, digest, owned} — the installed-harness sentinel
39
39
  ```
40
40
 
41
- Alongside the Claude tree, the installer projects the **Codex** artifacts so the repo also works under OpenAI Codex: `AGENTS.md` (above), `.agents/skills/` (a mirror of `.claude/skills/`, Codex's skill-scan path), and `.codex/agents/*.toml` + `.codex/hooks.json` (its subagents and SessionStart hook). Nothing is written outside the project. No marketplace registration, no `enabledPlugins` entry, no global cache dependency — Claude Code picks up the project-scoped agents/skills/rules on its next session start (and Codex reads `AGENTS.md` + `.agents/` + `.codex/`), and the hooks wire in through `.claude/settings.json`.
41
+ Alongside the Claude tree, the installer projects the **Codex** artifacts so the repo also works under OpenAI Codex: `AGENTS.md` (above), `.agents/skills/` (a mirror of `.claude/skills/`, Codex's skill-scan path), and `.codex/agents/*.toml` + `.codex/hooks.json` (its subagents and SessionStart hook) — each surface written only when the harness ships that component kind. Nothing is written outside the project. No marketplace registration, no `enabledPlugins` entry, no global cache dependency — Claude Code picks up the project-scoped agents/skills/rules on its next session start (and Codex reads `AGENTS.md` + `.agents/` + `.codex/`), and the hooks wire in through `.claude/settings.json`.
42
42
 
43
- Re-run `npx --yes @nurix/etna --name=code` anytime to refresh the harness content with what the latest published package ships. Existing same-named files are overwritten; files you've added that aren't part of the harness are left alone. In a git repo the previous `CLAUDE.md` is overwritten in place (recover it with `git checkout CLAUDE.md`); outside a git repo it's backed up to `CLAUDE.md.bak.<ISO-timestamp>`. Because the new `harness.json` carries the installed `{version, digest}`, a re-run whose **major** version is ahead of your repo's stamped `harness-version` is what the reconcile (below) detects as a structural upgrade.
43
+ Re-run `npx --yes @nurix/etna --name=code` anytime to refresh the harness content with what the latest published package ships. Existing same-named files are overwritten, and a component the harness used to own but no longer ships is pruned; files you've added that aren't part of the harness are left alone. In a git repo the previous `CLAUDE.md` and `.claude/` are overwritten and refreshed in place (recover them with `git checkout`); outside a git repo `CLAUDE.md` is backed up to `CLAUDE.md.bak.<ISO-timestamp>` and the whole `.claude/` tree is snapshotted to `.claude.bak.<ISO-timestamp>` first. Because the new `harness.json` carries the installed `{version, digest}`, a re-run whose **major** version is ahead of your repo's stamped `harness-version` is what the reconcile (below) detects as a structural upgrade.
44
44
 
45
45
  ## After install: bootstrap the harness
46
46
 
@@ -53,7 +53,7 @@ Then work normally. Boundaries aren't generated as files — the `boundary` skil
53
53
 
54
54
  One thing worth knowing on an existing repo:
55
55
 
56
- - **Your root `CLAUDE.md` is replaced** with the harness policy file (the old one is saved to `CLAUDE.md.bak.<ISO>`). If it held project-specific instructions, fold them back in after install — the harness file is the policy skeleton; your domain specifics sit alongside it.
56
+ - **Your root `CLAUDE.md` is replaced** with the harness policy file (recoverable from git, or from `CLAUDE.md.bak.<ISO>` outside a git repo). If it held project-specific instructions, fold them back in after install — the harness file is the policy skeleton; your domain specifics sit alongside it.
57
57
 
58
58
  ## Migrating from 0.1.x
59
59
 
@@ -67,8 +67,12 @@ Listed by `npx --yes @nurix/etna` (no args) using the bundled `data/harness-inde
67
67
 
68
68
  | Harness | Source in etna | What it ships |
69
69
  | --- | --- | --- |
70
- | `code` | [`harness/code/`](../../harness/code/) | Governance + session agents, baseline workflow skills, the ownership-index hook (merged single-folder layout) |
71
- | `kb-agi` | [`harness/kb-agi/`](../../harness/kb-agi/) | Knowledge-vault scaffolding |
70
+ | `code` | [`harness/code/`](../../harness/code/) | The canonical Nurix code-project harness — 6 governance/session agents, 6 workflow skills, 7 stack rules, the ownership-index `SessionStart` hook |
71
+ | `discover` | [`harness/discover/`](../../harness/discover/) | The product-discovery harness — 11 owned dimension-collector agents and 9 owned method rules, plus a `tooling-recommendations.md` at the project root. No skills, no hooks |
72
+ | `discussion` | [`harness/discussion/`](../../harness/discussion/) | The deliberation-workspace harness — 2 owned agents, 2 owned rules, and the `update-topics` hook that keeps the topic index current. No skills |
73
+ | `etna` | [`harness/etna/`](../../harness/etna/) | The forge's own governance harness — a deliberate fork of `code` reframed for authoring artifacts: 5 agents, 4 skills, 4 rules, the ownership-index hook |
74
+ | `hr` | [`harness/hr/`](../../harness/hr/) | The hiring / AI-steering evaluation harness — the `interview-eval` and `evaluator` skills plus one batching rule. No agents, no hooks |
75
+ | `kb-agi` | [`harness/kb-agi/`](../../harness/kb-agi/) | The self-improving knowledge-base harness — 3 owned vault agents (`kb-init`, `kb-audit`, `kb-apply`) and the owned `kb-export.mjs` script. No skills, no rules |
72
76
 
73
77
  ## How it works
74
78
 
@@ -88,15 +92,17 @@ Two kinds of membership:
88
92
  # harness/code/readme.md
89
93
  ---
90
94
  name: code
91
- skills: [boundary, brainstorm, documentation, workflow-mapper, design]
95
+ skills:
96
+ [boundary, test-plan, brainstorm, documentation, workflow-mapper, design]
92
97
  rules: [general, frontend, express, nomenclature, database, testing, pnpm]
93
- agents: [brainstorm, change-review, lld-mode, maintenance, performance-review]
98
+ agents:
99
+ [brainstorm, change-review, lld-mode, maintenance, performance-review, retro]
94
100
  ---
95
101
  ```
96
102
 
97
103
  - **Owned, by location** — hooks, scripts (and any unlisted, harness-specific agents) live inside the harness folder (`harness/<name>/{agents,hooks,scripts}/`). No list — membership is presence in the folder, exactly how a Claude Code plugin ships them. Shared governance agents are listed instead (above) and resolved from the `agents/` catalog, local-first; a harness can still drop its own copy beside it to override one by name.
98
104
 
99
- `scripts/build-data.js` (run by `prepack`) resolves the listed skills/rules/agents from the catalogs (local-first), copies any unlisted harness-owned agents plus the harness's `hooks/` and `scripts/` wholesale into `data/harnesses/<folder>/`; a listed skill/rule/agent that doesn't exist is a hard error. The marketplace channel (`.github/scripts/publish.mjs`) reads the same harness folder and resolves the listed skills/rules/agents the same local-first way — but the two channels' **footprints differ**: the marketplace plugin carries no root policy, and its Codex surface is skills + hooks only (Codex custom agents ship through this CLI). The installer (`bin/etna.js`) reads from the bundle at install time; no GitHub fetch at runtime.
105
+ `scripts/build-data.js` (run by `prepack`) resolves the listed skills/rules/agents from the catalogs (local-first), copies any unlisted harness-owned agents plus the harness's `hooks/` and `scripts/` wholesale into `data/harnesses/<folder>/`, adds the `harness/common/` root files and each bundled agent's `*.codex.toml` twin, and writes the `harness.json` marker (version, `files` list, kit digest); a listed skill/rule/agent that doesn't exist is a hard error. The marketplace channel (`.github/scripts/publish.mjs`) reads the same harness folder and resolves the listed skills/rules/agents the same local-first way — but the two channels' **footprints differ**: the marketplace plugin carries no root policy, and its Codex surface is skills + hooks only (Codex custom agents ship through this CLI). The installer (`bin/etna.js`) reads from the bundle at install time; no GitHub fetch at runtime.
100
106
 
101
107
  ## Building / contributing
102
108
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "fingerprint": "5a7e414ba0b5",
2
+ "fingerprint": "81d321149cba",
3
3
  "harnesses": {
4
4
  "code": {
5
5
  "plugin": "code",
@@ -6,5 +6,14 @@
6
6
  "editor.defaultFormatter": "esbenp.prettier-vscode",
7
7
  "editor.formatOnSave": true,
8
8
  "editor.wordWrap": "on"
9
+ },
10
+ // Hide harness-authoring scaffolding from the file explorer
11
+ "files.exclude": {
12
+ ".claude": true,
13
+ ".vscode": true,
14
+ ".agents": true,
15
+ ".codex": true,
16
+ "**/node_modules": true,
17
+ "**/apollo.service.json": true
9
18
  }
10
19
  }
@@ -35,7 +35,8 @@ Three peer modes plus a Completion step — enter wherever the user's thinking a
35
35
 
36
36
  1. Freeze the plan — `Status: Complete`, fill the Completed date. The file is now frozen.
37
37
  2. Flip the feature LLD to `status: active`; extend it in place with the how-it-works sections; confirm its `owns:` globs against the actual code.
38
- 3. The `boundary` skill must already have gated this plan before its first implementation step — see Ownership, Boundaries & Documentation.
38
+ 3. The `boundary` and `test-plan` skills must already have gated this plan before its first implementation step — see Ownership, Boundaries & Documentation.
39
+ 4. Derive/update the behavioral checklist at `docs/tests/{feature}.md` via the `documentation` skill (a truth document — present-tense, referencing the LLD's promises, restating no intent), then run the affected test suite + the project's e2e smoke gate and record the results in the changelog entry.
39
40
 
40
41
  **On Brainstorm Close** (when a session ends, not when a plan completes): close + harvest is the skill's `stop` procedure — converge, a user-approved harvest manifest, apply, then a uselessness test; conclusions disperse by scope (architecture docs, decision records, backlog), the record moves from `docs/research/{topic}-brainstorm/` to `docs/history/brainstorms/{topic}/`, and the user decides whether the moved folder is deleted or committed.
41
42
 
@@ -62,6 +63,10 @@ Boundaries are **diagnosed, not documented** — the `boundary` skill derives th
62
63
 
63
64
  **Invoke the `boundary` skill after a plan is approved and before its first implementation step.** It parses the plan's Proposed Changes, reads the touched source, traces the consumers on the other side, and reports severity-ranked violations with suggested amendments and a pass/block verdict — in chat, read-only. Fold the amendments into the plan before coding. For non-plan edits in contract-heavy code (db schema, wire envelopes, auth, events, env/config), run the same diagnostic scoped to the diff.
64
65
 
66
+ ### Test obligations — specify before implementing
67
+
68
+ **After `boundary` passes and its amendments are folded, invoke the `test-plan` skill** — same plan gate, ordered second. It derives the tests the plan owes from its Proposed Changes and the owning LLD, and writes a **Test Obligations** section into the plan (one row per promised behavior: layer · target file · oracle · fixtures). It writes no test files — the tests land *with* the code at implementation time (the suite-first guardrail), and the manifest is the checklist that work satisfies. This makes obligations survive dispatch to an executor that sees only the plan file, and lets a later doc-health pass prove the tests match the promises. On a boundary **BLOCK** it does not run — re-plan first. The three-moment split (gate → implementation → Completion checklist), the layer rules, and the e2e altitude gate live in the skill.
69
+
65
70
  ### Documentation — close the loop
66
71
 
67
72
  - **Every markdown document goes through the `documentation` skill.** Before creating or substantively editing any `.md` document — a doc, a README, an index, a record — invoke the `documentation` skill and apply its templates and `references/style-guide.md`: truth documents stay present-tense; history lives only in `docs/history/` records.
@@ -143,7 +143,7 @@ docs/
143
143
  │ ├── brainstorms/ # Harvested brainstorm sessions ({topic}/) — user decides kept (committed) or deleted; Readme.md is the index
144
144
  │ └── backlog.md # Tracked work items by priority (P0-P3)
145
145
  ├── memory/ # index.md, main.md, {agent}.md — project knowledge + per-agent strategy memory
146
- └── tests/ # Optional, author-maintained — create only if test docs exist
146
+ └── tests/ # Behavioral test checklists ({feature}.md, step → **Assert**), derived at plan Completion via the `documentation` skill; author-maintained, present only when test docs exist
147
147
  ```
148
148
 
149
149
  There is **no `docs/routing.md`**, **no `docs/modules/{m}/`**, **no `docs/pillars/`**, **no `docs/boundaries/`**. Code ownership lives in feature `owns:` frontmatter; a module is a `module:` label, not a folder; boundaries are diagnosed from source; brainstorm records live in `docs/research/` (active) and `docs/history/brainstorms/` (harvested).
@@ -152,7 +152,7 @@ There is **no `docs/routing.md`**, **no `docs/modules/{m}/`**, **no `docs/pillar
152
152
 
153
153
  ### Step B1 — Inventory and classify
154
154
 
155
- Glob every markdown file. Classify each: Architecture, Features, Operations, Plans, Changelogs, Backlog, Brainstorm records (stay), Stale/Deprecated (delete), Root docs (stay). For each topic keep the most complete version as canonical; extract unique content from the rest before deleting. **No redirect stubs** — when you move a file, delete the original and fix every reference (cross-doc links, memory files). Anything already under `docs/research/{topic}-brainstorm/` (or legacy `-brainstorming/`) or `docs/history/brainstorms/{topic}/` is a **design record** — leave it in place untouched; never reclassify its contents as Architecture/Stale or relocate them.
155
+ Glob every markdown file. Classify each: Architecture, Features, Operations, Tests (behavioral checklists — stay), Plans, Changelogs, Backlog, Brainstorm records (stay), Stale/Deprecated (delete), Root docs (stay). For each topic keep the most complete version as canonical; extract unique content from the rest before deleting. **No redirect stubs** — when you move a file, delete the original and fix every reference (cross-doc links, memory files). Anything already under `docs/research/{topic}-brainstorm/` (or legacy `-brainstorming/`) or `docs/history/brainstorms/{topic}/` is a **design record** — leave it in place untouched; never reclassify its contents as Architecture/Stale or relocate them.
156
156
 
157
157
  While reading each markdown doc to classify it, if the doc names a code symbol it asserts exists that **statically resolves to nothing**, note it as an observation for the Part A audit — **only** on stacks where static resolution is reliable (stay silent on codegen/reflection/DI/barrel stacks); scoped to docs already being read here, never a whole-repo prose sweep; **docs only**, never code-file comments.
158
158
 
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "harness": "code",
3
3
  "name": "code",
4
- "version": "3.2.1",
4
+ "version": "3.3.1",
5
5
  "files": [
6
6
  ".prettierignore",
7
7
  ".prettierrc.json",
8
8
  ".vscode/settings.json"
9
9
  ],
10
- "digest": "efcfde0efb8d"
10
+ "digest": "5d18dbd37c26"
11
11
  }
@@ -4,6 +4,34 @@ Fixed conventions for relational schemas — table/column/index **names**, the *
4
4
 
5
5
  Written for SQLite / Cloudflare D1 + Drizzle (the concrete types below). The **names and key vocabulary are engine-agnostic** — a Postgres project keeps the same column names and semantics with native types (`uuid`, `timestamptz`, `boolean`).
6
6
 
7
+ ## What earns a table
8
+
9
+ A table is the most expensive home for data — it costs a migration to change its shape, a join to read, and a mirror to keep in sync. Default to **not** a table. A concept earns its own table only when it needs at least one property in the left column; lacking all of them, the data belongs in the cheaper home on the right.
10
+
11
+ | A table is earned by… | Lacking all of these, the data belongs in… |
12
+ | -------------------------------------------------------------------------- | ------------------------------------------------------- |
13
+ | identity minted at **runtime** | — |
14
+ | variation **per tenant / user** | — |
15
+ | **independent addressing** — queried, indexed, or FK'd in isolation | a **JSON column** on its parent row |
16
+ | **concurrency-safe** writes — racing writers must not clobber a shared blob | — |
17
+ | a **DB-enforced invariant** — immutability trigger, unique, atomic boundary | — |
18
+ | dev-authored, published on deploy, identical for every tenant | **config / a constants file**, validated at build |
19
+ | exactly recomputable from other rows | a **cache** (KV / edge), recomputed on demand |
20
+ | process state that is dead once the request completes | a **log or workflow engine**, never the domain DB |
21
+ | owned by another system-of-record | a **reference column** — store the id, not the data |
22
+
23
+ Each routing rule is one question:
24
+
25
+ - **Static vocabulary or a fixed graph → config, not a table.** Dev-authored, same for every tenant, never FK'd at runtime ⇒ a constants file validated at build, surfaced in code as a constant/type. Reifying it into tables plus a seed makes the file and the tables a two-copy sync problem. The refined boundary is § Vocabulary / lookup tables.
26
+ - **History → one immutable log + a mutable head.** Never shatter versioning into per-entity `*_revisions` / `*_revision_sections` tables. Snapshot the coherent set once into an append-only versions table; current state is a derived projection of the newest entry. _Test: is this table just "old rows of that table"? → collapse it into the snapshot._
27
+ - **Process state is not domain state.** _Test: if it were deleted the instant the request finished, would a product feature break? No ⇒ it is a log line or a workflow-engine concern._
28
+ - **Derived output is a cache, not truth.** _Test: is it exactly recomputable from other rows + code? Yes ⇒ recompute and cache; never persist as first-class state._
29
+ - **Un-addressed detail is a column.** _Test: is this row ever SELECTed / JOINed / FK'd alone? No ⇒ it is a JSON field on its parent._
30
+ - **Another system's record → keep the pointer.** _Test: does a specialized service already own this — chat, telemetry, object store? ⇒ store its id, not a copy._
31
+ - **A surviving table names the invariant it buys.** Each kept structure pays for one guarantee — content-addressed immutable rows buy reproducibility, a monotonic sequence buys ordering/sync, an atomic write boundary buys multi-row atomicity. Drop the requirement and the table drops with it; keep the requirement and the structure is the price a blob cannot pay.
32
+
33
+ Table count tracks **requirements, not domain richness** — a rich domain does not entitle a schema to a table per noun.
34
+
7
35
  ## Keys & the shared vocabulary
8
36
 
9
37
  Three column roles are fixed across every table and every project:
@@ -33,6 +61,6 @@ Reference columns name what they point at: `<entity>_id` for a UUID-PK reference
33
61
 
34
62
  ## Vocabulary / lookup tables
35
63
 
36
- A controlled-vocabulary table (statuses, kinds, categories) follows a fixed shape: `id` (UUID) · `slug` (UNIQUE) · `label` · optional `description` · `status` · timestamps.
64
+ A controlled vocabulary earns a **table** only when a user row FKs into it and reads its `label` / `description` at runtime; a controlled vocabulary that is static, dev-authored config nobody references by FK stays **config** (§ What earns a table) — enumerable is not the same as tabular, and a `type`-style column validated against the constants file is preferred over an FK to a seeded mirror. When a table is earned, it follows a fixed shape: `id` (UUID) · `slug` (UNIQUE) · `label` · optional `description` · `status` · timestamps.
37
65
 
38
66
  - **Seeds are generated, never hand-written** — the source of truth is a CSV or a shared package; a generator emits the `*.seed.sql` with UUIDv5 ids and an idempotent `ON CONFLICT(slug)` upsert that preserves live counts / admin-tuned columns. Hand-editing seed SQL is ❌ — regenerate it.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: boundary
3
- description: "Boundary diagnostic for implementation plans. Reads the source a plan touches, finds the cross-cutting contracts it joins (db schema, wire envelope, auth, events), and reports violations with a pass/block verdict. Read-only; also runs on a diff. Triggers on 'boundary check', 'what could break'."
3
+ description: "Use when a plan or diff might break a cross-cutting contract. Boundary diagnostic: reads the source a plan touches — db schema, wire envelope, auth, events — and reports violations with a pass/block verdict. Read-only; also runs on a diff. Triggers on 'boundary check', 'what could break'."
4
4
  ---
5
5
 
6
6
  # Boundary Diagnostic
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: brainstorm
3
- description: "Thinking-partner session for designing large-scale software changes. Invoked ONLY by `/brainstorm start` and `/brainstorm stop` — never on phrases like 'let's plan' or 'let's design'. Entry needs a basis (a reference doc, prior session folder, or brain dump); drip-sized asks are redirected."
4
- argument-hint: "start | stop"
3
+ description: "Use when starting or stopping a large-scale design brainstorm via `/brainstorm start | stop` — never on phrases like 'let's plan' or 'let's design'. Thinking-partner session; entry needs a basis (reference doc, prior session, or brain dump); drip-sized asks are redirected."
5
4
  ---
6
5
 
7
6
  # Brainstorm
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: design
3
- description: 'Design operator web UIs — build, improve, judge, or extract: dashboards, CRMs, work queues, admin panels, internal tools. Also the @nurix/components reference — forms, data tables, modals, side panels, any @nurix/components import. Any web stack. Not marketing sites, storefronts, or mobile-native.'
3
+ description: "Use when building, improving, judging, or extracting operator web UIs — dashboards, CRMs, work queues, admin panels, internal tools. Also the @nurix/components reference: forms, data tables, modals, side panels, any import. Any web stack. Not marketing sites, storefronts, or mobile-native."
4
4
  ---
5
5
 
6
6
  # design
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: documentation
3
- description: Invoke BEFORE creating or substantively editing ANY markdown doc. Templates + style authority for technical docs — LLDs, ADs, plans, changelogs, READMEs, runbooks. Owns the tense rule: truth docs state the present; history only in dated records. Baselining is the maintenance agent.
3
+ description: "Use when creating or substantively editing ANY markdown doc — invoke BEFORE writing. Templates + style authority for technical docs: LLDs, ADs, plans, changelogs, READMEs, runbooks. Owns the tense rule: truth docs state the present; history only in dated records. Baselining is the maintenance agent."
4
4
  ---
5
5
 
6
6
  # Technical Documentation Skill
@@ -11,7 +11,8 @@ const RULES = [
11
11
  { id: 'progress-marker', severity: 'block', scope: 'truth', pattern: /\bphases?\s+\d[^.\n]{0,40}\b(complete|done)\b|\b(build|implementation) (is )?(\d+%|partially) (complete|done)\b/i, message: 'progress marker — progress lives in the plan/backlog record' },
12
12
  { id: 'temporal-hedge', severity: 'warn', scope: 'truth', pattern: /\bnot yet\b|\bfor now\b|\bcurrently\b|\bstill (unsupported|missing|pending|unimplemented|in progress)\b/i, message: 'temporal hedge — state the fact without "currently / not yet / for now"' },
13
13
  { id: 'predecessor-frame', severity: 'warn', scope: 'truth', pattern: /\bpreviously\b|\bthe old \w+\b|\blegacy \w+ (was|is)\b/i, message: 'predecessor framing — describe the one current design' },
14
- { id: 'status-glyph', severity: 'warn', scope: 'truth', pattern: /(^|\s)(✅|⏳|- \[[ x]\])/u, message: 'status glyph / checklist in a truth doc — tracking belongs in a record' },
14
+ // docs/tests/ behavioral checklists use `- [ ]` as test-step notation (step → **Assert**), not progress tracking — exempt that one folder from this rule; every other truth doc still bans checkboxes.
15
+ { id: 'status-glyph', severity: 'warn', scope: 'truth', exempt: /(^|\/)docs\/tests\//, pattern: /(^|\s)(✅|⏳|- \[[ x]\])/u, message: 'status glyph / checklist in a truth doc — tracking belongs in a record' },
15
16
  { id: 'date-in-truth-doc', severity: 'warn', scope: 'truth', pattern: /\b20\d{2}-\d{2}-\d{2}\b/, message: 'date in a truth doc — only the AD Decision Changelog and structure.md Health Log tails carry dates' },
16
17
  ];
17
18
 
@@ -71,6 +72,7 @@ function lintFile(path, findings) {
71
72
 
72
73
  for (const rule of RULES) {
73
74
  if (rule.scope !== scope || disabled.has(rule.id)) continue;
75
+ if (rule.exempt && rule.exempt.test(path)) continue;
74
76
  for (const { n, line } of lines) {
75
77
  if (!rule.pattern.test(line)) continue;
76
78
  if (line.includes(`doc-lint-disable: ${rule.id}`) || new RegExp(`doc-lint-disable:[^>]*\\b${rule.id}\\b`).test(line)) continue;
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: test-plan
3
+ description: "Use when deriving the tests a plan owes. Test-obligation diagnostic: runs at the plan gate after boundary, reads the plan's Proposed Changes + owning LLD, and writes a Test Obligations section (a row per behavior: layer, target file, oracle, fixtures). Writes no test files. Triggers on 'test plan'."
4
+ ---
5
+
6
+ # Test-Obligation Diagnostic
7
+
8
+ You derive the tests an implementation plan owes, and pin them into the plan as an explicit, reviewable manifest — at the same gate `boundary` runs, ordered second. You never write a test file; you make the standing "tests ship with the change" guardrail *concrete* for this plan, so the implementer satisfies a checklist instead of a vibe, and so a later doc-health pass can prove the tests match the promises.
9
+
10
+ **The litmus is "which promise, at which altitude, proven by what?"** A plan promises behavior (an LLD's `owns:` surface does X); each promise earns exactly one test at the cheapest layer that can actually prove it. Spend your reading deciding the *layer* and the *oracle*, not on prose.
11
+
12
+ ## The three-moment split
13
+
14
+ Testing spans three moments in the plan lifecycle. This skill owns **only the first** — it is why "write the tests at plan time" is wrong: at the gate the code does not exist yet, so the only coherent artifact is the manifest.
15
+
16
+ | Moment | Artifact | Owner |
17
+ | --- | --- | --- |
18
+ | **Plan gate** (here, after boundary) | Test Obligations section written into the plan | this skill |
19
+ | **Implementation** | the obligated tests, green in the same commits as the code | the implementer (standing guardrail — this skill never pre-empts it) |
20
+ | **Completion** | behavioral checklist at `docs/tests/{feature}.md`; run affected suite + the project's e2e smoke gate; note in changelog | the `documentation` skill + Completion steps |
21
+
22
+ Writing tests *before* the code they cover either fabricates implementation coupling (labels/routes/copy the plan hasn't fixed) or leaves the suite red on the shared branch for the plan's whole duration. Neither is acceptable — so the gate emits obligations, and the tests land *with* the code.
23
+
24
+ ## Contract sources
25
+
26
+ - **The plan's Proposed Changes** — files created/modified, entities/state, API changes, cross-module contracts. Each is a behavior that may owe a test.
27
+ - **The owning LLD** (`docs/features/{feature}.md`, matched via the ownership index) — the canonical statement of *what the feature does*. Every obligation names the LLD promise it proves; the LLD is never restated, only referenced. This one-directional chain (LLD → manifest → tests + Completion checklist) is what stops the three artifacts drifting into three contradictory truths.
28
+ - **`.claude/rules/testing.md`** — the frameworks and canonical placement per stack. The manifest's target paths and runners come from here, never invented. The repo's existing runner always wins.
29
+ - **`boundary`'s output** — a HIGH/CRITICAL finding it surfaced is itself a behavior owed a regression test (the failure mode, asserted). Fold boundary's amendments first, then read the amended plan.
30
+
31
+ ## Layer assignment
32
+
33
+ Assign each behavior the **cheapest layer that can prove it**, then name the oracle (the concrete thing that shows pass/fail):
34
+
35
+ - **unit** — a pure function / reducer / validator. Oracle: return value, thrown error.
36
+ - **component** — a UI unit's behavior in isolation (a widget, form, panel). Oracle: rendered role/text, emitted event. Runner: the stack's component runner per `testing.md`.
37
+ - **integration** — a route/handler against a real in-memory datastore via the app instance (not a mock of what you own). Oracle: response envelope shape, status code, the row actually written. **This is the default for server behavior** — most obligations land here, and this layer is the one the unit gate (`test` script / CI) runs.
38
+ - **e2e** — see the altitude gate below.
39
+
40
+ Error paths, races, and idempotency are obligations too, and they belong at **integration** with injected faults (network mock, the runner's fake timers, a forced constraint) — a process driving a live product cannot reliably *produce* them, so they are never an e2e obligation.
41
+
42
+ ### The e2e altitude gate
43
+
44
+ An e2e obligation (a scenario in the project's e2e tree per `testing.md`) is earned **only** when the behavior genuinely spans processes/services that no single-process test can exercise together. A single-process behavior, however important, is an integration obligation, never e2e.
45
+
46
+ - Density is **one scenario per cross-process journey, not one per feature** — the project's existing scenarios are the reference density. Mark each e2e obligation **"extend scenario `<name>`"** in preference to **"new scenario"**; a new scenario needs a genuinely new journey.
47
+ - **Refuse to emit any e2e obligation while the project's e2e smoke gate is not wired.** A generated spec fed to a suite nothing runs is a rot accelerator. The gate is a capped, tagged smoke subset run in CI (not the full e2e suite — an oversized always-on e2e suite is what gets evicted from CI, taking all enforcement with it). If no such CI gate exists, emit the e2e obligation as a **blocked** row naming the missing gate, not as work to do.
48
+
49
+ ## Run
50
+
51
+ **Input:** a plan path (given, or the newest in `docs/history/plans/`), already boundary-checked with amendments folded. For a non-plan diff in test-worthy code, the hunks stand in for Proposed Changes.
52
+
53
+ 1. **Build the behavior list.** From the plan's Proposed Changes + the owning LLD's promised behavior, list the behaviors this plan introduces or changes. Fold in each HIGH/CRITICAL boundary finding as a regression obligation. Note behaviors a type or the compiler already guards — those owe **no** test (the boundary litmus, inverted: if it can't ship green-but-broken, it needs no test).
54
+
55
+ 2. **Assign a layer + oracle** to each behavior (rules above). Prefer the cheapest layer that can actually prove the promise.
56
+
57
+ 3. **Map to a target file** per `testing.md` — the stack's unit/integration path for most, the e2e scenario tree for a program scenario. Name fixtures/factories (existing helper, or new) and the fault-injection needed (network mock, fake timers, forced constraint). Use the repo-standard fake-data source.
58
+
59
+ 4. **Apply the e2e altitude gate** to any cross-process obligation; downgrade everything else to integration.
60
+
61
+ 5. **Emit the Test Obligations section into the plan**, plus a one-line chat summary. Write it as a subsection of the plan (an executor dispatched with only the plan file sees nothing else — an obligation that lives only in chat is lost at dispatch):
62
+
63
+ ```
64
+ ## Test Obligations
65
+
66
+ | # | Behavior (LLD promise) | Layer | Target file | Oracle | Fixtures / faults |
67
+ | - | ---------------------- | ----- | ----------- | ------ | ----------------- |
68
+
69
+ Coverage: {N} behaviors · {N} integration · {N} component · {N} unit · {N} e2e ({N} extend, {N} new)
70
+ Completion checklist: docs/tests/{feature}.md (derived at Completion, not now)
71
+ ```
72
+
73
+ A behavior with no obligation is stated too, with the reason (compiler-guarded, covered by an existing test) — an empty cell reads as "not yet analyzed"; an explicit "none — compiler-guarded" reads as "checked."
74
+
75
+ ## Rules
76
+
77
+ - **Manifest only.** You write the Test Obligations section into the plan and a chat summary. You write **no** test files and **no** `docs/tests/` checklist — those are implementation-time and Completion-time, owned by others.
78
+ - **Every obligation names its LLD promise.** No promise, no obligation source — trace it or drop it. This is the anti-drift pin.
79
+ - **Cheapest proving layer wins.** Do not obligate an e2e scenario for what an integration test proves, or an integration test for what a unit test proves.
80
+ - **e2e only above the cross-process bar, and only while the smoke gate runs.** Otherwise the obligation is a blocked row naming the missing gate.
81
+ - **Runs after boundary; skipped on BLOCK.** A blocked plan is re-planned first; there is nothing stable to obligate tests against.
82
+ - **A short manifest is valid.** A plan that owes three integration tests owes three rows — do not pad. The coverage line is the evidence it was analyzed, not skipped.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: workflow-mapper
3
- description: "Discover and document a codebase's major workflows by tracing call chains from user action to database or external system — produces mermaid sequence diagrams and markdown. Triggers on 'map workflows', 'trace flows', 'sequence diagram', or 'how does X work end-to-end'."
3
+ description: "Use when mapping a codebase's major workflows — traces call chains from user action to database or external system and produces mermaid sequence diagrams + markdown. Triggers on 'map workflows', 'trace flows', 'sequence diagram', 'how does X work end-to-end'."
4
4
  ---
5
5
 
6
6
  # Workflow Mapper Skill
@@ -6,5 +6,14 @@
6
6
  "editor.defaultFormatter": "esbenp.prettier-vscode",
7
7
  "editor.formatOnSave": true,
8
8
  "editor.wordWrap": "on"
9
+ },
10
+ // Hide harness-authoring scaffolding from the file explorer
11
+ "files.exclude": {
12
+ ".claude": true,
13
+ ".vscode": true,
14
+ ".agents": true,
15
+ ".codex": true,
16
+ "**/node_modules": true,
17
+ "**/apollo.service.json": true
9
18
  }
10
19
  }
@@ -8,5 +8,5 @@
8
8
  ".vscode/settings.json",
9
9
  "tooling-recommendations.md"
10
10
  ],
11
- "digest": "3b747e869c3a"
11
+ "digest": "43b78e58bf42"
12
12
  }
@@ -6,5 +6,14 @@
6
6
  "editor.defaultFormatter": "esbenp.prettier-vscode",
7
7
  "editor.formatOnSave": true,
8
8
  "editor.wordWrap": "on"
9
+ },
10
+ // Hide harness-authoring scaffolding from the file explorer
11
+ "files.exclude": {
12
+ ".claude": true,
13
+ ".vscode": true,
14
+ ".agents": true,
15
+ ".codex": true,
16
+ "**/node_modules": true,
17
+ "**/apollo.service.json": true
9
18
  }
10
19
  }
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "harness": "discussion",
3
3
  "name": "discussion",
4
- "version": "0.4.2",
4
+ "version": "0.5.1",
5
5
  "files": [
6
6
  ".prettierignore",
7
7
  ".prettierrc.json",
8
8
  ".vscode/settings.json"
9
9
  ],
10
- "digest": "fa5d8b4fa816"
10
+ "digest": "3f0234327768"
11
11
  }
@@ -18,7 +18,7 @@ A ratified class-3 doc **is** the goal-track canon — never double-book it unde
18
18
 
19
19
  **Status vocabulary**: `**DRAFT — not yet ratified**` / `Not yet user-ratified` · `(ratified YYYY-MM-DD)` — at doc, section, or extract granularity · `**Superseded note (YYYY-MM-DD):** … kept as record` · `deferred — blocked on <gate>` vs the exclusion grades.
20
20
 
21
- **Immutability**: raw dumps immutable once filed (corrections land in critic or synthesis) · unratified syntheses may be rewritten in place as v2, recorded in provenance · ratified material changes by dated append or supersession only · nothing valuable is deleted — superseded → banner + keep; broken → flagged; closed threads → struck through. Every lifecycle event carries an ISO date.
21
+ **Immutability**: raw dumps immutable once filed (corrections land in critic or synthesis) · unratified syntheses may be rewritten in place as v2, recorded in provenance · ratified material changes by dated append or supersession only · nothing valuable is deleted — superseded → banner + keep; a retired topic → `git mv` to `archived/`, dated archive banner, kept frozen as record; broken → flagged; closed threads → struck through. Every lifecycle event carries an ISO date.
22
22
 
23
23
  ## The topic README
24
24
 
@@ -9,6 +9,7 @@ How sessions run in this workspace. What gets written is [artifacts.md](artifact
9
9
  - **External instruments** — code/data a topic depends on (scraper, corpus, pipeline) lives **outside the workspace**, never at the root: this folder holds only topics and the harness. Record the dependency on the depending topic's README (external path + ratification status); exclusions are recorded on the excluder's side.
10
10
  - **Git** — the workspace **is a git repository** (the `discussion-baseline` agent initializes one). The SessionEnd hook auto-commits each session's changes as one `📝 session close` batch — never per-file commits. Milestone commits by hand are still welcome: `✨` content, `🔧` workspace/method. Root `.gitignore` stays minimal.
11
11
  - **Grandfathering** — when the method changes, never reshuffle existing artifacts to match. New artifacts follow current rules; old layouts stay, flagged where confusing.
12
+ - **Archiving** — a topic whose inquiry is *closed by outside reality* — its design **realized**, its understanding **superseded** — is retired, not deleted: `git mv` its folder into `archived/<topic>/` (history preserved; the folder frozen, kept as record), add a dated archive banner to its README (`📦 ARCHIVED (YYYY-MM-DD) — realized/superseded`: state · reason · pointer to the live source of truth), and move its `TOPICS.md` entry into the **Archived topics** section. Archiving records that a topic is done; live work stops there and nothing valuable is lost.
12
13
 
13
14
  ## Session protocol
14
15
 
@@ -61,6 +62,7 @@ Method lives in the rules (timeless); state lives in `metadata/` (current); topi
61
62
  - Per topic: `` ## `topic-folder/` — <state phrase> `` — the suffix encodes **who it's waiting on** ("synthesis filed, awaiting user reaction").
62
63
  - One dense routing paragraph: what it is, which artifacts exist (files, dates), headline findings — enough to route without opening the folder.
63
64
  - Bullets: `**Open:**` (typically "user disposition on the N questions closing `<file>`") · exclusion grades, dated when user rulings · `**Note:**` caveats.
65
+ - Retired topics move to an `## Archived topics` section at the bottom — one condensed entry each (why archived: realized/superseded · the live source of truth); a one-line archiving note sits in the preamble. The active list holds only live topics.
64
66
 
65
67
  ### `SESSIONS.md` — session ledger
66
68
 
@@ -6,5 +6,14 @@
6
6
  "editor.defaultFormatter": "esbenp.prettier-vscode",
7
7
  "editor.formatOnSave": true,
8
8
  "editor.wordWrap": "on"
9
+ },
10
+ // Hide harness-authoring scaffolding from the file explorer
11
+ "files.exclude": {
12
+ ".claude": true,
13
+ ".vscode": true,
14
+ ".agents": true,
15
+ ".codex": true,
16
+ "**/node_modules": true,
17
+ "**/apollo.service.json": true
9
18
  }
10
19
  }
@@ -143,7 +143,7 @@ docs/
143
143
  │ ├── brainstorms/ # Harvested brainstorm sessions ({topic}/) — user decides kept (committed) or deleted; Readme.md is the index
144
144
  │ └── backlog.md # Tracked work items by priority (P0-P3)
145
145
  ├── memory/ # index.md, main.md, {agent}.md — project knowledge + per-agent strategy memory
146
- └── tests/ # Optional, author-maintained — create only if test docs exist
146
+ └── tests/ # Behavioral test checklists ({feature}.md, step → **Assert**), derived at plan Completion via the `documentation` skill; author-maintained, present only when test docs exist
147
147
  ```
148
148
 
149
149
  There is **no `docs/routing.md`**, **no `docs/modules/{m}/`**, **no `docs/pillars/`**, **no `docs/boundaries/`**. Code ownership lives in feature `owns:` frontmatter; a module is a `module:` label, not a folder; boundaries are diagnosed from source; brainstorm records live in `docs/research/` (active) and `docs/history/brainstorms/` (harvested).
@@ -152,7 +152,7 @@ There is **no `docs/routing.md`**, **no `docs/modules/{m}/`**, **no `docs/pillar
152
152
 
153
153
  ### Step B1 — Inventory and classify
154
154
 
155
- Glob every markdown file. Classify each: Architecture, Features, Operations, Plans, Changelogs, Backlog, Brainstorm records (stay), Stale/Deprecated (delete), Root docs (stay). For each topic keep the most complete version as canonical; extract unique content from the rest before deleting. **No redirect stubs** — when you move a file, delete the original and fix every reference (cross-doc links, memory files). Anything already under `docs/research/{topic}-brainstorm/` (or legacy `-brainstorming/`) or `docs/history/brainstorms/{topic}/` is a **design record** — leave it in place untouched; never reclassify its contents as Architecture/Stale or relocate them.
155
+ Glob every markdown file. Classify each: Architecture, Features, Operations, Tests (behavioral checklists — stay), Plans, Changelogs, Backlog, Brainstorm records (stay), Stale/Deprecated (delete), Root docs (stay). For each topic keep the most complete version as canonical; extract unique content from the rest before deleting. **No redirect stubs** — when you move a file, delete the original and fix every reference (cross-doc links, memory files). Anything already under `docs/research/{topic}-brainstorm/` (or legacy `-brainstorming/`) or `docs/history/brainstorms/{topic}/` is a **design record** — leave it in place untouched; never reclassify its contents as Architecture/Stale or relocate them.
156
156
 
157
157
  While reading each markdown doc to classify it, if the doc names a code symbol it asserts exists that **statically resolves to nothing**, note it as an observation for the Part A audit — **only** on stacks where static resolution is reliable (stay silent on codegen/reflection/DI/barrel stacks); scoped to docs already being read here, never a whole-repo prose sweep; **docs only**, never code-file comments.
158
158
 
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "harness": "etna",
3
3
  "name": "etna",
4
- "version": "1.1.13",
4
+ "version": "1.1.14",
5
5
  "files": [
6
6
  ".prettierignore",
7
7
  ".prettierrc.json",
8
8
  ".vscode/settings.json"
9
9
  ],
10
- "digest": "27b25f41633e"
10
+ "digest": "4a59f957bd58"
11
11
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: boundary
3
- description: "Boundary diagnostic for implementation plans. Reads the source a plan touches, finds the cross-cutting contracts it joins (db schema, wire envelope, auth, events), and reports violations with a pass/block verdict. Read-only; also runs on a diff. Triggers on 'boundary check', 'what could break'."
3
+ description: "Use when a plan or diff might break a cross-cutting contract. Boundary diagnostic: reads the source a plan touches — db schema, wire envelope, auth, events — and reports violations with a pass/block verdict. Read-only; also runs on a diff. Triggers on 'boundary check', 'what could break'."
4
4
  ---
5
5
 
6
6
  # Boundary Diagnostic
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: brainstorm
3
- description: "Thinking-partner session for designing large-scale software changes. Invoked ONLY by `/brainstorm start` and `/brainstorm stop` — never on phrases like 'let's plan' or 'let's design'. Entry needs a basis (a reference doc, prior session folder, or brain dump); drip-sized asks are redirected."
4
- argument-hint: "start | stop"
3
+ description: "Use when starting or stopping a large-scale design brainstorm via `/brainstorm start | stop` — never on phrases like 'let's plan' or 'let's design'. Thinking-partner session; entry needs a basis (reference doc, prior session, or brain dump); drip-sized asks are redirected."
5
4
  ---
6
5
 
7
6
  # Brainstorm
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: documentation
3
- description: Invoke BEFORE creating or substantively editing ANY markdown doc. Templates + style authority for technical docs — LLDs, ADs, plans, changelogs, READMEs, runbooks. Owns the tense rule: truth docs state the present; history only in dated records. Baselining is the maintenance agent.
3
+ description: "Use when creating or substantively editing ANY markdown doc — invoke BEFORE writing. Templates + style authority for technical docs: LLDs, ADs, plans, changelogs, READMEs, runbooks. Owns the tense rule: truth docs state the present; history only in dated records. Baselining is the maintenance agent."
4
4
  ---
5
5
 
6
6
  # Technical Documentation Skill
@@ -11,7 +11,8 @@ const RULES = [
11
11
  { id: 'progress-marker', severity: 'block', scope: 'truth', pattern: /\bphases?\s+\d[^.\n]{0,40}\b(complete|done)\b|\b(build|implementation) (is )?(\d+%|partially) (complete|done)\b/i, message: 'progress marker — progress lives in the plan/backlog record' },
12
12
  { id: 'temporal-hedge', severity: 'warn', scope: 'truth', pattern: /\bnot yet\b|\bfor now\b|\bcurrently\b|\bstill (unsupported|missing|pending|unimplemented|in progress)\b/i, message: 'temporal hedge — state the fact without "currently / not yet / for now"' },
13
13
  { id: 'predecessor-frame', severity: 'warn', scope: 'truth', pattern: /\bpreviously\b|\bthe old \w+\b|\blegacy \w+ (was|is)\b/i, message: 'predecessor framing — describe the one current design' },
14
- { id: 'status-glyph', severity: 'warn', scope: 'truth', pattern: /(^|\s)(✅|⏳|- \[[ x]\])/u, message: 'status glyph / checklist in a truth doc — tracking belongs in a record' },
14
+ // docs/tests/ behavioral checklists use `- [ ]` as test-step notation (step → **Assert**), not progress tracking — exempt that one folder from this rule; every other truth doc still bans checkboxes.
15
+ { id: 'status-glyph', severity: 'warn', scope: 'truth', exempt: /(^|\/)docs\/tests\//, pattern: /(^|\s)(✅|⏳|- \[[ x]\])/u, message: 'status glyph / checklist in a truth doc — tracking belongs in a record' },
15
16
  { id: 'date-in-truth-doc', severity: 'warn', scope: 'truth', pattern: /\b20\d{2}-\d{2}-\d{2}\b/, message: 'date in a truth doc — only the AD Decision Changelog and structure.md Health Log tails carry dates' },
16
17
  ];
17
18
 
@@ -71,6 +72,7 @@ function lintFile(path, findings) {
71
72
 
72
73
  for (const rule of RULES) {
73
74
  if (rule.scope !== scope || disabled.has(rule.id)) continue;
75
+ if (rule.exempt && rule.exempt.test(path)) continue;
74
76
  for (const { n, line } of lines) {
75
77
  if (!rule.pattern.test(line)) continue;
76
78
  if (line.includes(`doc-lint-disable: ${rule.id}`) || new RegExp(`doc-lint-disable:[^>]*\\b${rule.id}\\b`).test(line)) continue;
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: workflow-mapper
3
- description: "Discover and document a codebase's major workflows by tracing call chains from user action to database or external system — produces mermaid sequence diagrams and markdown. Triggers on 'map workflows', 'trace flows', 'sequence diagram', or 'how does X work end-to-end'."
3
+ description: "Use when mapping a codebase's major workflows — traces call chains from user action to database or external system and produces mermaid sequence diagrams + markdown. Triggers on 'map workflows', 'trace flows', 'sequence diagram', 'how does X work end-to-end'."
4
4
  ---
5
5
 
6
6
  # Workflow Mapper Skill
@@ -6,5 +6,14 @@
6
6
  "editor.defaultFormatter": "esbenp.prettier-vscode",
7
7
  "editor.formatOnSave": true,
8
8
  "editor.wordWrap": "on"
9
+ },
10
+ // Hide harness-authoring scaffolding from the file explorer
11
+ "files.exclude": {
12
+ ".claude": true,
13
+ ".vscode": true,
14
+ ".agents": true,
15
+ ".codex": true,
16
+ "**/node_modules": true,
17
+ "**/apollo.service.json": true
9
18
  }
10
19
  }
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "harness": "hr",
3
3
  "name": "hr",
4
- "version": "0.1.9",
4
+ "version": "0.1.10",
5
5
  "files": [
6
6
  ".prettierignore",
7
7
  ".prettierrc.json",
8
8
  ".vscode/settings.json"
9
9
  ],
10
- "digest": "b547d3ca2c1b"
10
+ "digest": "b9e1f9989578"
11
11
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: evaluator
3
- description: "Apply a production-readiness + product-quality rubric to a single software project. Takes a repo + output dir; writes a per-project workspace (README, `scorecard.yaml`, flat `raw/`) scoring 8 sub-criteria (4 Code + 4 Product) to /100 with evidence and gates. Scores one project, no cohort."
3
+ description: "Use when scoring a single software project for production-readiness + product quality. Takes a repo + output dir; writes a per-project workspace (README, `scorecard.yaml`, flat `raw/`) scoring 8 sub-criteria (4 Code + 4 Product) to /100 with evidence and gates. One project, no cohort."
4
4
  ---
5
5
 
6
6
  # Evaluator
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: interview-eval
3
- description: "Score how a human steered a Claude Code session — prompting, decomposition, verification, debugging, judgment — from `.jsonl` transcripts. For interview/hiring assessment of engineers using AI coding agents; writes a per-session report + scorecard plus a rollup. Judges the human, not the code."
3
+ description: "Use when assessing how a human steered a Claude Code session — prompting, decomposition, verification, debugging, judgment — from `.jsonl` transcripts. For interview/hiring of engineers using AI coding agents; writes a per-session report + scorecard plus a rollup. Judges the human, not the code."
4
4
  ---
5
5
 
6
6
  # interview-eval
@@ -6,5 +6,14 @@
6
6
  "editor.defaultFormatter": "esbenp.prettier-vscode",
7
7
  "editor.formatOnSave": true,
8
8
  "editor.wordWrap": "on"
9
+ },
10
+ // Hide harness-authoring scaffolding from the file explorer
11
+ "files.exclude": {
12
+ ".claude": true,
13
+ ".vscode": true,
14
+ ".agents": true,
15
+ ".codex": true,
16
+ "**/node_modules": true,
17
+ "**/apollo.service.json": true
9
18
  }
10
19
  }
@@ -7,5 +7,5 @@
7
7
  ".prettierrc.json",
8
8
  ".vscode/settings.json"
9
9
  ],
10
- "digest": "4d6027a2ef49"
10
+ "digest": "d2b1e92990e9"
11
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nurix/etna",
3
- "version": "0.19.23",
3
+ "version": "0.19.25",
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": {