@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.
- package/bin/etna.js +148 -67
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/.prettierignore +12 -0
- package/data/harnesses/code/.prettierrc.json +3 -0
- package/data/harnesses/code/.vscode/settings.json +10 -0
- package/data/harnesses/code/CLAUDE.md +5 -8
- package/data/harnesses/code/agents/change-review.md +13 -14
- package/data/harnesses/code/agents/harness-manager.md +14 -26
- package/data/harnesses/code/agents/maintenance.md +10 -18
- package/data/harnesses/code/harness.json +8 -3
- package/data/harnesses/code/hooks/hooks.json +2 -2
- package/data/harnesses/code/hooks/lib/{redact.js → redact.mjs} +2 -4
- package/data/harnesses/{code-fable/hooks/prompt-recall.js → code/hooks/prompt-recall.mjs} +5 -7
- package/data/harnesses/{code-fable/hooks/session-reflect.js → code/hooks/session-reflect.mjs} +9 -7
- package/data/harnesses/code/rules/general.md +23 -40
- package/data/harnesses/code/rules/nomenclature.md +0 -4
- package/data/harnesses/code/rules/typescript.md +19 -0
- package/data/harnesses/code/skills/boundary/SKILL.md +53 -239
- package/data/harnesses/code/skills/brainstorm/harvest.md +2 -2
- package/data/harnesses/code/skills/documentation/SKILL.md +2 -4
- package/data/harnesses/code/skills/documentation/references/templates.md +12 -89
- package/data/harnesses/code-fable/.prettierignore +12 -0
- package/data/harnesses/code-fable/.prettierrc.json +3 -0
- package/data/harnesses/code-fable/.vscode/settings.json +10 -0
- package/data/harnesses/code-fable/CLAUDE.md +5 -8
- package/data/harnesses/code-fable/agents/change-review.md +13 -14
- package/data/harnesses/code-fable/agents/harness-manager.md +14 -26
- package/data/harnesses/code-fable/agents/maintenance.md +10 -18
- package/data/harnesses/code-fable/harness.json +8 -3
- package/data/harnesses/code-fable/hooks/hooks.json +2 -2
- package/data/harnesses/code-fable/hooks/lib/{redact.js → redact.mjs} +2 -4
- package/data/harnesses/{code/hooks/prompt-recall.js → code-fable/hooks/prompt-recall.mjs} +5 -7
- package/data/harnesses/{code/hooks/session-reflect.js → code-fable/hooks/session-reflect.mjs} +9 -7
- package/data/harnesses/code-fable/rules/general.md +23 -40
- package/data/harnesses/code-fable/rules/nomenclature.md +0 -4
- package/data/harnesses/code-fable/rules/typescript.md +19 -0
- package/data/harnesses/code-fable/skills/boundary/SKILL.md +53 -239
- package/data/harnesses/code-fable/skills/brainstorm/harvest.md +2 -2
- package/data/harnesses/code-fable/skills/documentation/SKILL.md +2 -4
- package/data/harnesses/code-fable/skills/documentation/references/templates.md +12 -89
- package/data/harnesses/discover/.prettierignore +12 -0
- package/data/harnesses/discover/.prettierrc.json +3 -0
- package/data/harnesses/discover/.vscode/settings.json +10 -0
- package/data/harnesses/discover/harness.json +6 -1
- package/data/harnesses/discover/rules/evaluation.md +13 -1
- package/data/harnesses/discover/rules/ingestion.md +15 -0
- package/data/harnesses/discover/rules/tradecraft.md +13 -0
- package/data/harnesses/hr/.prettierignore +12 -0
- package/data/harnesses/hr/.prettierrc.json +3 -0
- package/data/harnesses/hr/.vscode/settings.json +10 -0
- package/data/harnesses/hr/harness.json +6 -1
- package/data/harnesses/kb-agi/.prettierignore +12 -0
- package/data/harnesses/kb-agi/.prettierrc.json +3 -0
- package/data/harnesses/kb-agi/.vscode/settings.json +10 -0
- package/data/harnesses/kb-agi/harness.json +6 -1
- 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.
|
|
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
|
-
- **
|
|
14
|
-
- **
|
|
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/
|
|
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
|
-
**
|
|
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
|
|
42
|
-
-
|
|
43
|
-
-
|
|
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/
|
|
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.
|
|
5
|
-
"epoch":
|
|
6
|
-
"
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
35
|
+
export { redact };
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
// plugins/code/hooks/prompt-recall.
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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;
|
package/data/harnesses/{code/hooks/session-reflect.js → code-fable/hooks/session-reflect.mjs}
RENAMED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
// plugins/code/hooks/session-reflect.
|
|
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
|
-
|
|
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
|
-
|
|
6
|
-
const
|
|
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
|
-
|
|
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:**
|
|
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
|
-
|
|
9
|
+
## Recommended Packages
|
|
19
10
|
|
|
20
|
-
No alternatives.
|
|
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
|
-
|
|
|
40
|
-
|
|
|
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
|
|
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
|
|
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
|
-
|
|
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`
|
|
@@ -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:
|
|
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
|
-
#
|
|
6
|
+
# Boundary Diagnostic
|
|
7
7
|
|
|
8
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
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
|
-
|
|
131
|
-
|
|
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
|
-
**
|
|
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
|
-
#
|
|
164
|
-
|
|
165
|
-
Plan: {path}
|
|
166
|
-
Boundaries: {N} files evaluated
|
|
167
|
-
Intersections: {N} ({critical}/{high}/{medium}/{low})
|
|
57
|
+
# Boundary check — {plan}
|
|
168
58
|
|
|
169
|
-
|
|
170
|
-
|
|
59
|
+
Touched files read: {N} · Boundaries identified: {N}
|
|
60
|
+
Violations: {N} ({critical}/{high}/{medium}/{low})
|
|
171
61
|
|
|
172
|
-
|
|
173
|
-
- Violation #1 → add step X to Implementation Steps / Done Criteria …
|
|
62
|
+
| # | Boundary | Planned change | Other side (file:line) | Severity |
|
|
174
63
|
|
|
175
|
-
##
|
|
176
|
-
##
|
|
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
|
-
- **
|
|
224
|
-
|
|
225
|
-
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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.
|