@nurix/etna 0.16.0 → 0.17.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.
Files changed (56) hide show
  1. package/bin/etna.js +148 -67
  2. package/data/harness-index.json +1 -1
  3. package/data/harnesses/code/.prettierignore +12 -0
  4. package/data/harnesses/code/.prettierrc.json +3 -0
  5. package/data/harnesses/code/.vscode/settings.json +10 -0
  6. package/data/harnesses/code/CLAUDE.md +5 -8
  7. package/data/harnesses/code/agents/change-review.md +13 -14
  8. package/data/harnesses/code/agents/harness-manager.md +14 -26
  9. package/data/harnesses/code/agents/maintenance.md +10 -18
  10. package/data/harnesses/code/harness.json +8 -3
  11. package/data/harnesses/code/hooks/hooks.json +2 -2
  12. package/data/harnesses/code/hooks/lib/{redact.js → redact.mjs} +2 -4
  13. package/data/harnesses/{code-fable/hooks/prompt-recall.js → code/hooks/prompt-recall.mjs} +5 -7
  14. package/data/harnesses/{code-fable/hooks/session-reflect.js → code/hooks/session-reflect.mjs} +9 -7
  15. package/data/harnesses/code/rules/general.md +23 -40
  16. package/data/harnesses/code/rules/nomenclature.md +0 -4
  17. package/data/harnesses/code/rules/typescript.md +19 -0
  18. package/data/harnesses/code/skills/boundary/SKILL.md +53 -239
  19. package/data/harnesses/code/skills/brainstorm/harvest.md +2 -2
  20. package/data/harnesses/code/skills/documentation/SKILL.md +2 -4
  21. package/data/harnesses/code/skills/documentation/references/templates.md +12 -89
  22. package/data/harnesses/code-fable/.prettierignore +12 -0
  23. package/data/harnesses/code-fable/.prettierrc.json +3 -0
  24. package/data/harnesses/code-fable/.vscode/settings.json +10 -0
  25. package/data/harnesses/code-fable/CLAUDE.md +5 -8
  26. package/data/harnesses/code-fable/agents/change-review.md +13 -14
  27. package/data/harnesses/code-fable/agents/harness-manager.md +14 -26
  28. package/data/harnesses/code-fable/agents/maintenance.md +10 -18
  29. package/data/harnesses/code-fable/harness.json +8 -3
  30. package/data/harnesses/code-fable/hooks/hooks.json +2 -2
  31. package/data/harnesses/code-fable/hooks/lib/{redact.js → redact.mjs} +2 -4
  32. package/data/harnesses/{code/hooks/prompt-recall.js → code-fable/hooks/prompt-recall.mjs} +5 -7
  33. package/data/harnesses/{code/hooks/session-reflect.js → code-fable/hooks/session-reflect.mjs} +9 -7
  34. package/data/harnesses/code-fable/rules/general.md +23 -40
  35. package/data/harnesses/code-fable/rules/nomenclature.md +0 -4
  36. package/data/harnesses/code-fable/rules/typescript.md +19 -0
  37. package/data/harnesses/code-fable/skills/boundary/SKILL.md +53 -239
  38. package/data/harnesses/code-fable/skills/brainstorm/harvest.md +2 -2
  39. package/data/harnesses/code-fable/skills/documentation/SKILL.md +2 -4
  40. package/data/harnesses/code-fable/skills/documentation/references/templates.md +12 -89
  41. package/data/harnesses/discover/.prettierignore +12 -0
  42. package/data/harnesses/discover/.prettierrc.json +3 -0
  43. package/data/harnesses/discover/.vscode/settings.json +10 -0
  44. package/data/harnesses/discover/harness.json +6 -1
  45. package/data/harnesses/discover/rules/evaluation.md +13 -1
  46. package/data/harnesses/discover/rules/ingestion.md +15 -0
  47. package/data/harnesses/discover/rules/tradecraft.md +13 -0
  48. package/data/harnesses/hr/.prettierignore +12 -0
  49. package/data/harnesses/hr/.prettierrc.json +3 -0
  50. package/data/harnesses/hr/.vscode/settings.json +10 -0
  51. package/data/harnesses/hr/harness.json +6 -1
  52. package/data/harnesses/kb-agi/.prettierignore +12 -0
  53. package/data/harnesses/kb-agi/.prettierrc.json +3 -0
  54. package/data/harnesses/kb-agi/.vscode/settings.json +10 -0
  55. package/data/harnesses/kb-agi/harness.json +6 -1
  56. package/package.json +4 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: maintenance
3
- description: Docs-vs-code coherence audit. Detects and fixes docs drifting from code — stale docs, broken ownership, routing table drift, unowned source paths. Run weekly or after major changes. Contract violations (code drifting from docs) are handled by the `boundary` skill (`init` mode); pre-implementation impact checks by its `check` mode.
3
+ description: Docs-vs-code coherence audit. Detects and fixes docs drifting from code — stale docs, broken ownership, routing table drift, unowned source paths. Run weekly or after major changes. Cross-cutting contract analysis is the `boundary` skill (`check` for plans, `scan` for diffs) — it derives contracts dynamically from source; there are no persisted boundary docs to audit.
4
4
  tools: Read, Glob, Grep, Edit, Write, Bash
5
5
  model: inherit
6
6
  ---
@@ -9,14 +9,14 @@ model: inherit
9
9
 
10
10
  You maintain codebase coherence by detecting drift between docs and code, and fixing it. Scope: stale feature docs, broken ownership, routing table drift, architecture drift, unowned source paths, backlog health.
11
11
 
12
- Out of scope (handled by the `boundary` skill):
13
- - **Code drifted from docs** (boundary violations in live code) → `boundary` skill, `init` mode
14
- - **Pre-implementation impact analysis** (checking a plan against the typed boundaries before coding) → `boundary` skill, `check` mode
12
+ Out of scope (handled by the `boundary` skill, which derives contracts dynamically from source):
13
+ - **Pre-implementation impact analysis** (checking a plan against cross-cutting contracts before coding) → `boundary` skill, `check` mode
14
+ - **Diff-scoped contract analysis** (what could a change break across module edges) → `boundary` skill, `scan` mode
15
15
 
16
16
  ## Before Starting
17
17
 
18
18
  1. If `./docs/memory/maintenance.md` exists, read it for last review state, ownership snapshots, high-churn paths, and known failure modes.
19
- 2. If `./docs/boundaries/` exists, read its files for context on the typed cross-module contracts the docs should reflect.
19
+ 2. If `./docs/memory/boundary.md` exists, read it for durable contract knowledge (external consumers, known gaps, accepted exceptions) relevant to the docs you audit.
20
20
  3. If `./docs/routing.md` exists, read it for the current file-pattern-to-doc mapping.
21
21
 
22
22
  ## Step 1: Build Ownership Map
@@ -25,7 +25,7 @@ Out of scope (handled by the `boundary` skill):
25
25
  2. Glob `docs/{features,modules/*/features}/**/*.md` and extract YAML frontmatter (`owns`, `touches`, `depends_on`)
26
26
  3. Build a map: glob pattern → owning doc
27
27
 
28
- **Boundary-first fallback.** If that glob yields **no `owns` frontmatter at all** (a real possibility — a boundary-first repo can carry thousands of LOC, a populated `docs/boundaries/`, and zero feature docs) **but `docs/boundaries/**` exists**, drive this pass's ownership/drift surface off the boundaries instead: each boundary's `Bounds`/`Shape` referents (the modules/paths it names) plus `docs/architecture/**` file-references become the **primary** ownership map for Steps 2–5. Feature-`owns` stays primary the moment any appears — this is a fallback, not a replacement. Don't re-mechanize the named-referent / path checks here; Step 2 already resolves boundary referents and Step 5 already greps architecture paths — this clause just promotes them to primary when feature-owns is empty. **Exempt the pre-code markers:** a referent a boundary marks `planned`/`point*`/`transcribe*` is intentionally not-yet-landed — never flag it absent (that is the greenfield contract, not drift); only a referent whose target *has* landed fires, via Step 2's planned-landed / transcribe-landed checks.
28
+ **Architecture-first fallback.** If that glob yields **no `owns` frontmatter at all** (a fresh-baseline repo can carry thousands of LOC and zero feature docs), drive this pass's ownership/drift surface off `docs/architecture/**` file-references instead — they become the **primary** ownership map for Steps 2–5. Feature-`owns` stays primary the moment any appears — this is a fallback, not a replacement.
29
29
 
30
30
  ## Step 2: Validate Ownership
31
31
 
@@ -38,17 +38,9 @@ For each code symbol or path a doc explicitly **names** (function/export/route/t
38
38
  - Flag unresolved named referents as drift, phrased as a question (e.g. `docs/x.md:NN names fn() — no static definition found; confirm it is not generated/re-exported before treating as stale`)
39
39
  - On codegen/reflection/DI/barrel stacks where static resolution is unreliable, skip — yielding nothing is correct
40
40
 
41
- Also resolve **`point`-mode boundary pointers** in `docs/boundaries/**/*.md` — a boundary whose `Bounds`/`Shape` carries a `file:line` pointer (e.g. `self-locating in lib/db.js:14`):
42
- - Verify the named path still exists; a rename (e.g. `lib/db.js`→`lib/store.js`) leaves a stale pointer that **distill② can never repair — boundary docs are write-protected** — so this pass is where it surfaces, as a question
43
- - If the boundary carries a literal **`planned`** marker (or "pointer lands at implementation") on a named target **and that target now resolves to real code**, the pointer has gone stale the other way — the code landed but the doc still says planned. Raise it as a question (e.g. `db-schema.md is marked planned but packages/api/src/db/schema.ts now exists — confirm and convert the pointer to file:line`). A barrel/re-export at the named path counts as landed.
44
- - Same skip rule on unreliable-resolution stacks
45
-
46
- Also handle **`transcribe`-mode boundaries** — a boundary whose `Shape` transcribes a contract (an event/wire/config shape copied from doctrine) **until** the owning module lands, typically with a checklist line like *"replace this transcription when X lands"*:
47
- - If **X now exists**, the transcribed shape can have silently diverged from the real one — and a transcription names fields, so the divergence is field-level, not path-level (e.g. an event transcribed as `{ entity, event_kind, … }` against a landed module that uses `eventKind`). Raise it as a question that the transcribed shape be reconciled against the real one — or handed to a proposed `boundary init` run for that file (e.g. `outbox-events.md transcribes event_kind but the landed outbox module uses eventKind — confirm the transcription should be reconciled to the real shape`). Question-phrased, never asserted; never rewrite the boundary doc yourself (write-protected).
48
-
49
- Also check **`env-var-contract` boundaries** — a boundary whose job is to assert the env contract (typically *"every variable the code reads appears in `.env.example`"*). The named-referent check above covers code symbols and paths but not env vars, which live in a separate canonical file:
50
- - Extract the `UPPER_SNAKE` tokens the boundary names in its `Shape` (allow a trailing `*` glob, e.g. `MASTER_KEK*` matching `MASTER_KEK`, `MASTER_KEK_ID`). Grep each against the declared canonical `.env`/`.env.example` (the boundary names which file is canonical).
51
- - Raise any absence as a question (e.g. `env-vars.md names MASTER_KEK* but no MASTER_KEK* appears in .env.example — confirm the contract still holds`). Same skip rule when the canonical file isn't declared or doesn't exist.
41
+ Also check the **env contract** when the repo declares a canonical `.env.example` (or equivalent). The named-referent check above covers code symbols and paths but not env vars, which live in a separate canonical file:
42
+ - Extract the `UPPER_SNAKE` env keys the code reads (`process.env.X` and the deploy scripts' generated env). Grep each against the canonical `.env.example`.
43
+ - Raise any absence as a question (e.g. `server reads MASTER_KEK but no MASTER_KEK appears in .env.example — confirm the contract still holds`). Skip when no canonical file exists.
52
44
 
53
45
  ## Step 3: Detect Doc Drift
54
46
 
@@ -108,7 +100,7 @@ If `qmd` is absent (`command -v qmd` finds nothing), **skip this whole step sile
108
100
 
109
101
  This catches **doc-area drift** — the subject the doc abstracts moving or vanishing from the indexed corpus. It does **not** catch a pure code rename that leaves the doc's prose unchanged: the anchor query semantically matches the doc's own text, so the result set doesn't shift. Code-symbol drift is Step 2's named-referent check, not this one — the two are complementary, not redundant.
110
102
 
111
- Glob `docs/{features,modules/*/features}/**/*.md`, `docs/boundaries/**/*.md`, `docs/architecture/**/*.md`, and `docs/operations/**/*.md` for docs carrying `anchors:` frontmatter. For each anchor query on such a doc, resolve it with `qmd query --format json -c <collection> "<query>"` — repeat `-c` per relevant `{repo}-*` collection (no comma lists; the collection set is what `qmd collection list` shows). If a query resolves to **nothing**, or to a result set that has **radically shifted** away from the area the doc describes, raise it as a drift finding **phrased as a question** — e.g. `docs/x.md anchors "<query>" — resolves to nothing; confirm the area moved before treating the doc as stale` — and ask the user. On **yes**, dispatch the remediation sub-agent (the same yes-branch as Step 6). On **decline or no action**, append it to `docs/history/backlog.md` at the finding's own P0–P3 severity per the **Declined realignment** branch in Step 6 — same scale, deduped, never a gate.
103
+ Glob `docs/{features,modules/*/features}/**/*.md`, `docs/architecture/**/*.md`, and `docs/operations/**/*.md` for docs carrying `anchors:` frontmatter. For each anchor query on such a doc, resolve it with `qmd query --format json -c <collection> "<query>"` — repeat `-c` per relevant `{repo}-*` collection (no comma lists; the collection set is what `qmd collection list` shows). If a query resolves to **nothing**, or to a result set that has **radically shifted** away from the area the doc describes, raise it as a drift finding **phrased as a question** — e.g. `docs/x.md anchors "<query>" — resolves to nothing; confirm the area moved before treating the doc as stale` — and ask the user. On **yes**, dispatch the remediation sub-agent (the same yes-branch as Step 6). On **decline or no action**, append it to `docs/history/backlog.md` at the finding's own P0–P3 severity per the **Declined realignment** branch in Step 6 — same scale, deduped, never a gate.
112
104
 
113
105
  ---
114
106
 
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "harness": "code-fable",
3
3
  "name": "code-fable",
4
- "version": "0.15.3",
5
- "epoch": 13,
6
- "digest": "30d4aa84ee01"
4
+ "version": "0.17.0",
5
+ "epoch": 14,
6
+ "files": [
7
+ ".prettierrc.json",
8
+ ".prettierignore",
9
+ ".vscode/settings.json"
10
+ ],
11
+ "digest": "c62c79b3aff1"
7
12
  }
@@ -6,7 +6,7 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-reflect.js\"",
9
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-reflect.mjs\"",
10
10
  "async": true
11
11
  }
12
12
  ]
@@ -18,7 +18,7 @@
18
18
  "hooks": [
19
19
  {
20
20
  "type": "command",
21
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/prompt-recall.js\""
21
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/prompt-recall.mjs\""
22
22
  }
23
23
  ]
24
24
  }
@@ -1,6 +1,4 @@
1
- // harness/code/hooks/lib/redact.js — shared privacy filter: redacts secret shapes before ledger append.
2
-
3
- "use strict";
1
+ // harness/code/hooks/lib/redact.mjs — shared privacy filter: redacts secret shapes before ledger append.
4
2
 
5
3
  // Patterns ordered most-specific first; each replaces a match with [redacted:{kind}].
6
4
  const RULES = [
@@ -34,4 +32,4 @@ function redact(text) {
34
32
  return out;
35
33
  }
36
34
 
37
- module.exports = { redact };
35
+ export { redact };
@@ -1,11 +1,9 @@
1
- // plugins/code/hooks/prompt-recall.js — UserPromptSubmit hook: surface fact-cards relevant to the prompt via qmd BM25; stdout is injected as context.
1
+ // plugins/code/hooks/prompt-recall.mjs — UserPromptSubmit hook: surface fact-cards relevant to the prompt via qmd BM25; stdout is injected as context.
2
2
 
3
- "use strict";
4
-
5
- const fs = require("fs");
6
- const os = require("os");
7
- const path = require("path");
8
- const { execFileSync } = require("child_process");
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { execFileSync } from "node:child_process";
9
7
 
10
8
  const MIN_PROMPT_CHARS = 24;
11
9
  const MIN_SCORE = 0.45;
@@ -1,12 +1,14 @@
1
- // plugins/code/hooks/session-reflect.js — SessionEnd hook: reflect the session into durable lessons (headless haiku pass) appended to the actor's memory inbox.
1
+ // plugins/code/hooks/session-reflect.mjs — SessionEnd hook: reflect the session into durable lessons (headless haiku pass) appended to the actor's memory inbox.
2
2
 
3
- "use strict";
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { execFileSync, spawn } from "node:child_process";
7
+ import { fileURLToPath } from "node:url";
8
+ import { redact } from "./lib/redact.mjs";
4
9
 
5
- const fs = require("fs");
6
- const os = require("os");
7
- const path = require("path");
8
- const { execFileSync, spawn } = require("child_process");
9
- const { redact } = require("./lib/redact.js");
10
+ // ESM has no __filename; derive it from this module's URL for the detached worker re-spawn.
11
+ const __filename = fileURLToPath(import.meta.url);
10
12
 
11
13
  // A session below all three thresholds carries no lesson worth a model call.
12
14
  const MIN_TURNS = 8;
@@ -1,23 +1,14 @@
1
- ---
2
- applyTo: "**"
3
- ---
1
+ # General Standards
4
2
 
5
- # Technology Choices & Standards
6
-
7
- Opinionated technology stack and implementation standards.
8
-
9
- ## Environment & Versions
10
-
11
- ### Runtime Versions
3
+ ## Runtime Versions
12
4
 
13
5
  - **Node.js:** 22 LTS (latest)
14
- - **Package manager:** project-determined — detect from lockfile (`pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, `package-lock.json` → npm). Use the matching command in scripts; don't hardcode a specific manager.
15
- - **PostgreSQL:** 17
16
- - **Redis:** 7.x (latest)
6
+ - **Package manager:** detect from lockfile (`pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, `package-lock.json` → npm). Don't hardcode one.
7
+ - **PostgreSQL:** 17 · **Redis:** 7.x (latest)
17
8
 
18
- ### Recommended Packages
9
+ ## Recommended Packages
19
10
 
20
- No alternatives. No explanations for obvious choices. Just use these.
11
+ No alternatives. Just use these.
21
12
 
22
13
  | Package | Version | Use | Don't use |
23
14
  | ------------------------ | --------- | ---------------------- | ----------------------- |
@@ -36,8 +27,8 @@ No alternatives. No explanations for obvious choices. Just use these.
36
27
  | react-window | ^2.2.3 | List virtualization | react-virtualized |
37
28
  | nanoid | ^5.1.6 | ID generation | uuid |
38
29
  | express | ^5.2.1 | HTTP server | fastify, koa |
39
- | prisma | ^7.2.0 | Database ORM | sequelize, typeorm |
40
- | @prisma/client | ^7.2.0 | Database queries | - |
30
+ | drizzle-orm | latest | Database ORM | prisma, sequelize, typeorm |
31
+ | drizzle-kit | latest | Schema tooling | - |
41
32
  | zod | ^4.2.1 | Schema validation | yup, joi |
42
33
  | jose | ^6.1.3 | JWT handling | jsonwebtoken |
43
34
  | microdiff | ^1.5.0 | JSON object diffing | deep-diff, just-diff |
@@ -49,15 +40,24 @@ No alternatives. No explanations for obvious choices. Just use these.
49
40
  | cmdk | ^1.1.1 | Command palette | kbar, ninja-keys |
50
41
  | @monaco-editor/react | ^4.7.0 | Code editor | codemirror, ace |
51
42
  | localforage | ^1.10.0 | Client-side storage | idb, dexie |
52
- | socket.io-client | ^4.7.0 | WebSocket client | native WebSocket, ws |
53
43
  | react-markdown | ^10.1.0 | Markdown rendering | marked, markdown-it |
54
44
  | fast-deep-equal | ^3.1.3 | Deep equality checks | lodash.isEqual |
55
45
  | @ngneat/falso | latest | Fake data generation | @faker-js/faker, chance |
56
46
  | pino | ^10.1.0 | Logging | winston |
57
47
 
58
- ---
48
+ ## Frontend
49
+
50
+ Tailwind v4 + shadcn-based UI; conventions below.
51
+
52
+ - **@nurix/components** — the UI component library (linked, pre-built); import by subpath, e.g. `@nurix/components/button`.
53
+ - No components.json / shadcn CLI scaffold (.tsx, not .jsx) — consume the prebuilt library; never `npx shadcn add`.
54
+ - 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` (linked @nurix/* ship nested react — keep one hook dispatcher).
55
+ - Tailwind v4, CSS-only (no tailwind.config.js, no PostCSS): styling runs through the `@tailwindcss/vite` plugin.
56
+ - CSS entry `globals.css`, imports in order — `@import "./personality.css"` (token source) → `@import "tailwindcss"` → the library's `styles.css` — then `@theme inline {…}` mapping vars to tokens.
57
+ - Tokens: CSS vars in `:root, :root[data-theme="light"]` + the archetype selector `[data-style="…"]` (personality.css), consumed as semantic Tailwind tokens (bg-card, text-foreground). Honor the archetype's radius/theme decisions (e.g. re-assert `--radius-*` to 0 for a flat archetype; add `@custom-variant dark` only if the project supports dark mode). The library bundles class-variance-authority, clsx, tailwind-merge.
58
+ - Plus general styling rules (spacing in 2/4/6/8, object-cover, truncate, motion, color tokens — never hardcoded hex in components).
59
59
 
60
- ## Express 5.x Setup
60
+ ## Express 5.x
61
61
 
62
62
  ```javascript
63
63
  app.use(cors({ origin: process.env.FRONTEND_URL }));
@@ -66,31 +66,14 @@ app.use(express.json({ limit: "10mb" }));
66
66
  app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
67
67
  ```
68
68
 
69
- - Middleware order: body parsers, custom middleware, routes, error handlers
70
- - Use Express Router for modular routes
71
- - Centralized error handler as last middleware
72
- - Use middleware for auth
73
-
74
- ---
69
+ - Middleware order: body parsers → custom middleware → routes → error handlers (centralized, last). Auth via middleware. Use Express Router for modular routes.
75
70
 
76
71
  ## API Response Format
77
72
 
78
73
  ```json
79
- {
80
- "status": 1,
81
- "message": "Message sent successfully",
82
- "data": { "delivered": [{ "user@example.com": 2 }] }
83
- }
74
+ { "status": 1, "message": "Message sent successfully", "data": { "delivered": [{ "user@example.com": 2 }] } }
84
75
  ```
85
76
 
86
- **Error:**
87
-
88
- ```json
89
- {
90
- "status": 0,
91
- "message": "Error description",
92
- "data": null
93
- }
94
- ```
77
+ Error: `{ "status": 0, "message": "Error description", "data": null }`
95
78
 
96
79
  HTTP codes: `200`, `201` (created), `400`, `401` (not authenticated), `403` (not authorized), `404`, `500`
@@ -1,7 +1,3 @@
1
- ---
2
- applyTo: "**"
3
- ---
4
-
5
1
  # Nomenclature Guidelines
6
2
 
7
3
  ## Naming Guidelines
@@ -0,0 +1,19 @@
1
+ # TypeScript
2
+
3
+ TypeScript conventions. Compiles under `strict: true` and `noUncheckedIndexedAccess: true`.
4
+
5
+ ## Type Safety
6
+
7
+ - Never `any` to silence the checker. Unknown value → type `unknown`, then narrow (type guard / `zod` parse).
8
+ - Never weaken strictness. No `// @ts-nocheck`; `// @ts-expect-error` only with a same-line reason.
9
+ - Infer locals & return types; annotate at boundaries (exports, props, contracts).
10
+
11
+ ## Generics
12
+
13
+ Shared libraries (`@nurix/components`, etc.) are generic on purpose — that's what types `cell(row)`, checks `slug` against `keyof TData`, and ties `data`/`columns` together. Keep the safety; never widen to `any` to dodge it. State the row type **once per file**, then let inference carry it:
14
+
15
+ - **Bind a repeated factory once** (preferred): `const col = defineColumn<BriefRow>;` → `col({...})`. No `<T>` per call; `row` stays typed. Canonical: `modules/briefs/BriefsListPage.tsx`.
16
+ - **Infer JSX type args from props:** `<Table data={rows} columns={columns} />`, not `<Table<BriefRow> …>`.
17
+ - **Anchor with a collection annotation** when nothing else can: `const columns: ColumnDefinition<BriefRow>[] = [...]`.
18
+
19
+ Don't repeat `fn<Type>(…)` / `<Comp<Type>>` at every call site — cosmetic only (same safety, same emitted JS).
@@ -1,262 +1,76 @@
1
1
  ---
2
2
  name: boundary
3
- description: The single home for a repo's typed application boundaries — the cross-cutting contracts (db-schema, wire envelope, store shape, auth, tenancy, event schemas, edge bindings, …) where editing one module silently breaks another. One skill, four modes — `generate` (baseline all boundaries from the detected stack + IA), `create` (add/update one boundary), `check` (pre-implementation: does this plan touch a boundary? severity-ranked impact), `init` (reconcile: ensure files exist, scan live code for violations + drift, backfill new catalog types). All four read one shared knowledge root, `references/boundary-taxonomy.md`, so they can never drift apart. Triggers on "application boundary", "baseline boundaries", "map boundaries", "generate boundaries", "create a boundary", "introspect", "check this plan", "impact analysis", "what could break", "scan for boundary violations", "boundary drift", "/boundary".
3
+ description: Boundary diagnostic for implementation plans. Given a plan file, reads the source files the plan touches, identifies the application boundaries they participate in — the cross-cutting contracts (db schema, wire envelope, auth, events, env/config, …) where editing one side silently breaks the other — and reports whether the planned changes would violate any of them, severity-ranked, with suggested plan amendments. Read-only; the report lands in chat. Can also run against a diff or named code area instead of a plan. Triggers on "boundary check", "check this plan", "what could break", "impact analysis", "/boundary".
4
4
  ---
5
5
 
6
- # Application Boundary Skill
6
+ # Boundary Diagnostic
7
7
 
8
- The one place a repo's **typed application boundaries** are produced, checked, and
9
- maintained. A boundary is a horizontal cross-cutting contract — an *edge* between
10
- modules, layers, services, or deploy units — where editing one side silently
11
- breaks another. They live as one file per boundary in `docs/boundaries/`.
8
+ You diagnose whether a plan's changes would break an application boundary.
12
9
 
13
- This skill merges what used to be three components — `boundary-mapper` (produce),
14
- `introspect` (pre-impl check), `contract-monitor` (post-impl scan) — into one
15
- skill with four modes. The reason they're one skill: **all four share a single
16
- knowledge root** — [`references/boundary-taxonomy.md`](references/boundary-taxonomy.md),
17
- the exhaustive catalog of *what an application boundary is* and *which kinds
18
- exist* (≈170 types across persistence, wire/API, frontend, auth/tenancy,
19
- async/messaging, config/build, runtime/infra). Three separate components meant
20
- three copies of that mental model that drifted apart. One skill, one catalog,
21
- zero drift.
10
+ A boundary is a contract between two pieces of code (or code and infra) that
11
+ share an assumption with **no compile-time link** between them: the column a raw
12
+ SQL string names, the JSON envelope a client unwraps, the event string a
13
+ dispatcher parses, the env var a deploy script writes. Changing one side ships
14
+ green and breaks the other silently. Your job is to find those edges in the
15
+ plan's path *before* execution.
22
16
 
23
- > **Read [`references/boundary-taxonomy.md`](references/boundary-taxonomy.md)
24
- > first, in every mode.** It carries the boundary-file anatomy, the
25
- > `point`-vs-`transcribe` doc-mode, the technology/domain split, the per-type
26
- > severities, and the safe-change checklists. This SKILL.md only orchestrates the
27
- > four verbs over that knowledge.
17
+ [`references/boundary-taxonomy.md`](references/boundary-taxonomy.md) is the
18
+ catalog of boundary types (~170 across persistence, wire/API, frontend,
19
+ auth/tenancy, async/messaging, config/build, runtime/infra), with per-type
20
+ severities and safe-change checklists. Read it first — it tells you what to
21
+ look for; the code tells you what exists.
28
22
 
29
- ---
30
-
31
- ## Picking a mode
32
-
33
- | You want to… | Mode | Old component |
34
- |---|---|---|
35
- | Baseline a repo — generate every boundary that exists from the stack + IA | **`generate`** | boundary-mapper (baseline) |
36
- | Add or update **one** boundary file | **`create`** | boundary-mapper (create/update) |
37
- | Before coding: does this **plan** touch a boundary? Severity-ranked impact | **`check`** | introspect |
38
- | Reconcile: do files exist? scan **live code** for violations + drift; backfill new catalog types | **`init`** | contract-monitor + drift + catalog-sync |
39
-
40
- If the user's intent is ambiguous, ask which mode. `generate` and `create` are the
41
- same writer at different scope (`generate` = `create` over every detected
42
- boundary).
43
-
44
- ## Execution model — pure skill
45
-
46
- This skill runs **in the main session** — no standing sub-agents. `check` and
47
- `create` are light and always run inline. `generate` and `init` sweep the
48
- codebase; run that sweep inline too. **Only** for an unusually large repo (where
49
- an inline full-codebase sweep would blow the context budget) may you dispatch a
50
- **one-shot scan worker** sub-agent that reads this same taxonomy reference and
51
- returns structured findings — the knowledge still lives in exactly one place.
52
- Default to inline; reach for a worker only when the repo's size forces it.
53
-
54
- ## Before any mode
55
-
56
- 1. Read [`references/boundary-taxonomy.md`](references/boundary-taxonomy.md) — the
57
- boundary types, doc-modes, default severities, and safe-change checklists for
58
- the stack you're about to touch.
59
- 2. If `docs/memory/boundary.md` exists, read it for project-specific
60
- boundary exceptions, known violations the user chose not to fix, where the IA
61
- model actually lives, and stack quirks that change a doc-mode.
62
- 3. If `docs/routing.md` exists, read it for module-ownership context.
63
-
64
- ---
65
-
66
- ## Mode: `generate` (baseline)
67
-
68
- No `docs/boundaries/` yet (or a from-scratch re-baseline). Detect the stack + IA,
69
- instantiate one file per boundary that actually exists.
70
-
71
- 1. **Detect the stack** — read `package.json` (deps), the lockfile, and skim the
72
- source tree. Map detected technologies to their boundary types via the
73
- taxonomy. (Detect the package manager from the lockfile; don't assume.)
74
- 2. **Detect the information architecture** — read `docs/architecture/`
75
- (domain-model, ADRs), doctrine in any `docs/modules/*/pillars/`, and the
76
- entity/contract definitions (a `@org/contracts` package, `schema.ts`,
77
- `models.py`). The IA entity model is the flagship `domain` boundary
78
- (`information-architecture`).
79
- 3. **For each boundary the repo exhibits**, write one file to `docs/boundaries/`
80
- using the boundary-file anatomy. Decide `point` vs `transcribe` per the
81
- taxonomy: `point` boundaries get a `file:line` pointer + invariants +
82
- checklist (never a transcribed copy — it rots); `transcribe` boundaries (the
83
- composed store tree, the response envelope, the IA topology, tenancy) get the
84
- contract canonically in the file.
85
- - **Greenfield branch — contract decided, no code yet.** If the repo is
86
- pre-code (ADRs/domain-model decide the contract but the source module isn't
87
- scaffolded), a `point` boundary has no `file:line` to mirror. Don't skip it
88
- and don't fabricate a pointer — per the taxonomy's *Greenfield / pre-code
89
- boundaries*, emit it as **conventions-with-planned-pointer**: record the
90
- decided conventions inline, carry a `planned <path>` pointer, mark it `point*`
91
- (a pre-code `transcribe` source is `transcribe*`), and make the last
92
- safe-change item "replace the planned pointer with the real `file:line` once
93
- code lands." Link any item that hinges on an unresolved decision to its
94
- open-question/ADR.
95
- 4. **Set severity** from the taxonomy default, adjusted by the concrete repo +
96
- project memory. Severity is load-bearing — it drives `check` review depth and
97
- `init` scan priority.
98
- 5. **Name the bounding code units** (paths / package names) in every `Bounds`
99
- line. The distinct vertices named across all `Bounds` lines are the
100
- **module-discovery feed** `harness-manager` reads to propose modules — name
101
- them precisely.
102
- 6. **Write `docs/boundaries/README.md`** as the index per the taxonomy's index
103
- spec: a table with a **Doc-mode column**
104
- (`| Boundary | Type | Severity | Doc-mode | Bounds (summary) |`), then the
105
- standing blocks — **Active technology packs**, **Folds applied** (which catalog
106
- types collapsed into which file — surface the per-entry fold-notes as a ledger),
107
- and **Skipped** (types omitted + one-line reason each). Add the `point*`/`transcribe*`
108
- footnote if any boundary is pre-code.
109
- 7. **Don't invent boundaries the repo doesn't have.** Only instantiate types the
110
- detected stack/IA actually exhibits; `log()` what you skipped and why.
111
-
112
- Write only under `docs/boundaries/` and `docs/memory/boundary.md`.
113
- Read-only on production code.
23
+ ## Run
114
24
 
115
- ## Mode: `create` (targeted)
25
+ **Input:** a plan path (given, or the newest in `docs/history/plans/`) — or a
26
+ diff / named code area for non-plan work. Everything below is identical either
27
+ way; a diff's hunks stand in for the plan's steps.
116
28
 
117
- Add or update a single boundary file (e.g. a new contract surfaced by `check`, or
118
- a boundary the author asks for by name).
29
+ 1. **Build the touch list.** Parse the plan's Proposed Changes: files to be
30
+ created or modified, entities/state added or changed, API changes, anything
31
+ the author flags as a cross-module contract. Note missing subsections.
119
32
 
120
- 1. Identify the boundary type from the taxonomy; confirm the repo exhibits it.
121
- 2. Write/update the one file with the full anatomy (Bounds, Shape, Invariants,
122
- Blast radius, Safe-change checklist), `point` or `transcribe` per the taxonomy.
123
- 3. Update `docs/boundaries/README.md` (add/refresh the index row).
124
- 4. For an **update** that fixes drift (the `maintenance` agent flags a boundary
125
- file drifted from its source): re-read the source, refresh the `Shape` pointer
126
- or transcription, leave invariants/checklist intact unless they changed.
33
+ 2. **Identify the boundaries in the touched code.** Read each touched file (and
34
+ its immediate imports). Against the taxonomy, list the contracts this code
35
+ participates in — the schema it queries, the envelope it emits, the events
36
+ it publishes or parses, the auth checks it sits behind, the env it reads.
127
37
 
128
- ## Mode: `check` (pre-implementation)
38
+ 3. **Trace the other side.** A boundary has two sides; the danger lives on the
39
+ one the plan *doesn't* mention. For each contract, grep the shared name or
40
+ shape across the repo — the column name in other SQL strings, the response
41
+ field in client unwrappers, the event kind in subscribers, the route in
42
+ fetch calls and scripts, the env key in deploy tooling. Record consumers as
43
+ `file:line`.
129
44
 
130
- Read-only. Intersect a plan's proposed changes with the documented boundaries and
131
- surface cascading risk *before* coding. (This is the old `introspect`.)
45
+ 4. **Judge each intersection.** Does the planned change alter a shape a traced
46
+ consumer depends on, break an invariant the code establishes (append-only
47
+ vocabularies, idempotency keys, fail-closed checks, ordering guarantees), or
48
+ add a new cross-module edge? Severity:
49
+ - **CRITICAL** — can corrupt persisted data, or open an auth/money path.
50
+ - **HIGH** — silent cross-module breakage that would ship green.
51
+ - **MEDIUM** — drift-prone coupling (string-matched, duplicated constants).
52
+ - **LOW** — smell. Loud, immediately-visible breakage caps at HIGH.
132
53
 
133
- **Inputs.** Plan path (user-provided; else newest in `docs/history/plans/`). Every
134
- file in `docs/boundaries/` is mandatory input — if the folder is missing/empty,
135
- stop and tell the user to run `generate` first. Source files read on demand to
136
- verify — never guess from plan text alone.
137
-
138
- 1. **Load.** Resolve + read the plan; confirm it has a `Status:` (warn if
139
- `Complete`/`Abandoned`). Read every boundary file; parse the
140
- `[BOUNDARY: type · severity]` header + Bounds / Shape / Invariants / Blast
141
- radius / Safe-change checklist. Severity sets review depth.
142
- 2. **Parse proposed changes.** The plan template's **Proposed Changes**
143
- subsections are load-bearing: New Files / Modified Files (check against the
144
- modules each boundary **Bounds** and any `Shape` `file:line`) · New Entities /
145
- State (vs `information-architecture` + `db-schema`) · API Changes (vs
146
- `wire-response-envelope` / `rest-endpoint-shape` / `graphql-schema`) ·
147
- Cross-Module Contracts Affected (the author's own admission — start there,
148
- then verify). Note any missing subsection.
149
- 3. **Analyze each boundary × change pair** for: **Bounds overlap** (touches a
150
- bounded module or a `Shape` pointer), **Invariant overlap** (introduces a
151
- shape the invariants prohibit — raw literal where a shared constant is
152
- required, entity type not in the canonical array, persisted field without
153
- fallback), **Shape overlap** (modifies the canonical Shape). Read the actual
154
- source to verify any plausible intersection.
155
- 4. **Assign severity.** Anchor to the boundary's own severity (the floor),
156
- adjust by the concrete change. Critical = silent data loss / corrupted
157
- persistence / broken cross-entity refs; High = stale state / broken lookups /
158
- schema selectors without fallbacks; Medium = likely-to-drift string-coupling;
159
- Low = code smell.
160
- 5. **Report** (in chat — never written to disk):
54
+ 5. **Report in chat:**
161
55
 
162
56
  ```
163
- # Application Boundary — check — {plan filename}
164
-
165
- Plan: {path}
166
- Boundaries: {N} files evaluated
167
- Intersections: {N} ({critical}/{high}/{medium}/{low})
57
+ # Boundary check — {plan}
168
58
 
169
- ## Violations
170
- | # | Boundary | Risk | Severity | Plan step |
59
+ Touched files read: {N} · Boundaries identified: {N}
60
+ Violations: {N} ({critical}/{high}/{medium}/{low})
171
61
 
172
- ## Suggested Plan Additions
173
- - Violation #1 → add step X to Implementation Steps / Done Criteria …
62
+ | # | Boundary | Planned change | Other side (file:line) | Severity |
174
63
 
175
- ## Missing Subsections
176
- ## New Cross-Module Contracts Detected
177
- {describe the proposed boundary (type, Bounds, Shape, suggested severity) and
178
- recommend running `create` to formalize it — do not write the file from check}
64
+ ## Suggested plan amendments
65
+ ## Missing plan subsections
179
66
  ```
180
67
 
181
- The severity-ranked table is the load-bearing output. Suggestions are advisory —
182
- the user folds them back into the plan; **`check` never edits the plan or code.**
183
-
184
- ## Mode: `init` (reconcile — ensure, scan, backfill)
185
-
186
- Ensure boundaries are initialized and current. Three jobs in one pass:
187
-
188
- 1. **Ensure exist.** If `docs/boundaries/` is missing/empty → this is really a
189
- baseline; run `generate` instead and stop.
190
- 2. **Scan live code for violations** (the old `contract-monitor`). For each
191
- boundary file, parse its header + invariants, then scan the codebase for
192
- violations of *that* boundary. Categories: **unregistered entity types**
193
- (strings used as entity-type ids not in the canonical array) · **string-coupled
194
- constants** (raw literals where a shared constant import is required) ·
195
- **cross-entity references without propagation** (storing entity A's
196
- slug/name/id by value in B with no documented propagation) · **schema
197
- mismatches** (selectors hitting persisted fields without a fallback default) ·
198
- **undocumented endpoints** (routes with no feature-doc / `api` boundary entry).
199
- Verify by reading actual source — never guess.
200
- 3. **Detect drift + backfill the catalog.** (a) Flag any boundary file whose
201
- `Shape` has drifted from its source (refresh it via `create`/update). (b)
202
- Compare the taxonomy's boundary *types* against what the repo now exhibits —
203
- if the repo gained a technology (or the catalog gained a type) that the repo
204
- exhibits but `docs/boundaries/` lacks, surface it and offer to `generate` that
205
- one boundary.
206
-
207
- **Act on violations.** Mechanical fix (missing constant import, missing fallback,
208
- missing canonical-array entry) → fix directly with an Edit. Design-decision fix
209
- (new entity-type registration, new propagation mechanism, schema migration) →
210
- append to `docs/history/backlog.md` as P0/P1 with the specific boundary + bounded
211
- modules. Pre-existing exception (in `docs/memory/boundary.md`) → skip
212
- silently.
213
-
214
- **Report** a table `| # | Boundary | Violation | Location | Action taken |`, then
215
- update `docs/memory/boundary.md` with scan date, per-boundary
216
- violation counts, items fixed / backlogged, new exceptions, and any
217
- newly-exhibited boundary types backfilled.
218
-
219
- ---
220
-
221
68
  ## Rules
222
69
 
223
- - **One knowledge root.** Every mode reads `references/boundary-taxonomy.md`.
224
- Extend the catalog there, never inline a divergent copy in a mode.
225
- - **`check` is read-only** — never edits the plan, source, boundaries, or memory.
226
- The other modes write only under `docs/boundaries/`,
227
- `docs/memory/boundary.md`, and (for `init` mechanical fixes) the
228
- specific violating source line. Never touch frozen records
229
- (`docs/history/plans/`, `docs/history/changelog/`) or feature docs.
230
- - **Verify against source.** Never report a violation or intersection from text
231
- alone — read the files the plan/boundary names.
232
- - **One file per boundary.** Don't bundle unrelated boundaries.
233
- - **Severity is load-bearing.** It feeds `check` review depth + `init` priority.
234
- Default from the taxonomy; adjust from the repo + memory.
235
- - **Name the bounding code units** in every `Bounds` line — they're the
236
- module-discovery feed `harness-manager` reads. A workspace is *not* a module.
237
- - **Don't invent boundaries the repo doesn't have.** Only instantiate exhibited
238
- types; `log()` skips. **A clean report is valid** — empty violations/intersections
239
- means say so, don't pad.
240
- - **Docs never outrank code.** For `point` boundaries, mirror with `file:line`;
241
- don't fork the shape. `file:line` stays primary for point-mode boundaries; an
242
- `anchors:` query (see the documentation skill's frontmatter reference) MAY
243
- supplement it for refactor-resilience — optional and additive, it re-resolves
244
- the shape when the line moves, never replaces the pointer.
245
- - **Be specific** — file paths, line numbers, boundary names, the exact violating
246
- string or code shape.
247
-
248
- ## Strategy memory
249
-
250
- After a run you may append to `docs/memory/boundary.md` (git-tracked):
251
- project-specific boundary exceptions, where the IA model actually lives in this
252
- repo, stack quirks that change a doc-mode, recurring drift/violation zones,
253
- historical violation counts. Never user preferences (those are `~/.claude/`);
254
- never a transcription of a boundary's content (that's the boundary file). Date
255
- each entry `## YYYY-MM-DD — {topic}`.
256
-
257
- ## Output locations
258
-
259
- - `generate`/`create`/`init` write boundary files to `docs/boundaries/` + the
260
- index `docs/boundaries/README.md`; memory to `docs/memory/boundary.md`.
261
- - `check` returns its report in chat — never written to disk; the user folds
262
- insights back into the plan.
70
+ - **Read-only.** You never edit the plan, the code, or any doc. The user folds
71
+ your amendments into the plan.
72
+ - **Verify in source.** Never flag from plan text alone — read both sides of
73
+ every boundary you report, and cite both as `file:line`.
74
+ - **No consumer, no finding.** An intersection with no traced other side isn't
75
+ a violation; at most note it as a new edge the plan introduces.
76
+ - **A clean report is valid.** Zero violations means say so — don't pad.