@nurix/etna 0.1.2 → 0.2.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/README.md +34 -27
- package/bin/etna.js +82 -52
- package/data/harness-index.json +2 -8
- package/data/harnesses/code/CLAUDE.md +18 -9
- package/data/harnesses/code/agents/change-review.md +80 -0
- package/data/harnesses/code/agents/contract-monitor.md +65 -0
- package/data/harnesses/code/agents/dep-health.md +148 -0
- package/data/harnesses/code/agents/git-history.md +201 -0
- package/data/harnesses/code/agents/maintenance.md +112 -0
- package/data/harnesses/code/agents/review.md +109 -0
- package/data/harnesses/code/agents/spec-mode.md +56 -0
- package/data/harnesses/code/rules/general.md +96 -0
- package/data/harnesses/code/rules/nomenclature.md +34 -0
- package/data/harnesses/code/scripts/build-ownership-index.sh +138 -0
- package/data/harnesses/code/scripts/check-doc-drift.sh +125 -0
- package/data/harnesses/code/skills/documentation/SKILL.md +238 -0
- package/data/harnesses/code/skills/documentation/references/examples.md +193 -0
- package/data/harnesses/code/skills/documentation/references/style-guide.md +288 -0
- package/data/harnesses/code/skills/documentation/references/templates.md +901 -0
- package/data/harnesses/code/skills/introspect/SKILL.md +135 -0
- package/data/harnesses/code/skills/workflow-mapper/SKILL.md +430 -0
- package/data/harnesses/code/skills/workflow-mapper/references/patterns.md +214 -0
- package/data/harnesses/kb-agi/CLAUDE.md +57 -0
- package/data/harnesses/kb-agi/agents/kb-agi-init.md +276 -0
- package/data/harnesses/kb-agi/agents/kb-catch-stale.md +22 -0
- package/data/harnesses/kb-agi/agents/kb-find-gaps.md +27 -0
- package/data/harnesses/kb-agi/agents/kb-fix-links.md +22 -0
- package/data/harnesses/kb-agi/agents/kb-refresh-frontdoor.md +22 -0
- package/data/harnesses/kb-agi/agents/kb-refresh-summaries.md +22 -0
- package/data/harnesses/kb-agi/agents/kb-sync-metadata.md +25 -0
- package/data/harnesses/kb-agi/rules/kb.md +13 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
# @nurix/etna
|
|
2
2
|
|
|
3
|
-
CLI to bootstrap a project with a Nurix harness.
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
/plugin marketplace add nurixlabs/skills
|
|
7
|
-
/plugin install nurix-harness@nurixlabs
|
|
8
|
-
```
|
|
9
|
-
|
|
10
|
-
inside Claude Code, plus drops the harness's `CLAUDE.md` at the project root. See [`../../harness/README.md`](../../harness/README.md) for what a harness is and why the install model is designed this way.
|
|
3
|
+
CLI to bootstrap a project with a Nurix harness. Copies the harness's `CLAUDE.md`, skills, agents, rules, and scripts into the project so they live with the code — version-controllable, editable per project, and visible without leaving the repo.
|
|
11
4
|
|
|
12
5
|
## Usage
|
|
13
6
|
|
|
@@ -17,46 +10,60 @@ From the root of the project you want to bootstrap:
|
|
|
17
10
|
npx @nurix/etna nurix-harness
|
|
18
11
|
```
|
|
19
12
|
|
|
20
|
-
After this completes
|
|
13
|
+
After this completes:
|
|
21
14
|
|
|
22
15
|
```
|
|
23
16
|
your-project/
|
|
24
|
-
├── CLAUDE.md
|
|
17
|
+
├── CLAUDE.md ← harness policy (backup of any existing file is kept)
|
|
25
18
|
└── .claude/
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
├── skills/ ← documentation, introspect, workflow-mapper
|
|
20
|
+
├── agents/ ← spec-mode, maintenance, contract-monitor, …
|
|
21
|
+
├── rules/ ← general.md, nomenclature.md
|
|
22
|
+
└── scripts/ ← build-ownership-index.sh, check-doc-drift.sh
|
|
29
23
|
```
|
|
30
24
|
|
|
31
|
-
That's the entire footprint.
|
|
25
|
+
That's the entire footprint. Nothing is written outside the project. No marketplace registration, no `enabledPlugins` entry, no global cache dependency — Claude Code picks up project-scoped agents/skills/rules/scripts on its next session start.
|
|
32
26
|
|
|
33
|
-
|
|
27
|
+
Re-run `npx @nurix/etna nurix-harness` anytime to refresh the harness content with what the latest published package ships. Existing same-named files are overwritten; files you've added that aren't part of the harness are left alone. The previous `CLAUDE.md` is backed up to `CLAUDE.md.bak.<ISO-timestamp>`.
|
|
28
|
+
|
|
29
|
+
## Migrating from 0.1.x
|
|
30
|
+
|
|
31
|
+
`0.1.x` registered the harness as a Claude Code plugin via the `nurixlabs/skills` marketplace. `0.2.0` switches to direct file installation. When you re-run `etna` on a project that has a `0.1.x` install, the CLI removes the stale `enabledPlugins["<plugin>@nurixlabs"]` entry from `<project>/.claude/settings.json` so Claude Code doesn't double-load the same content from the global plugin cache.
|
|
32
|
+
|
|
33
|
+
The `nurixlabs` marketplace entry in `~/.claude/settings.json` is harmless to leave (it doesn't load anything by itself) — remove it manually if you want.
|
|
34
34
|
|
|
35
35
|
## Available harnesses
|
|
36
36
|
|
|
37
37
|
Listed by `npx @nurix/etna` (no args) using the bundled `data/harness-index.json`.
|
|
38
38
|
|
|
39
|
-
| Plugin name |
|
|
39
|
+
| Plugin name | Source in etna | What it ships |
|
|
40
40
|
|---|---|---|
|
|
41
|
-
| `nurix-harness` | [`harness
|
|
41
|
+
| `nurix-harness` | [`plugins/nurix-harness.md`](../../plugins/nurix-harness.md) → top-level catalogs | Governance agents, baseline workflow skills, ownership/drift scripts |
|
|
42
|
+
| `kb-agi` | [`plugins/kb-agi.md`](../../plugins/kb-agi.md) → top-level catalogs | Knowledge-vault scaffolding |
|
|
42
43
|
|
|
43
44
|
## How it works
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
4. Adds `<plugin-name>@<marketplace>: true` to `./.claude/settings.json` under `enabledPlugins`.
|
|
46
|
+
Each harness has a folder under `etna/harness/<name>/` with:
|
|
47
|
+
- `CLAUDE.md` — the project-root policy file
|
|
48
|
+
- `index.md` — symlinked to `../../plugins/<plugin>.md` (the manifest)
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
The plugin manifest's YAML frontmatter declares which top-level `agents/`, `skills/`, `rules/`, and `scripts/` belong to that plugin:
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
```yaml
|
|
53
|
+
---
|
|
54
|
+
name: nurix-harness
|
|
55
|
+
skills: [documentation, introspect, workflow-mapper]
|
|
56
|
+
agents: [spec-mode, maintenance, contract-monitor, …]
|
|
57
|
+
scripts: [build-ownership-index, check-doc-drift]
|
|
58
|
+
rules: [general, nomenclature]
|
|
59
|
+
---
|
|
60
|
+
```
|
|
53
61
|
|
|
54
|
-
-
|
|
55
|
-
- **`CLAUDE.md`** is bundled in this npm package, versioned with the package. A plugin cannot ship a project-root `CLAUDE.md` (Claude Code's plugin spec doesn't support it), so the CLI ships that half itself. Harness policy changes warrant a package version bump.
|
|
62
|
+
`scripts/build-data.js` (run by `prepack`) resolves each manifest against the catalogs and bundles the result into `data/harnesses/<folder>/`. The installer (`bin/etna.js`) reads from that bundle at install time. No GitHub fetch at runtime — the published npm package contains everything.
|
|
56
63
|
|
|
57
64
|
## Building / contributing
|
|
58
65
|
|
|
59
|
-
`data/` is generated. Don't edit by hand. To regenerate after
|
|
66
|
+
`data/` is generated. Don't edit by hand. To regenerate after editing the etna catalogs or manifests:
|
|
60
67
|
|
|
61
68
|
```sh
|
|
62
69
|
cd packages/cli
|
|
@@ -67,4 +74,4 @@ npm run build:data
|
|
|
67
74
|
|
|
68
75
|
## Publishing
|
|
69
76
|
|
|
70
|
-
CI auto-publishes on push to `dev` when `packages/cli/**` or `harness/**` change — see [`.github/workflows/publish-etna-to-npm.yml`](../../.github/workflows/publish-etna-to-npm.yml). The `publishConfig.access` is `restricted` so the scoped package is private by default.
|
|
77
|
+
CI auto-publishes on push to `dev` when `packages/cli/**` or `harness/**` change — see [`.github/workflows/publish-etna-to-npm.yml`](../../.github/workflows/publish-etna-to-npm.yml). The `publishConfig.access` is `restricted` so the scoped package is private by default. The `NPM_PUBLISH_TOKEN` GitHub Actions secret must be a granular access token with **Bypass 2FA** enabled — classic automation tokens are rejected by the npmjs.com 2FA policy on the `nurix-tools` account.
|
package/bin/etna.js
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// packages/cli/bin/etna.js
|
|
3
|
-
// `npx @nurix/etna <plugin-name>` —
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import {
|
|
3
|
+
// `npx @nurix/etna <plugin-name>` — copy a Nurix harness into the current
|
|
4
|
+
// directory. Drops the harness's CLAUDE.md at the project root and copies its
|
|
5
|
+
// skills, agents, rules, and scripts into <project>/.claude/. No marketplace,
|
|
6
|
+
// no plugin enablement — files live in the project, version-controlled and
|
|
7
|
+
// editable per project. Re-run `etna` to refresh.
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
existsSync,
|
|
11
|
+
mkdirSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
writeFileSync,
|
|
14
|
+
renameSync,
|
|
15
|
+
copyFileSync,
|
|
16
|
+
cpSync,
|
|
17
|
+
readdirSync,
|
|
18
|
+
lstatSync,
|
|
19
|
+
} from 'node:fs';
|
|
10
20
|
import { join, dirname, resolve } from 'node:path';
|
|
11
21
|
import { fileURLToPath } from 'node:url';
|
|
12
22
|
|
|
@@ -14,17 +24,18 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
14
24
|
const pkgRoot = resolve(__dirname, '..');
|
|
15
25
|
const dataDir = join(pkgRoot, 'data');
|
|
16
26
|
|
|
27
|
+
// Subfolders bundled inside each harness payload that map 1:1 onto
|
|
28
|
+
// <project>/.claude/<name>/. Order matches Claude Code's own folder layout.
|
|
29
|
+
const PLUGIN_SUBDIRS = ['skills', 'agents', 'rules', 'scripts'];
|
|
30
|
+
|
|
17
31
|
function usage() {
|
|
18
32
|
const idx = loadHarnessIndex();
|
|
19
33
|
console.log(`\nUsage: npx @nurix/etna <plugin-name>\n`);
|
|
20
34
|
const names = Object.keys(idx.plugins);
|
|
21
35
|
if (names.length) {
|
|
22
|
-
console.log(`Available harnesses
|
|
23
|
-
for (const name of names) {
|
|
24
|
-
console.log(` ${name.padEnd(20)} → ${idx.marketplace.name}`);
|
|
25
|
-
}
|
|
36
|
+
console.log(`Available harnesses:`);
|
|
37
|
+
for (const name of names) console.log(` ${name}`);
|
|
26
38
|
}
|
|
27
|
-
console.log(`\nMarketplace: ${idx.marketplace.source.repo} (github)`);
|
|
28
39
|
console.log();
|
|
29
40
|
}
|
|
30
41
|
|
|
@@ -41,6 +52,14 @@ function die(msg, code = 1) {
|
|
|
41
52
|
process.exit(code);
|
|
42
53
|
}
|
|
43
54
|
|
|
55
|
+
function isSymlink(path) {
|
|
56
|
+
try {
|
|
57
|
+
return lstatSync(path).isSymbolicLink();
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
// Move <path> to <path>.bak.<timestamp> if it exists. Returns the backup path, or null.
|
|
45
64
|
function backupIfExists(path) {
|
|
46
65
|
if (!existsSync(path) && !isSymlink(path)) return null;
|
|
@@ -50,14 +69,6 @@ function backupIfExists(path) {
|
|
|
50
69
|
return backup;
|
|
51
70
|
}
|
|
52
71
|
|
|
53
|
-
function isSymlink(path) {
|
|
54
|
-
try {
|
|
55
|
-
return lstatSync(path).isSymbolicLink();
|
|
56
|
-
} catch {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
72
|
function readJson(path) {
|
|
62
73
|
if (!existsSync(path)) return {};
|
|
63
74
|
try {
|
|
@@ -73,27 +84,44 @@ function writeJson(path, value) {
|
|
|
73
84
|
writeFileSync(path, JSON.stringify(value, null, 2) + '\n');
|
|
74
85
|
}
|
|
75
86
|
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
// Copy the harness's bundled .claude/<subdir>/ contents into <project>/.claude/<subdir>/,
|
|
88
|
+
// merging with whatever's already there. Existing same-named entries are overwritten so
|
|
89
|
+
// re-running `etna` always refreshes harness-provided content. Returns count copied.
|
|
90
|
+
function copyHarnessSubdir(bundledSubdir, projectSubdir) {
|
|
91
|
+
if (!existsSync(bundledSubdir)) return 0;
|
|
92
|
+
mkdirSync(projectSubdir, { recursive: true });
|
|
93
|
+
let copied = 0;
|
|
94
|
+
for (const entry of readdirSync(bundledSubdir, { withFileTypes: true })) {
|
|
95
|
+
const src = join(bundledSubdir, entry.name);
|
|
96
|
+
const dest = join(projectSubdir, entry.name);
|
|
97
|
+
cpSync(src, dest, { recursive: true, force: true });
|
|
98
|
+
copied += 1;
|
|
99
|
+
}
|
|
100
|
+
return copied;
|
|
85
101
|
}
|
|
86
102
|
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
103
|
+
// Clean up any leftover marketplace/enabledPlugins entries from a previous 0.1.x
|
|
104
|
+
// install. After 0.2.0 we don't use the marketplace flow at all — leaving stale
|
|
105
|
+
// entries causes Claude Code to also resolve the plugin from the global cache,
|
|
106
|
+
// double-loading the same skills under a different namespace.
|
|
107
|
+
function migrateFromPluginInstall(targetDir, pluginName) {
|
|
108
|
+
const removed = [];
|
|
109
|
+
const projectSettingsPath = join(targetDir, '.claude', 'settings.json');
|
|
110
|
+
if (existsSync(projectSettingsPath)) {
|
|
111
|
+
const s = readJson(projectSettingsPath);
|
|
112
|
+
if (s.enabledPlugins) {
|
|
113
|
+
const keys = Object.keys(s.enabledPlugins).filter((k) =>
|
|
114
|
+
k.startsWith(`${pluginName}@`),
|
|
115
|
+
);
|
|
116
|
+
for (const k of keys) {
|
|
117
|
+
delete s.enabledPlugins[k];
|
|
118
|
+
removed.push(`enabledPlugins["${k}"] in ${projectSettingsPath}`);
|
|
119
|
+
}
|
|
120
|
+
if (Object.keys(s.enabledPlugins).length === 0) delete s.enabledPlugins;
|
|
121
|
+
writeJson(projectSettingsPath, s);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return removed;
|
|
97
125
|
}
|
|
98
126
|
|
|
99
127
|
async function main(args) {
|
|
@@ -111,34 +139,36 @@ async function main(args) {
|
|
|
111
139
|
);
|
|
112
140
|
}
|
|
113
141
|
|
|
114
|
-
const
|
|
115
|
-
|
|
142
|
+
const bundledHarnessDir = join(dataDir, 'harnesses', harnessFolder);
|
|
143
|
+
const bundledClaudeMd = join(bundledHarnessDir, 'CLAUDE.md');
|
|
144
|
+
if (!existsSync(bundledClaudeMd)) {
|
|
116
145
|
die(`Bundled CLAUDE.md missing for harness '${harnessFolder}'. Rebuild package data.`);
|
|
117
146
|
}
|
|
118
147
|
|
|
119
148
|
const targetDir = process.cwd();
|
|
120
149
|
const claudeMdDest = join(targetDir, 'CLAUDE.md');
|
|
121
|
-
const marketplace = idx.marketplace;
|
|
122
|
-
const fqKey = `${pluginName}@${marketplace.name}`;
|
|
123
150
|
|
|
124
|
-
console.log(`\nInstalling ${
|
|
151
|
+
console.log(`\nInstalling ${pluginName} into ${targetDir}`);
|
|
125
152
|
|
|
126
153
|
const claudeBackup = backupIfExists(claudeMdDest);
|
|
127
154
|
if (claudeBackup) console.log(` backed up existing CLAUDE.md → ${claudeBackup}`);
|
|
128
155
|
|
|
129
|
-
copyFileSync(
|
|
156
|
+
copyFileSync(bundledClaudeMd, claudeMdDest);
|
|
130
157
|
console.log(` installed CLAUDE.md → ${claudeMdDest}`);
|
|
131
158
|
|
|
132
|
-
const
|
|
133
|
-
|
|
159
|
+
for (const sub of PLUGIN_SUBDIRS) {
|
|
160
|
+
const count = copyHarnessSubdir(
|
|
161
|
+
join(bundledHarnessDir, sub),
|
|
162
|
+
join(targetDir, '.claude', sub),
|
|
163
|
+
);
|
|
164
|
+
if (count > 0) console.log(` copied ${count} ${sub} → ${targetDir}/.claude/${sub}/`);
|
|
165
|
+
}
|
|
134
166
|
|
|
135
|
-
const
|
|
136
|
-
console.log(`
|
|
167
|
+
const migrated = migrateFromPluginInstall(targetDir, pluginName);
|
|
168
|
+
for (const entry of migrated) console.log(` removed stale ${entry}`);
|
|
137
169
|
|
|
138
170
|
console.log(`\nDone. Open Claude Code in ${targetDir}.`);
|
|
139
|
-
console.log(
|
|
140
|
-
`It will fetch ${marketplace.source.repo}, install ${pluginName}, and load it on next session start.\n`,
|
|
141
|
-
);
|
|
171
|
+
console.log(`Re-run \`npx @nurix/etna ${pluginName}\` anytime to refresh harness content.\n`);
|
|
142
172
|
}
|
|
143
173
|
|
|
144
174
|
main(process.argv.slice(2)).catch((e) => {
|
package/data/harness-index.json
CHANGED
|
@@ -73,6 +73,13 @@ Feature docs in `docs/features/` must include YAML frontmatter with `owns`, `tou
|
|
|
73
73
|
|
|
74
74
|
The spec-mode agent checks for ownership conflicts during research. If conflicts are found, address them in the spec before proceeding.
|
|
75
75
|
|
|
76
|
+
### Architecture Decisions (ADRs)
|
|
77
|
+
|
|
78
|
+
Record an ADR when a decision (a) changes how state, dependencies, or boundaries are organized, (b) has a rejected alternative worth remembering, and (c) could plausibly be re-litigated. Default: don't write one. Style preferences, local refactors, and obvious-from-code choices don't qualify.
|
|
79
|
+
|
|
80
|
+
- File: `docs/architecture/adr/NNNN-kebab-case.md` (4-digit sequential number). Frozen once written — supersede via a new ADR, never edit.
|
|
81
|
+
- Invoke the `documentation` skill with "record an ADR" or "architecture decision".
|
|
82
|
+
|
|
76
83
|
---
|
|
77
84
|
|
|
78
85
|
## Agents
|
|
@@ -88,7 +95,7 @@ Seven sub-agents and three skills are defined under `.claude/`. Reach for them b
|
|
|
88
95
|
| `dep-health` | Agent | Dependency supply-chain audit | "audit dependencies", "check CVEs", "dep health" |
|
|
89
96
|
| `git-history` | Agent | Classify recent commits into a velocity/severity CSV | "classify commits", "commit history report" |
|
|
90
97
|
| `change-review` | Agent | Velocity heatmap, risk zones (high churn + low coverage) | "where is risk accumulating", "velocity report" |
|
|
91
|
-
| `documentation` | Skill | Baseline audit, scaffolding, templates | "baseline docs", "create docs", "audit docs" |
|
|
98
|
+
| `documentation` | Skill | Baseline audit, scaffolding, templates, ADRs, guardrails | "baseline docs", "create docs", "audit docs", "record an ADR", "architecture decision", "create guardrails" |
|
|
92
99
|
| `workflow-mapper` | Skill | Discover and diagram codebase workflows | "map workflows", "trace flows" |
|
|
93
100
|
| `introspect` | Skill | Pre-implementation impact analysis against guardrails | "introspect this plan", "impact analysis" |
|
|
94
101
|
|
|
@@ -140,20 +147,22 @@ Unless explicitly requested:
|
|
|
140
147
|
|
|
141
148
|
The six procedural folders below are fixed. Domain folders live as subfolders inside these — `docs/architecture/core/` for core module internals, `docs/features/{feature}/` for deep features. Discovery flows through **[`docs/routing.md`](docs/routing.md)** when present.
|
|
142
149
|
|
|
143
|
-
| Folder
|
|
144
|
-
|
|
|
145
|
-
| `docs/architecture/`
|
|
146
|
-
| `docs/
|
|
147
|
-
| `docs/
|
|
148
|
-
| `docs/
|
|
149
|
-
| `docs/
|
|
150
|
-
| `docs/
|
|
150
|
+
| Folder | Purpose | Mutability |
|
|
151
|
+
| ------------------------ | -------------------------------------------------------- | --------------------- |
|
|
152
|
+
| `docs/architecture/` | System-wide patterns, technical decisions, API specs | Living |
|
|
153
|
+
| `docs/architecture/adr/` | Architecture Decision Records — numbered, one per file | Frozen (supersede) |
|
|
154
|
+
| `docs/features/` | How implemented features work today | Living |
|
|
155
|
+
| `docs/operations/` | Setup, development, deployment | Living |
|
|
156
|
+
| `docs/history/` | Plans, changelogs, backlog | Frozen or append-only |
|
|
157
|
+
| `docs/memory/` | Project knowledge + agent state | Living |
|
|
158
|
+
| `docs/tests/` | Test plans and automation | Living |
|
|
151
159
|
|
|
152
160
|
### Naming & Lifecycle
|
|
153
161
|
|
|
154
162
|
- Features: `kebab-case.md` — living reference, status `Active`
|
|
155
163
|
- Plans: `YYYY-MM-DD-kebab-case.md` — frozen after `Complete` or `Abandoned`
|
|
156
164
|
- Changelogs: `YYYY-MM-DD-kebab-case.md` — append-only
|
|
165
|
+
- ADRs: `NNNN-kebab-case.md` (4-digit zero-padded number, sequential) — frozen; supersede via a new ADR
|
|
157
166
|
- Memory: `{agent-name}.md` — agent-maintained
|
|
158
167
|
|
|
159
168
|
### Documentation Maintenance
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: change-review
|
|
3
|
+
description: Velocity analysis — produces commit heatmap, risk-zone identification (high churn + low guardrail coverage), stabilization signals, team distribution. Consumes `git-history`'s CSV output rather than reanalyzing commits. Use when planning where to invest in better guardrails or rules.
|
|
4
|
+
tools: Read, Glob, Grep, Bash
|
|
5
|
+
model: inherit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Change-Review Agent
|
|
9
|
+
|
|
10
|
+
You analyze code-change velocity over time to identify risk zones. Where `git-history` classifies *what* changed (module, impact, severity), you ask *where is change accelerating or decelerating* — and whether guardrail coverage matches the risk.
|
|
11
|
+
|
|
12
|
+
You answer: "where should the tech lead invest in better guardrails or rules next?"
|
|
13
|
+
|
|
14
|
+
## Before Starting
|
|
15
|
+
|
|
16
|
+
1. If `./docs/memory/change-review.md` exists, read it for historical velocity snapshots, prior risk-zone flags, and known concentration risks.
|
|
17
|
+
2. If `./docs/memory/git-history.md` or a recent CSV from the git-history agent exists, prefer it as input — don't re-classify commits. If no recent run exists, ask the user to run `git-history` first or fall back to a direct `git log` analysis.
|
|
18
|
+
3. Read `./docs/operations/guardrails.md` if it exists. Risk-zone scoring is "high churn + low guardrail coverage" — without guardrails, you can only produce a heatmap, not risk zones.
|
|
19
|
+
4. Read `./docs/OWNERSHIP_INDEX.md` if it exists, to map churn to owning feature docs.
|
|
20
|
+
|
|
21
|
+
## Step 1: Velocity Heatmap
|
|
22
|
+
|
|
23
|
+
Compute commits-per-module over rolling windows:
|
|
24
|
+
|
|
25
|
+
- **1 week** — what's hot right now
|
|
26
|
+
- **1 month** — current rhythm
|
|
27
|
+
- **3 months** — quarter-level pattern
|
|
28
|
+
|
|
29
|
+
Aggregate by top-level module (`src/{module}/`). Report:
|
|
30
|
+
|
|
31
|
+
| Module | Commits 1w | Commits 1m | Commits 3m | Trend |
|
|
32
|
+
|--------|-----------:|-----------:|-----------:|-------|
|
|
33
|
+
|
|
34
|
+
Trend column: ↑ rising, ↓ falling, → steady, ↑↑ sharply rising, ↓↓ sharply falling.
|
|
35
|
+
|
|
36
|
+
## Step 2: Risk Zones
|
|
37
|
+
|
|
38
|
+
For each module with high churn (top quartile in 1m window), check guardrail coverage:
|
|
39
|
+
|
|
40
|
+
- Does `docs/operations/guardrails.md` mention this module?
|
|
41
|
+
- Is the module's `owns` doc up to date? (cross-reference with maintenance agent's last drift check if available)
|
|
42
|
+
- Does the module appear in `docs/routing.md`?
|
|
43
|
+
|
|
44
|
+
Flag risk zones: high churn × low coverage. These are where silent breakage is most likely.
|
|
45
|
+
|
|
46
|
+
## Step 3: Stabilization Signals
|
|
47
|
+
|
|
48
|
+
Identify modules where 1-week and 1-month velocity are both decreasing. Candidates for reducing review overhead — fewer guardrails needed where churn is dropping.
|
|
49
|
+
|
|
50
|
+
## Step 4: Team Distribution
|
|
51
|
+
|
|
52
|
+
Per module, list which authors made the most commits in the 3-month window. Surface concentration risks:
|
|
53
|
+
|
|
54
|
+
- One author > 70% of commits in a critical module → bus-factor flag
|
|
55
|
+
- One author owns all changes touching a guardrail → review-coverage flag
|
|
56
|
+
|
|
57
|
+
## Step 5: Report & Track
|
|
58
|
+
|
|
59
|
+
### Report format
|
|
60
|
+
|
|
61
|
+
- **Velocity heatmap** (table above)
|
|
62
|
+
- **Risk zones**: top 5 high-churn / low-coverage modules with specific guardrails to consider adding
|
|
63
|
+
- **Stabilization signals**: modules where review overhead can decrease
|
|
64
|
+
- **Concentration risks**: bus-factor and review-coverage flags
|
|
65
|
+
- **Recommendations**: prioritized list — typically "add guardrail for X", "update routing.md to cover Y", "spread reviews of Z across more authors"
|
|
66
|
+
|
|
67
|
+
### Update tracking
|
|
68
|
+
|
|
69
|
+
1. Write snapshot to `docs/memory/change-review.md` with date and all four sections above
|
|
70
|
+
2. Append items to `docs/history/backlog.md`:
|
|
71
|
+
- `- **[P1]** No guardrail coverage for high-churn module \`src/{name}/\` — discovered by change-review`
|
|
72
|
+
3. Report summary to the user
|
|
73
|
+
|
|
74
|
+
## Rules
|
|
75
|
+
|
|
76
|
+
- Prefer git-history's CSV as input; only fall back to direct `git log` analysis if no recent CSV exists
|
|
77
|
+
- Read-only for source code
|
|
78
|
+
- May write to `docs/memory/change-review.md` and `docs/history/backlog.md`
|
|
79
|
+
- Risk-zone identification requires guardrails — if `guardrails.md` doesn't exist, produce heatmap only and tell the user that risk-zone scoring needs documented guardrails
|
|
80
|
+
- A clean report (no risk zones) is valid for a low-churn, well-covered project
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: contract-monitor
|
|
3
|
+
description: Post-implementation contract auditor. Scans the live codebase for violations of cross-module contracts documented in `docs/operations/guardrails.md` — unregistered entity types, string-coupled constants, cross-entity references without propagation, schema mismatches, undocumented endpoints. Run on request after significant merges. Pairs with the `introspect` skill (which runs pre-implementation).
|
|
4
|
+
tools: Read, Glob, Grep, Edit, Write, Bash
|
|
5
|
+
model: inherit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Contract Monitor
|
|
9
|
+
|
|
10
|
+
You scan the codebase for cross-module contract violations. Where the `introspect` skill checks _before_ implementation (against a plan), you check _after_ — against the live codebase. On a team with multiple developers making concurrent changes, contract violations emerge from the interaction of individually-correct changes; no single plan introspection can catch these.
|
|
11
|
+
|
|
12
|
+
## Before Starting
|
|
13
|
+
|
|
14
|
+
1. Read `./docs/operations/guardrails.md` — this is your source of truth for which contracts exist. If it doesn't exist, stop and tell the user a guardrails doc is required.
|
|
15
|
+
2. If `./docs/memory/contract-monitor.md` exists, read it for known exceptions, historical violation counts, and patterns the user has chosen not to fix.
|
|
16
|
+
3. If `./docs/routing.md` exists, read it for module ownership context.
|
|
17
|
+
|
|
18
|
+
## Scan
|
|
19
|
+
|
|
20
|
+
For each guardrail section in `guardrails.md`:
|
|
21
|
+
|
|
22
|
+
1. Parse the section — extract contract name, canonical source location, consumer paths, violation description, checklist (if any)
|
|
23
|
+
2. Scan the codebase for violations of that specific contract. Categories to check:
|
|
24
|
+
- **Unregistered entity types** — strings used as entity-type identifiers that aren't in the canonical array
|
|
25
|
+
- **String-coupled constants** — raw string literals used where shared constant imports are required
|
|
26
|
+
- **Cross-entity references without propagation** — storing entity A's slug/name/id by value in entity B without documented propagation
|
|
27
|
+
- **Schema mismatches** — selectors accessing persisted fields without fallback defaults
|
|
28
|
+
- **Undocumented API endpoints** — routes in code with no corresponding feature doc or guardrail entry
|
|
29
|
+
3. Verify by reading actual source files — never guess.
|
|
30
|
+
|
|
31
|
+
## Act
|
|
32
|
+
|
|
33
|
+
For each violation:
|
|
34
|
+
|
|
35
|
+
- **Mechanical fix** (missing constant import, missing fallback default, missing entry in canonical array) → fix it directly with an Edit
|
|
36
|
+
- **Design-decision fix** (new entity type registration, new cross-entity propagation mechanism, schema migration) → append to `docs/history/backlog.md` as P0 or P1 with the specific guardrail and consumer paths
|
|
37
|
+
- **Pre-existing exception** (recorded in `docs/memory/contract-monitor.md`) → skip silently; don't re-flag
|
|
38
|
+
|
|
39
|
+
## Report
|
|
40
|
+
|
|
41
|
+
Present a structured report:
|
|
42
|
+
|
|
43
|
+
| # | Guardrail | Violation | Location | Action taken |
|
|
44
|
+
|---|-----------|-----------|----------|--------------|
|
|
45
|
+
|
|
46
|
+
Then update `docs/memory/contract-monitor.md` with:
|
|
47
|
+
- Scan date
|
|
48
|
+
- Violation counts per guardrail
|
|
49
|
+
- Items fixed mechanically (with file paths)
|
|
50
|
+
- Items backlogged (with P-level and rationale)
|
|
51
|
+
- New exceptions added (if any)
|
|
52
|
+
|
|
53
|
+
## Optional: extend guardrails
|
|
54
|
+
|
|
55
|
+
If you discover patterns in code that should be guardrails but aren't yet documented:
|
|
56
|
+
|
|
57
|
+
1. Surface the candidate guardrail to the user with a draft entry
|
|
58
|
+
2. Wait for approval before writing to `guardrails.md` — never add a guardrail unilaterally
|
|
59
|
+
|
|
60
|
+
## Rules
|
|
61
|
+
|
|
62
|
+
- Never delete feature docs or modify frozen records (`docs/history/plans/`, `docs/history/changelog/`)
|
|
63
|
+
- Be specific: include file paths, line numbers, and the exact violating string
|
|
64
|
+
- Respect known-exceptions in `docs/memory/contract-monitor.md`
|
|
65
|
+
- A clean report is valid — do not invent violations
|