@nurix/etna 0.15.7 → 0.16.0
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 +64 -13
- package/data/harness-index.json +7 -5
- package/data/harnesses/code/CLAUDE.md +3 -7
- package/data/harnesses/code/agents/harness-manager.md +4 -4
- package/data/harnesses/code/agents/maintenance.md +1 -1
- package/data/harnesses/code/harness.json +4 -3
- package/data/harnesses/code/hooks/hooks.json +3 -17
- package/data/harnesses/code/hooks/prompt-recall.js +157 -0
- package/data/harnesses/code/hooks/session-reflect.js +283 -0
- package/data/harnesses/code/skills/distiller/SKILL.md +62 -60
- package/data/harnesses/code/skills/distiller/references/docs-mode.md +21 -21
- package/data/harnesses/code/skills/distiller/references/method.md +41 -41
- package/data/harnesses/code/skills/documentation/references/templates.md +20 -27
- package/data/harnesses/code-fable/CLAUDE.md +138 -0
- package/data/harnesses/code-fable/agents/brainstorm.md +35 -0
- package/data/harnesses/code-fable/agents/change-review.md +249 -0
- package/data/harnesses/code-fable/agents/harness-manager.md +193 -0
- package/data/harnesses/code-fable/agents/maintenance.md +123 -0
- package/data/harnesses/code-fable/agents/performance-review.md +218 -0
- package/data/harnesses/code-fable/agents/spec-mode.md +56 -0
- package/data/harnesses/code-fable/harness.json +7 -0
- package/data/harnesses/code-fable/hooks/hooks.json +27 -0
- package/data/harnesses/code-fable/hooks/lib/redact.js +37 -0
- package/data/harnesses/code-fable/hooks/prompt-recall.js +157 -0
- package/data/harnesses/code-fable/hooks/session-reflect.js +283 -0
- package/data/harnesses/code-fable/rules/general.md +96 -0
- package/data/harnesses/code-fable/rules/nomenclature.md +34 -0
- package/data/harnesses/code-fable/skills/boundary/SKILL.md +262 -0
- package/data/harnesses/code-fable/skills/boundary/references/boundary-taxonomy.md +734 -0
- package/data/harnesses/code-fable/skills/brainstorm/README.md +136 -0
- package/data/harnesses/code-fable/skills/brainstorm/SKILL.md +135 -0
- package/data/harnesses/code-fable/skills/brainstorm/harvest.md +26 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/backlog.md +39 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/build.md +37 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/decisions.md +29 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/doctrine.md +31 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/domain-model.md +25 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/glossary.md +19 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/milestones.md +43 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/non-goals.md +21 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/plan-doc.md +61 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/questions.md +35 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/references.md +28 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/rejected.md +29 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/state.md +8 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/tech.md +23 -0
- package/data/harnesses/code-fable/skills/brainstorm/templates/thesis.md +21 -0
- package/data/harnesses/code-fable/skills/distiller/SKILL.md +219 -0
- package/data/harnesses/code-fable/skills/distiller/references/docs-mode.md +526 -0
- package/data/harnesses/code-fable/skills/distiller/references/method.md +166 -0
- package/data/harnesses/code-fable/skills/documentation/SKILL.md +127 -0
- package/data/harnesses/code-fable/skills/documentation/references/examples.md +193 -0
- package/data/harnesses/code-fable/skills/documentation/references/style-guide.md +288 -0
- package/data/harnesses/code-fable/skills/documentation/references/templates.md +942 -0
- package/data/harnesses/code-fable/skills/workflow-mapper/SKILL.md +430 -0
- package/data/harnesses/code-fable/skills/workflow-mapper/references/patterns.md +214 -0
- package/data/harnesses/{re → discover}/CLAUDE.md +48 -33
- package/data/harnesses/{re → discover}/agents/api.md +3 -3
- package/data/harnesses/{re → discover}/agents/codebase.md +3 -3
- package/data/harnesses/{re → discover}/agents/community.md +3 -3
- package/data/harnesses/{re → discover}/agents/deployed-client-bundle.md +5 -5
- package/data/harnesses/{re → discover}/agents/distribution-artifacts.md +4 -4
- package/data/harnesses/{re → discover}/agents/docs.md +3 -3
- package/data/harnesses/{re → discover}/agents/infra-backend-fingerprint.md +2 -2
- package/data/harnesses/{re → discover}/agents/packages.md +3 -3
- package/data/harnesses/{re → discover}/agents/session.md +19 -11
- package/data/harnesses/{re → discover}/agents/website.md +3 -3
- package/data/harnesses/{re → discover}/agents/wire-capture.md +9 -7
- package/data/harnesses/discover/harness.json +7 -0
- package/data/harnesses/{re/rules/re-discovery.md → discover/rules/discovery.md} +54 -17
- package/data/harnesses/{re/rules/re-evaluation.md → discover/rules/evaluation.md} +12 -1
- package/data/harnesses/{re/rules/re-ingestion.md → discover/rules/ingestion.md} +192 -23
- package/data/harnesses/discover/rules/self-correction.md +177 -0
- package/data/harnesses/discover/rules/tradecraft.md +532 -0
- package/data/harnesses/hr/CLAUDE.md +91 -0
- package/data/harnesses/hr/harness.json +7 -0
- package/data/harnesses/hr/skills/interview-eval/SKILL.md +118 -0
- package/data/harnesses/hr/skills/interview-eval/TEMPLATE.md +124 -0
- package/data/harnesses/hr/skills/interview-eval/extract_session.py +418 -0
- package/data/harnesses/hr/skills/interview-eval/rubric.md +219 -0
- package/data/harnesses/kb-agi/harness.json +1 -0
- package/package.json +1 -1
- package/data/harnesses/code/hooks/post-commit.js +0 -136
- package/data/harnesses/code/hooks/post-tool-failure.js +0 -136
- package/data/harnesses/code/hooks/session-stop.js +0 -207
- package/data/harnesses/re/harness.json +0 -6
|
@@ -0,0 +1,283 @@
|
|
|
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.
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
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
|
+
|
|
11
|
+
// A session below all three thresholds carries no lesson worth a model call.
|
|
12
|
+
const MIN_TURNS = 8;
|
|
13
|
+
const MAX_MESSAGE_CHARS = 1500;
|
|
14
|
+
const MAX_DIGEST_CHARS = 80000;
|
|
15
|
+
const MAX_LESSONS = 6;
|
|
16
|
+
|
|
17
|
+
// Read all of stdin synchronously; returns "" if nothing is piped.
|
|
18
|
+
function readStdin() {
|
|
19
|
+
try {
|
|
20
|
+
return fs.readFileSync(0, "utf8");
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Resolve the project root: CLAUDE_PROJECT_DIR env, else payload cwd, else process cwd.
|
|
27
|
+
function resolveProjectDir(payload) {
|
|
28
|
+
return process.env.CLAUDE_PROJECT_DIR || (payload && payload.cwd) || process.cwd();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Run a git command in projectDir; returns trimmed stdout or fallback on any error.
|
|
32
|
+
function git(projectDir, args, fallback) {
|
|
33
|
+
try {
|
|
34
|
+
return execFileSync("git", ["-C", projectDir, ...args], {
|
|
35
|
+
encoding: "utf8",
|
|
36
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
37
|
+
}).trim() || fallback;
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return fallback;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Slug a raw actor name into kebab-case ASCII.
|
|
44
|
+
function slugActor(name) {
|
|
45
|
+
return String(name || "")
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
48
|
+
.replace(/^-+|-+$/g, "");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Determine the actor: git user.name (slugged) → $USER → "agent".
|
|
52
|
+
function resolveActor(projectDir) {
|
|
53
|
+
const fromGit = slugActor(git(projectDir, ["config", "user.name"], ""));
|
|
54
|
+
if (fromGit) return fromGit;
|
|
55
|
+
const fromEnv = slugActor(process.env.USER || "");
|
|
56
|
+
if (fromEnv) return fromEnv;
|
|
57
|
+
return "agent";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Truncate a single line to n chars with an ellipsis marker.
|
|
61
|
+
function clip(text, n) {
|
|
62
|
+
const clean = String(text || "").replace(/\s+/g, " ").trim();
|
|
63
|
+
return clean.length > n ? clean.slice(0, n) + "…" : clean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Extract plain text from a message whose content is a string or content-block array.
|
|
67
|
+
function extractText(msg) {
|
|
68
|
+
if (!msg || !msg.content) return "";
|
|
69
|
+
if (typeof msg.content === "string") return msg.content;
|
|
70
|
+
if (Array.isArray(msg.content)) {
|
|
71
|
+
return msg.content
|
|
72
|
+
.filter((b) => b && b.type === "text" && typeof b.text === "string")
|
|
73
|
+
.map((b) => b.text)
|
|
74
|
+
.join(" ");
|
|
75
|
+
}
|
|
76
|
+
return "";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Strip harness-injected wrappers so only what the user actually typed survives.
|
|
80
|
+
function stripInjected(text) {
|
|
81
|
+
return String(text || "")
|
|
82
|
+
.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "")
|
|
83
|
+
.replace(/<(local-command-caveat|local-command-stdout|local-command-stderr|command-name|command-message|command-args|command-contents)>[\s\S]*?<\/\1>/g, "")
|
|
84
|
+
.trim();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Scan an assistant message's tool_use blocks for edited files and git commits.
|
|
88
|
+
function collectToolUse(msg, summary) {
|
|
89
|
+
if (!msg || !Array.isArray(msg.content)) return;
|
|
90
|
+
for (const block of msg.content) {
|
|
91
|
+
if (!block || block.type !== "tool_use") continue;
|
|
92
|
+
const name = block.name || "";
|
|
93
|
+
const input = block.input || {};
|
|
94
|
+
if (/^(Edit|Write|MultiEdit|NotebookEdit)$/.test(name) && input.file_path) {
|
|
95
|
+
summary.files.add(input.file_path);
|
|
96
|
+
}
|
|
97
|
+
if (name === "Bash" && typeof input.command === "string" && /\bgit\s+commit\b/.test(input.command)) {
|
|
98
|
+
summary.sawCommit = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Walk the JSONL transcript once: turn/edit/commit counts plus a USER/ASSISTANT text digest.
|
|
104
|
+
function digestTranscript(transcriptPath) {
|
|
105
|
+
const out = { turns: 0, files: new Set(), sawCommit: false, lines: [] };
|
|
106
|
+
if (!transcriptPath || !fs.existsSync(transcriptPath)) return out;
|
|
107
|
+
for (const line of fs.readFileSync(transcriptPath, "utf8").split("\n")) {
|
|
108
|
+
if (!line.trim()) continue;
|
|
109
|
+
let row;
|
|
110
|
+
try {
|
|
111
|
+
row = JSON.parse(line);
|
|
112
|
+
} catch (e) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (row.isSidechain) continue;
|
|
116
|
+
const msg = row && row.message ? row.message : row;
|
|
117
|
+
const role = (msg && msg.role) || row.type;
|
|
118
|
+
if (role === "user") {
|
|
119
|
+
const text = stripInjected(extractText(msg));
|
|
120
|
+
if (!text || text.startsWith("Caveat: The messages below")) continue;
|
|
121
|
+
out.lines.push("USER: " + clip(text, MAX_MESSAGE_CHARS));
|
|
122
|
+
} else if (role === "assistant") {
|
|
123
|
+
out.turns += 1;
|
|
124
|
+
collectToolUse(msg, out);
|
|
125
|
+
const text = extractText(msg);
|
|
126
|
+
if (text) out.lines.push("ASSISTANT: " + clip(text, MAX_MESSAGE_CHARS));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Compute the per-actor inbox file path.
|
|
133
|
+
function inboxPath(projectDir, actor) {
|
|
134
|
+
const dir = path.join(projectDir, "docs", "memory", "inbox");
|
|
135
|
+
return { dir, file: path.join(dir, `${actor}.md`) };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// True when this session already wrote an inbox entry (SessionEnd re-fires on resume).
|
|
139
|
+
function alreadyReflected(file, sessionId) {
|
|
140
|
+
try {
|
|
141
|
+
return Boolean(sessionId) && fs.existsSync(file) && fs.readFileSync(file, "utf8").includes(`session ${sessionId}`);
|
|
142
|
+
} catch (e) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Build the inbox file header block.
|
|
148
|
+
function inboxHeader(actor) {
|
|
149
|
+
return `# Memory inbox — ${actor}\nSession lessons awaiting distillation — appended by the session-reflect hook, consumed by \`distiller facts\`.\n`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// The full output contract sits AFTER the transcript — with it only up front, the model
|
|
153
|
+
// continues the conversation in the transcript instead of reflecting on it.
|
|
154
|
+
const PROMPT_HEADER = [
|
|
155
|
+
"You are a memory reflector. Below is the transcript of a FINISHED coding session,",
|
|
156
|
+
"delimited by <transcript> tags. Do not reply to it or continue it — you will be asked",
|
|
157
|
+
"to extract durable lessons from it after the closing tag.",
|
|
158
|
+
"",
|
|
159
|
+
"<transcript>",
|
|
160
|
+
"",
|
|
161
|
+
].join("\n");
|
|
162
|
+
|
|
163
|
+
const PROMPT_FOOTER = [
|
|
164
|
+
"",
|
|
165
|
+
"</transcript>",
|
|
166
|
+
"",
|
|
167
|
+
"The session above is over. Extract ONLY durable lessons a future session in this",
|
|
168
|
+
"repository would benefit from knowing:",
|
|
169
|
+
"- corrections or preferences the user stated",
|
|
170
|
+
"- failures whose root cause was found, with the fix",
|
|
171
|
+
"- decisions made, with their rationale",
|
|
172
|
+
"- discovered constraints or non-obvious facts about the project or its tooling",
|
|
173
|
+
"Rules:",
|
|
174
|
+
"- Most sessions contain none. If nothing qualifies, output exactly: NONE",
|
|
175
|
+
`- Otherwise output at most ${MAX_LESSONS} bullets and nothing else — no preamble, no headers.`,
|
|
176
|
+
'- Each bullet starts with "- [project]" (true of this repo or its tooling) or "- [user]" (true of this user).',
|
|
177
|
+
"- Each bullet is one complete, self-contained sentence with names and identifiers spelled out.",
|
|
178
|
+
"- Routine work (commits made, files edited, tasks completed) is NOT a lesson. Never include secrets.",
|
|
179
|
+
].join("\n");
|
|
180
|
+
|
|
181
|
+
// Hook mode: stash the payload and hand off to a detached worker so session exit is never delayed.
|
|
182
|
+
function hook() {
|
|
183
|
+
const raw = readStdin();
|
|
184
|
+
let payload = {};
|
|
185
|
+
try {
|
|
186
|
+
payload = raw ? JSON.parse(raw) : {};
|
|
187
|
+
} catch (e) {
|
|
188
|
+
payload = {};
|
|
189
|
+
}
|
|
190
|
+
if (!payload.transcript_path) return;
|
|
191
|
+
|
|
192
|
+
const stash = {
|
|
193
|
+
projectDir: resolveProjectDir(payload),
|
|
194
|
+
transcriptPath: payload.transcript_path,
|
|
195
|
+
sessionId: payload.session_id || "",
|
|
196
|
+
};
|
|
197
|
+
const stashFile = path.join(os.tmpdir(), `etna-reflect-${stash.sessionId || process.pid}.json`);
|
|
198
|
+
fs.writeFileSync(stashFile, JSON.stringify(stash), "utf8");
|
|
199
|
+
spawn(process.execPath, [__filename, "--worker", stashFile], { detached: true, stdio: "ignore" }).unref();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Worker mode: filter, digest, run the reflection model, append surviving lessons to the inbox.
|
|
203
|
+
function worker(stashFile) {
|
|
204
|
+
let stash;
|
|
205
|
+
try {
|
|
206
|
+
stash = JSON.parse(fs.readFileSync(stashFile, "utf8"));
|
|
207
|
+
} finally {
|
|
208
|
+
try {
|
|
209
|
+
fs.rmSync(stashFile, { force: true });
|
|
210
|
+
} catch (e) {
|
|
211
|
+
// Stale stash files are harmless.
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const { projectDir, transcriptPath, sessionId } = stash;
|
|
216
|
+
const digest = digestTranscript(transcriptPath);
|
|
217
|
+
if (digest.turns < MIN_TURNS && digest.files.size === 0 && !digest.sawCommit) return;
|
|
218
|
+
|
|
219
|
+
const actor = resolveActor(projectDir);
|
|
220
|
+
const { dir, file } = inboxPath(projectDir, actor);
|
|
221
|
+
if (alreadyReflected(file, sessionId)) return;
|
|
222
|
+
|
|
223
|
+
let text = redact(digest.lines.join("\n"));
|
|
224
|
+
if (text.length > MAX_DIGEST_CHARS) {
|
|
225
|
+
text = text.slice(0, 8000) + "\n[... middle of session elided ...]\n" + text.slice(-(MAX_DIGEST_CHARS - 8000));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const output = execFileSync("claude", ["-p", "--model", "haiku"], {
|
|
229
|
+
input: PROMPT_HEADER + text + PROMPT_FOOTER,
|
|
230
|
+
encoding: "utf8",
|
|
231
|
+
cwd: os.tmpdir(),
|
|
232
|
+
env: Object.assign({}, process.env, { ETNA_REFLECT: "1" }),
|
|
233
|
+
timeout: 240000,
|
|
234
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
235
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const lessons = output
|
|
239
|
+
.split("\n")
|
|
240
|
+
.map((l) => l.trim())
|
|
241
|
+
.filter((l) => /^- \[(project|user)\]\s+\S/.test(l))
|
|
242
|
+
.slice(0, MAX_LESSONS)
|
|
243
|
+
.map((l) => redact(l));
|
|
244
|
+
if (!lessons.length) return;
|
|
245
|
+
|
|
246
|
+
const repo = path.basename(projectDir);
|
|
247
|
+
const branch = git(projectDir, ["rev-parse", "--abbrev-ref", "HEAD"], "-");
|
|
248
|
+
const now = new Date();
|
|
249
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
250
|
+
const stamp = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}`;
|
|
251
|
+
|
|
252
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
253
|
+
if (alreadyReflected(file, sessionId)) return;
|
|
254
|
+
const exists = fs.existsSync(file);
|
|
255
|
+
const block =
|
|
256
|
+
(exists ? "\n" : inboxHeader(actor) + "\n") +
|
|
257
|
+
`## ${stamp} · session ${sessionId || "unknown"} · ${repo}@${branch}\n` +
|
|
258
|
+
lessons.join("\n") +
|
|
259
|
+
"\n";
|
|
260
|
+
fs.appendFileSync(file, block, "utf8");
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function main() {
|
|
264
|
+
if (process.env.ETNA_REFLECT) return;
|
|
265
|
+
const workerIdx = process.argv.indexOf("--worker");
|
|
266
|
+
if (workerIdx !== -1) {
|
|
267
|
+
worker(process.argv[workerIdx + 1]);
|
|
268
|
+
} else {
|
|
269
|
+
hook();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
main();
|
|
275
|
+
} catch (e) {
|
|
276
|
+
try {
|
|
277
|
+
process.stderr.write(`session-reflect hook: ${e && e.message ? e.message : e}\n`);
|
|
278
|
+
} catch (e2) {
|
|
279
|
+
// Nothing more we can do.
|
|
280
|
+
}
|
|
281
|
+
} finally {
|
|
282
|
+
process.exit(0);
|
|
283
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Technology Choices & Standards
|
|
6
|
+
|
|
7
|
+
Opinionated technology stack and implementation standards.
|
|
8
|
+
|
|
9
|
+
## Environment & Versions
|
|
10
|
+
|
|
11
|
+
### Runtime Versions
|
|
12
|
+
|
|
13
|
+
- **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)
|
|
17
|
+
|
|
18
|
+
### Recommended Packages
|
|
19
|
+
|
|
20
|
+
No alternatives. No explanations for obvious choices. Just use these.
|
|
21
|
+
|
|
22
|
+
| Package | Version | Use | Don't use |
|
|
23
|
+
| ------------------------ | --------- | ---------------------- | ----------------------- |
|
|
24
|
+
| react | ^19.2.3 | UI components | - |
|
|
25
|
+
| vite | ^8.0.0 | Build tool, dev server | webpack, CRA |
|
|
26
|
+
| tailwindcss | ^4.1.18 | Styling | styled-components |
|
|
27
|
+
| @vitejs/plugin-react-swc | ^4.2.2 | JSX compilation | @vitejs/plugin-react |
|
|
28
|
+
| react-router-dom | ^7.11.0 | Routing | - |
|
|
29
|
+
| @reduxjs/toolkit | ^2.11.2 | State management | zustand, jotai |
|
|
30
|
+
| react-redux | ^9.2.0 | Redux React bindings | - |
|
|
31
|
+
| axios | ^1.13.2 | HTTP requests | fetch |
|
|
32
|
+
| date-fns | ^4.1.0 | Date formatting | moment, dayjs |
|
|
33
|
+
| @dnd-kit/core | ^6.3.1 | Drag and drop | react-beautiful-dnd |
|
|
34
|
+
| motion | ^12.23.26 | Animations | react-spring |
|
|
35
|
+
| sonner | ^2.0.7 | Toast notifications | react-toastify |
|
|
36
|
+
| react-window | ^2.2.3 | List virtualization | react-virtualized |
|
|
37
|
+
| nanoid | ^5.1.6 | ID generation | uuid |
|
|
38
|
+
| 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 | - |
|
|
41
|
+
| zod | ^4.2.1 | Schema validation | yup, joi |
|
|
42
|
+
| jose | ^6.1.3 | JWT handling | jsonwebtoken |
|
|
43
|
+
| microdiff | ^1.5.0 | JSON object diffing | deep-diff, just-diff |
|
|
44
|
+
| react18-json-view | ^0.2.9 | JSON tree view | react-json-view |
|
|
45
|
+
| @tiptap/react | ^3.11.1 | Rich text editor | slate, quill, draft-js |
|
|
46
|
+
| shiki | ^3.22.0 | Syntax highlighting | prism, highlight.js |
|
|
47
|
+
| lucide-react | ^0.562.0 | Icons | react-icons, heroicons |
|
|
48
|
+
| @pdfme/generator | ^5.5.8 | PDF generation | jspdf, pdfkit |
|
|
49
|
+
| cmdk | ^1.1.1 | Command palette | kbar, ninja-keys |
|
|
50
|
+
| @monaco-editor/react | ^4.7.0 | Code editor | codemirror, ace |
|
|
51
|
+
| localforage | ^1.10.0 | Client-side storage | idb, dexie |
|
|
52
|
+
| socket.io-client | ^4.7.0 | WebSocket client | native WebSocket, ws |
|
|
53
|
+
| react-markdown | ^10.1.0 | Markdown rendering | marked, markdown-it |
|
|
54
|
+
| fast-deep-equal | ^3.1.3 | Deep equality checks | lodash.isEqual |
|
|
55
|
+
| @ngneat/falso | latest | Fake data generation | @faker-js/faker, chance |
|
|
56
|
+
| pino | ^10.1.0 | Logging | winston |
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Express 5.x Setup
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
app.use(cors({ origin: process.env.FRONTEND_URL }));
|
|
64
|
+
app.use(compression());
|
|
65
|
+
app.use(express.json({ limit: "10mb" }));
|
|
66
|
+
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
|
|
67
|
+
```
|
|
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
|
+
---
|
|
75
|
+
|
|
76
|
+
## API Response Format
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"status": 1,
|
|
81
|
+
"message": "Message sent successfully",
|
|
82
|
+
"data": { "delivered": [{ "user@example.com": 2 }] }
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Error:**
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"status": 0,
|
|
91
|
+
"message": "Error description",
|
|
92
|
+
"data": null
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
HTTP codes: `200`, `201` (created), `400`, `401` (not authenticated), `403` (not authorized), `404`, `500`
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Nomenclature Guidelines
|
|
6
|
+
|
|
7
|
+
## Naming Guidelines
|
|
8
|
+
|
|
9
|
+
- Use descriptive names that reveal intent, purpose, and usage
|
|
10
|
+
- Names should explain why something exists and how it's used
|
|
11
|
+
- Favor clarity over brevity
|
|
12
|
+
- Maintain consistency within the codebase
|
|
13
|
+
- Avoid abbreviations unless they're universally understood
|
|
14
|
+
|
|
15
|
+
## Naming Styles
|
|
16
|
+
|
|
17
|
+
- Variables : Use nouns or noun phrases. Examples: `userData`, `firstName`, `paymentStatus`, `configOptions`
|
|
18
|
+
- Functions & Methods : Use verbs or verb phrases that describe the action. Examples: `calculateTotal`, `getUserInfo`, `validateInput`, `sendNotification`
|
|
19
|
+
- Boolean Variables : Prefix with is**, has**, can**, or should**. Examples: `isValid`, `hasPermission`, `canEdit`, `shouldRefresh`
|
|
20
|
+
- Arrays : Use plural nouns. Examples: `users`, `productList`, `errorMessages`, `activeConnections`
|
|
21
|
+
- Classes & Components : Use singular nouns or noun phrases. Examples: `RequestHandler`, `UserProfile`, `PaymentProcessor`, `DataValidator`
|
|
22
|
+
- Constants : Use uppercase with meaningful names. Examples: `DEFAULT_TIMEOUT`, `API_BASE_URL`, `MAX_RETRY_COUNT`, `COLOR_PRIMARY`
|
|
23
|
+
- Event Handlers : Use Prefix with handle or on. Examples: `handleSubmit`, `onUserLogin`, `handleInputChange`, `onPageLoad`
|
|
24
|
+
- Interfaces & Types : Use Consider prefixing with I for interfaces (if preferred). Examples: `IUserData`, `IConfigOptions`, `AuthResponse`, `RequestParams`
|
|
25
|
+
- Promises & Async Functions : Use Consider using future tense or suffixes like Async. Examples: `fetchUserData`, `getUserProfileAsync`, `loadConfigAsync`, `processPayment`
|
|
26
|
+
- File & Folder Structure : Use lowercase with descriptive names. Group by feature or function type. Example: `auth/login_service.js`
|
|
27
|
+
|
|
28
|
+
## Casing Styles
|
|
29
|
+
|
|
30
|
+
- camelCase : Used in Variables, function names, method names, property names. Examples: `userData`, `calculateTotal`, `firstName`, `isValidEmail`
|
|
31
|
+
- PascalCase : Used in Class names, component names, type definitions. Examples: `UserProfile`, `DataService`, `AuthProvider`, `RequestHandler`
|
|
32
|
+
- snake_case : Used in File names, folder names. Examples: `user_service`, `api_utils`, `auth_middleware`, `data_models`
|
|
33
|
+
- UPPER_SNAKE_CASE : Used in Constants, environment variables. Examples: `MAX_RETRY_COUNT`, `API_KEY`, `DEFAULT_TIMEOUT`, `COLOR_PRIMARY`
|
|
34
|
+
- kebab-case : Used in URLs, CSS class names, HTML IDs. Examples: `nav-container`, `user-profile`, `button-primary`, `form-wrapper`
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
---
|
|
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".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Application Boundary Skill
|
|
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/`.
|
|
12
|
+
|
|
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.
|
|
22
|
+
|
|
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.
|
|
28
|
+
|
|
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.
|
|
114
|
+
|
|
115
|
+
## Mode: `create` (targeted)
|
|
116
|
+
|
|
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).
|
|
119
|
+
|
|
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.
|
|
127
|
+
|
|
128
|
+
## Mode: `check` (pre-implementation)
|
|
129
|
+
|
|
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`.)
|
|
132
|
+
|
|
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):
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
# Application Boundary — check — {plan filename}
|
|
164
|
+
|
|
165
|
+
Plan: {path}
|
|
166
|
+
Boundaries: {N} files evaluated
|
|
167
|
+
Intersections: {N} ({critical}/{high}/{medium}/{low})
|
|
168
|
+
|
|
169
|
+
## Violations
|
|
170
|
+
| # | Boundary | Risk | Severity | Plan step |
|
|
171
|
+
|
|
172
|
+
## Suggested Plan Additions
|
|
173
|
+
- Violation #1 → add step X to Implementation Steps / Done Criteria …
|
|
174
|
+
|
|
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}
|
|
179
|
+
```
|
|
180
|
+
|
|
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
|
+
## Rules
|
|
222
|
+
|
|
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.
|