@nurix/etna 0.19.23 → 0.19.24
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/data/harness-index.json +1 -1
- package/data/harnesses/code/.vscode/settings.json +9 -0
- package/data/harnesses/code/CLAUDE.md +6 -1
- package/data/harnesses/code/agents/maintenance.md +2 -2
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/rules/database.md +29 -1
- package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +3 -1
- package/data/harnesses/code/skills/test-plan/SKILL.md +82 -0
- package/data/harnesses/discover/.vscode/settings.json +9 -0
- package/data/harnesses/discover/harness.json +1 -1
- package/data/harnesses/discussion/.vscode/settings.json +9 -0
- package/data/harnesses/discussion/harness.json +2 -2
- package/data/harnesses/discussion/rules/artifacts.md +1 -1
- package/data/harnesses/discussion/rules/method.md +2 -0
- package/data/harnesses/etna/.vscode/settings.json +9 -0
- package/data/harnesses/etna/agents/maintenance.md +2 -2
- package/data/harnesses/etna/harness.json +1 -1
- package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +3 -1
- package/data/harnesses/hr/.vscode/settings.json +9 -0
- package/data/harnesses/hr/harness.json +1 -1
- package/data/harnesses/kb-agi/.vscode/settings.json +9 -0
- package/data/harnesses/kb-agi/harness.json +1 -1
- package/package.json +1 -1
package/data/harness-index.json
CHANGED
|
@@ -6,5 +6,14 @@
|
|
|
6
6
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
7
7
|
"editor.formatOnSave": true,
|
|
8
8
|
"editor.wordWrap": "on"
|
|
9
|
+
},
|
|
10
|
+
// Hide harness-authoring scaffolding from the file explorer
|
|
11
|
+
"files.exclude": {
|
|
12
|
+
".claude": true,
|
|
13
|
+
".vscode": true,
|
|
14
|
+
".agents": true,
|
|
15
|
+
".codex": true,
|
|
16
|
+
"**/node_modules": true,
|
|
17
|
+
"**/apollo.service.json": true
|
|
9
18
|
}
|
|
10
19
|
}
|
|
@@ -35,7 +35,8 @@ Three peer modes plus a Completion step — enter wherever the user's thinking a
|
|
|
35
35
|
|
|
36
36
|
1. Freeze the plan — `Status: Complete`, fill the Completed date. The file is now frozen.
|
|
37
37
|
2. Flip the feature LLD to `status: active`; extend it in place with the how-it-works sections; confirm its `owns:` globs against the actual code.
|
|
38
|
-
3. The `boundary`
|
|
38
|
+
3. The `boundary` and `test-plan` skills must already have gated this plan before its first implementation step — see Ownership, Boundaries & Documentation.
|
|
39
|
+
4. Derive/update the behavioral checklist at `docs/tests/{feature}.md` via the `documentation` skill (a truth document — present-tense, referencing the LLD's promises, restating no intent), then run the affected test suite + the project's e2e smoke gate and record the results in the changelog entry.
|
|
39
40
|
|
|
40
41
|
**On Brainstorm Close** (when a session ends, not when a plan completes): close + harvest is the skill's `stop` procedure — converge, a user-approved harvest manifest, apply, then a uselessness test; conclusions disperse by scope (architecture docs, decision records, backlog), the record moves from `docs/research/{topic}-brainstorm/` to `docs/history/brainstorms/{topic}/`, and the user decides whether the moved folder is deleted or committed.
|
|
41
42
|
|
|
@@ -62,6 +63,10 @@ Boundaries are **diagnosed, not documented** — the `boundary` skill derives th
|
|
|
62
63
|
|
|
63
64
|
**Invoke the `boundary` skill after a plan is approved and before its first implementation step.** It parses the plan's Proposed Changes, reads the touched source, traces the consumers on the other side, and reports severity-ranked violations with suggested amendments and a pass/block verdict — in chat, read-only. Fold the amendments into the plan before coding. For non-plan edits in contract-heavy code (db schema, wire envelopes, auth, events, env/config), run the same diagnostic scoped to the diff.
|
|
64
65
|
|
|
66
|
+
### Test obligations — specify before implementing
|
|
67
|
+
|
|
68
|
+
**After `boundary` passes and its amendments are folded, invoke the `test-plan` skill** — same plan gate, ordered second. It derives the tests the plan owes from its Proposed Changes and the owning LLD, and writes a **Test Obligations** section into the plan (one row per promised behavior: layer · target file · oracle · fixtures). It writes no test files — the tests land *with* the code at implementation time (the suite-first guardrail), and the manifest is the checklist that work satisfies. This makes obligations survive dispatch to an executor that sees only the plan file, and lets a later doc-health pass prove the tests match the promises. On a boundary **BLOCK** it does not run — re-plan first. The three-moment split (gate → implementation → Completion checklist), the layer rules, and the e2e altitude gate live in the skill.
|
|
69
|
+
|
|
65
70
|
### Documentation — close the loop
|
|
66
71
|
|
|
67
72
|
- **Every markdown document goes through the `documentation` skill.** Before creating or substantively editing any `.md` document — a doc, a README, an index, a record — invoke the `documentation` skill and apply its templates and `references/style-guide.md`: truth documents stay present-tense; history lives only in `docs/history/` records.
|
|
@@ -143,7 +143,7 @@ docs/
|
|
|
143
143
|
│ ├── brainstorms/ # Harvested brainstorm sessions ({topic}/) — user decides kept (committed) or deleted; Readme.md is the index
|
|
144
144
|
│ └── backlog.md # Tracked work items by priority (P0-P3)
|
|
145
145
|
├── memory/ # index.md, main.md, {agent}.md — project knowledge + per-agent strategy memory
|
|
146
|
-
└── tests/ #
|
|
146
|
+
└── tests/ # Behavioral test checklists ({feature}.md, step → **Assert**), derived at plan Completion via the `documentation` skill; author-maintained, present only when test docs exist
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
There is **no `docs/routing.md`**, **no `docs/modules/{m}/`**, **no `docs/pillars/`**, **no `docs/boundaries/`**. Code ownership lives in feature `owns:` frontmatter; a module is a `module:` label, not a folder; boundaries are diagnosed from source; brainstorm records live in `docs/research/` (active) and `docs/history/brainstorms/` (harvested).
|
|
@@ -152,7 +152,7 @@ There is **no `docs/routing.md`**, **no `docs/modules/{m}/`**, **no `docs/pillar
|
|
|
152
152
|
|
|
153
153
|
### Step B1 — Inventory and classify
|
|
154
154
|
|
|
155
|
-
Glob every markdown file. Classify each: Architecture, Features, Operations, Plans, Changelogs, Backlog, Brainstorm records (stay), Stale/Deprecated (delete), Root docs (stay). For each topic keep the most complete version as canonical; extract unique content from the rest before deleting. **No redirect stubs** — when you move a file, delete the original and fix every reference (cross-doc links, memory files). Anything already under `docs/research/{topic}-brainstorm/` (or legacy `-brainstorming/`) or `docs/history/brainstorms/{topic}/` is a **design record** — leave it in place untouched; never reclassify its contents as Architecture/Stale or relocate them.
|
|
155
|
+
Glob every markdown file. Classify each: Architecture, Features, Operations, Tests (behavioral checklists — stay), Plans, Changelogs, Backlog, Brainstorm records (stay), Stale/Deprecated (delete), Root docs (stay). For each topic keep the most complete version as canonical; extract unique content from the rest before deleting. **No redirect stubs** — when you move a file, delete the original and fix every reference (cross-doc links, memory files). Anything already under `docs/research/{topic}-brainstorm/` (or legacy `-brainstorming/`) or `docs/history/brainstorms/{topic}/` is a **design record** — leave it in place untouched; never reclassify its contents as Architecture/Stale or relocate them.
|
|
156
156
|
|
|
157
157
|
While reading each markdown doc to classify it, if the doc names a code symbol it asserts exists that **statically resolves to nothing**, note it as an observation for the Part A audit — **only** on stacks where static resolution is reliable (stay silent on codegen/reflection/DI/barrel stacks); scoped to docs already being read here, never a whole-repo prose sweep; **docs only**, never code-file comments.
|
|
158
158
|
|
|
@@ -4,6 +4,34 @@ Fixed conventions for relational schemas — table/column/index **names**, the *
|
|
|
4
4
|
|
|
5
5
|
Written for SQLite / Cloudflare D1 + Drizzle (the concrete types below). The **names and key vocabulary are engine-agnostic** — a Postgres project keeps the same column names and semantics with native types (`uuid`, `timestamptz`, `boolean`).
|
|
6
6
|
|
|
7
|
+
## What earns a table
|
|
8
|
+
|
|
9
|
+
A table is the most expensive home for data — it costs a migration to change its shape, a join to read, and a mirror to keep in sync. Default to **not** a table. A concept earns its own table only when it needs at least one property in the left column; lacking all of them, the data belongs in the cheaper home on the right.
|
|
10
|
+
|
|
11
|
+
| A table is earned by… | Lacking all of these, the data belongs in… |
|
|
12
|
+
| -------------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
13
|
+
| identity minted at **runtime** | — |
|
|
14
|
+
| variation **per tenant / user** | — |
|
|
15
|
+
| **independent addressing** — queried, indexed, or FK'd in isolation | a **JSON column** on its parent row |
|
|
16
|
+
| **concurrency-safe** writes — racing writers must not clobber a shared blob | — |
|
|
17
|
+
| a **DB-enforced invariant** — immutability trigger, unique, atomic boundary | — |
|
|
18
|
+
| dev-authored, published on deploy, identical for every tenant | **config / a constants file**, validated at build |
|
|
19
|
+
| exactly recomputable from other rows | a **cache** (KV / edge), recomputed on demand |
|
|
20
|
+
| process state that is dead once the request completes | a **log or workflow engine**, never the domain DB |
|
|
21
|
+
| owned by another system-of-record | a **reference column** — store the id, not the data |
|
|
22
|
+
|
|
23
|
+
Each routing rule is one question:
|
|
24
|
+
|
|
25
|
+
- **Static vocabulary or a fixed graph → config, not a table.** Dev-authored, same for every tenant, never FK'd at runtime ⇒ a constants file validated at build, surfaced in code as a constant/type. Reifying it into tables plus a seed makes the file and the tables a two-copy sync problem. The refined boundary is § Vocabulary / lookup tables.
|
|
26
|
+
- **History → one immutable log + a mutable head.** Never shatter versioning into per-entity `*_revisions` / `*_revision_sections` tables. Snapshot the coherent set once into an append-only versions table; current state is a derived projection of the newest entry. _Test: is this table just "old rows of that table"? → collapse it into the snapshot._
|
|
27
|
+
- **Process state is not domain state.** _Test: if it were deleted the instant the request finished, would a product feature break? No ⇒ it is a log line or a workflow-engine concern._
|
|
28
|
+
- **Derived output is a cache, not truth.** _Test: is it exactly recomputable from other rows + code? Yes ⇒ recompute and cache; never persist as first-class state._
|
|
29
|
+
- **Un-addressed detail is a column.** _Test: is this row ever SELECTed / JOINed / FK'd alone? No ⇒ it is a JSON field on its parent._
|
|
30
|
+
- **Another system's record → keep the pointer.** _Test: does a specialized service already own this — chat, telemetry, object store? ⇒ store its id, not a copy._
|
|
31
|
+
- **A surviving table names the invariant it buys.** Each kept structure pays for one guarantee — content-addressed immutable rows buy reproducibility, a monotonic sequence buys ordering/sync, an atomic write boundary buys multi-row atomicity. Drop the requirement and the table drops with it; keep the requirement and the structure is the price a blob cannot pay.
|
|
32
|
+
|
|
33
|
+
Table count tracks **requirements, not domain richness** — a rich domain does not entitle a schema to a table per noun.
|
|
34
|
+
|
|
7
35
|
## Keys & the shared vocabulary
|
|
8
36
|
|
|
9
37
|
Three column roles are fixed across every table and every project:
|
|
@@ -33,6 +61,6 @@ Reference columns name what they point at: `<entity>_id` for a UUID-PK reference
|
|
|
33
61
|
|
|
34
62
|
## Vocabulary / lookup tables
|
|
35
63
|
|
|
36
|
-
A controlled
|
|
64
|
+
A controlled vocabulary earns a **table** only when a user row FKs into it and reads its `label` / `description` at runtime; a controlled vocabulary that is static, dev-authored config nobody references by FK stays **config** (§ What earns a table) — enumerable is not the same as tabular, and a `type`-style column validated against the constants file is preferred over an FK to a seeded mirror. When a table is earned, it follows a fixed shape: `id` (UUID) · `slug` (UNIQUE) · `label` · optional `description` · `status` · timestamps.
|
|
37
65
|
|
|
38
66
|
- **Seeds are generated, never hand-written** — the source of truth is a CSV or a shared package; a generator emits the `*.seed.sql` with UUIDv5 ids and an idempotent `ON CONFLICT(slug)` upsert that preserves live counts / admin-tuned columns. Hand-editing seed SQL is ❌ — regenerate it.
|
|
@@ -11,7 +11,8 @@ const RULES = [
|
|
|
11
11
|
{ id: 'progress-marker', severity: 'block', scope: 'truth', pattern: /\bphases?\s+\d[^.\n]{0,40}\b(complete|done)\b|\b(build|implementation) (is )?(\d+%|partially) (complete|done)\b/i, message: 'progress marker — progress lives in the plan/backlog record' },
|
|
12
12
|
{ id: 'temporal-hedge', severity: 'warn', scope: 'truth', pattern: /\bnot yet\b|\bfor now\b|\bcurrently\b|\bstill (unsupported|missing|pending|unimplemented|in progress)\b/i, message: 'temporal hedge — state the fact without "currently / not yet / for now"' },
|
|
13
13
|
{ id: 'predecessor-frame', severity: 'warn', scope: 'truth', pattern: /\bpreviously\b|\bthe old \w+\b|\blegacy \w+ (was|is)\b/i, message: 'predecessor framing — describe the one current design' },
|
|
14
|
-
|
|
14
|
+
// docs/tests/ behavioral checklists use `- [ ]` as test-step notation (step → **Assert**), not progress tracking — exempt that one folder from this rule; every other truth doc still bans checkboxes.
|
|
15
|
+
{ id: 'status-glyph', severity: 'warn', scope: 'truth', exempt: /(^|\/)docs\/tests\//, pattern: /(^|\s)(✅|⏳|- \[[ x]\])/u, message: 'status glyph / checklist in a truth doc — tracking belongs in a record' },
|
|
15
16
|
{ id: 'date-in-truth-doc', severity: 'warn', scope: 'truth', pattern: /\b20\d{2}-\d{2}-\d{2}\b/, message: 'date in a truth doc — only the AD Decision Changelog and structure.md Health Log tails carry dates' },
|
|
16
17
|
];
|
|
17
18
|
|
|
@@ -71,6 +72,7 @@ function lintFile(path, findings) {
|
|
|
71
72
|
|
|
72
73
|
for (const rule of RULES) {
|
|
73
74
|
if (rule.scope !== scope || disabled.has(rule.id)) continue;
|
|
75
|
+
if (rule.exempt && rule.exempt.test(path)) continue;
|
|
74
76
|
for (const { n, line } of lines) {
|
|
75
77
|
if (!rule.pattern.test(line)) continue;
|
|
76
78
|
if (line.includes(`doc-lint-disable: ${rule.id}`) || new RegExp(`doc-lint-disable:[^>]*\\b${rule.id}\\b`).test(line)) continue;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-plan
|
|
3
|
+
description: "Test-obligation diagnostic for implementation plans. Runs at the plan gate after boundary, derives the tests a plan owes from its Proposed Changes + the owning LLD, and writes a Test Obligations section into the plan — one row per promised behavior: layer, target file, oracle, fixtures. Advisory + plan-mutating; writes no test files. Triggers on 'test plan', 'what needs tests', 'test obligations'."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Test-Obligation Diagnostic
|
|
7
|
+
|
|
8
|
+
You derive the tests an implementation plan owes, and pin them into the plan as an explicit, reviewable manifest — at the same gate `boundary` runs, ordered second. You never write a test file; you make the standing "tests ship with the change" guardrail *concrete* for this plan, so the implementer satisfies a checklist instead of a vibe, and so a later doc-health pass can prove the tests match the promises.
|
|
9
|
+
|
|
10
|
+
**The litmus is "which promise, at which altitude, proven by what?"** A plan promises behavior (an LLD's `owns:` surface does X); each promise earns exactly one test at the cheapest layer that can actually prove it. Spend your reading deciding the *layer* and the *oracle*, not on prose.
|
|
11
|
+
|
|
12
|
+
## The three-moment split
|
|
13
|
+
|
|
14
|
+
Testing spans three moments in the plan lifecycle. This skill owns **only the first** — it is why "write the tests at plan time" is wrong: at the gate the code does not exist yet, so the only coherent artifact is the manifest.
|
|
15
|
+
|
|
16
|
+
| Moment | Artifact | Owner |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| **Plan gate** (here, after boundary) | Test Obligations section written into the plan | this skill |
|
|
19
|
+
| **Implementation** | the obligated tests, green in the same commits as the code | the implementer (standing guardrail — this skill never pre-empts it) |
|
|
20
|
+
| **Completion** | behavioral checklist at `docs/tests/{feature}.md`; run affected suite + the project's e2e smoke gate; note in changelog | the `documentation` skill + Completion steps |
|
|
21
|
+
|
|
22
|
+
Writing tests *before* the code they cover either fabricates implementation coupling (labels/routes/copy the plan hasn't fixed) or leaves the suite red on the shared branch for the plan's whole duration. Neither is acceptable — so the gate emits obligations, and the tests land *with* the code.
|
|
23
|
+
|
|
24
|
+
## Contract sources
|
|
25
|
+
|
|
26
|
+
- **The plan's Proposed Changes** — files created/modified, entities/state, API changes, cross-module contracts. Each is a behavior that may owe a test.
|
|
27
|
+
- **The owning LLD** (`docs/features/{feature}.md`, matched via the ownership index) — the canonical statement of *what the feature does*. Every obligation names the LLD promise it proves; the LLD is never restated, only referenced. This one-directional chain (LLD → manifest → tests + Completion checklist) is what stops the three artifacts drifting into three contradictory truths.
|
|
28
|
+
- **`.claude/rules/testing.md`** — the frameworks and canonical placement per stack. The manifest's target paths and runners come from here, never invented. The repo's existing runner always wins.
|
|
29
|
+
- **`boundary`'s output** — a HIGH/CRITICAL finding it surfaced is itself a behavior owed a regression test (the failure mode, asserted). Fold boundary's amendments first, then read the amended plan.
|
|
30
|
+
|
|
31
|
+
## Layer assignment
|
|
32
|
+
|
|
33
|
+
Assign each behavior the **cheapest layer that can prove it**, then name the oracle (the concrete thing that shows pass/fail):
|
|
34
|
+
|
|
35
|
+
- **unit** — a pure function / reducer / validator. Oracle: return value, thrown error.
|
|
36
|
+
- **component** — a UI unit's behavior in isolation (a widget, form, panel). Oracle: rendered role/text, emitted event. Runner: the stack's component runner per `testing.md`.
|
|
37
|
+
- **integration** — a route/handler against a real in-memory datastore via the app instance (not a mock of what you own). Oracle: response envelope shape, status code, the row actually written. **This is the default for server behavior** — most obligations land here, and this layer is the one the unit gate (`test` script / CI) runs.
|
|
38
|
+
- **e2e** — see the altitude gate below.
|
|
39
|
+
|
|
40
|
+
Error paths, races, and idempotency are obligations too, and they belong at **integration** with injected faults (network mock, the runner's fake timers, a forced constraint) — a process driving a live product cannot reliably *produce* them, so they are never an e2e obligation.
|
|
41
|
+
|
|
42
|
+
### The e2e altitude gate
|
|
43
|
+
|
|
44
|
+
An e2e obligation (a scenario in the project's e2e tree per `testing.md`) is earned **only** when the behavior genuinely spans processes/services that no single-process test can exercise together. A single-process behavior, however important, is an integration obligation, never e2e.
|
|
45
|
+
|
|
46
|
+
- Density is **one scenario per cross-process journey, not one per feature** — the project's existing scenarios are the reference density. Mark each e2e obligation **"extend scenario `<name>`"** in preference to **"new scenario"**; a new scenario needs a genuinely new journey.
|
|
47
|
+
- **Refuse to emit any e2e obligation while the project's e2e smoke gate is not wired.** A generated spec fed to a suite nothing runs is a rot accelerator. The gate is a capped, tagged smoke subset run in CI (not the full e2e suite — an oversized always-on e2e suite is what gets evicted from CI, taking all enforcement with it). If no such CI gate exists, emit the e2e obligation as a **blocked** row naming the missing gate, not as work to do.
|
|
48
|
+
|
|
49
|
+
## Run
|
|
50
|
+
|
|
51
|
+
**Input:** a plan path (given, or the newest in `docs/history/plans/`), already boundary-checked with amendments folded. For a non-plan diff in test-worthy code, the hunks stand in for Proposed Changes.
|
|
52
|
+
|
|
53
|
+
1. **Build the behavior list.** From the plan's Proposed Changes + the owning LLD's promised behavior, list the behaviors this plan introduces or changes. Fold in each HIGH/CRITICAL boundary finding as a regression obligation. Note behaviors a type or the compiler already guards — those owe **no** test (the boundary litmus, inverted: if it can't ship green-but-broken, it needs no test).
|
|
54
|
+
|
|
55
|
+
2. **Assign a layer + oracle** to each behavior (rules above). Prefer the cheapest layer that can actually prove the promise.
|
|
56
|
+
|
|
57
|
+
3. **Map to a target file** per `testing.md` — the stack's unit/integration path for most, the e2e scenario tree for a program scenario. Name fixtures/factories (existing helper, or new) and the fault-injection needed (network mock, fake timers, forced constraint). Use the repo-standard fake-data source.
|
|
58
|
+
|
|
59
|
+
4. **Apply the e2e altitude gate** to any cross-process obligation; downgrade everything else to integration.
|
|
60
|
+
|
|
61
|
+
5. **Emit the Test Obligations section into the plan**, plus a one-line chat summary. Write it as a subsection of the plan (an executor dispatched with only the plan file sees nothing else — an obligation that lives only in chat is lost at dispatch):
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
## Test Obligations
|
|
65
|
+
|
|
66
|
+
| # | Behavior (LLD promise) | Layer | Target file | Oracle | Fixtures / faults |
|
|
67
|
+
| - | ---------------------- | ----- | ----------- | ------ | ----------------- |
|
|
68
|
+
|
|
69
|
+
Coverage: {N} behaviors · {N} integration · {N} component · {N} unit · {N} e2e ({N} extend, {N} new)
|
|
70
|
+
Completion checklist: docs/tests/{feature}.md (derived at Completion, not now)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
A behavior with no obligation is stated too, with the reason (compiler-guarded, covered by an existing test) — an empty cell reads as "not yet analyzed"; an explicit "none — compiler-guarded" reads as "checked."
|
|
74
|
+
|
|
75
|
+
## Rules
|
|
76
|
+
|
|
77
|
+
- **Manifest only.** You write the Test Obligations section into the plan and a chat summary. You write **no** test files and **no** `docs/tests/` checklist — those are implementation-time and Completion-time, owned by others.
|
|
78
|
+
- **Every obligation names its LLD promise.** No promise, no obligation source — trace it or drop it. This is the anti-drift pin.
|
|
79
|
+
- **Cheapest proving layer wins.** Do not obligate an e2e scenario for what an integration test proves, or an integration test for what a unit test proves.
|
|
80
|
+
- **e2e only above the cross-process bar, and only while the smoke gate runs.** Otherwise the obligation is a blocked row naming the missing gate.
|
|
81
|
+
- **Runs after boundary; skipped on BLOCK.** A blocked plan is re-planned first; there is nothing stable to obligate tests against.
|
|
82
|
+
- **A short manifest is valid.** A plan that owes three integration tests owes three rows — do not pad. The coverage line is the evidence it was analyzed, not skipped.
|
|
@@ -6,5 +6,14 @@
|
|
|
6
6
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
7
7
|
"editor.formatOnSave": true,
|
|
8
8
|
"editor.wordWrap": "on"
|
|
9
|
+
},
|
|
10
|
+
// Hide harness-authoring scaffolding from the file explorer
|
|
11
|
+
"files.exclude": {
|
|
12
|
+
".claude": true,
|
|
13
|
+
".vscode": true,
|
|
14
|
+
".agents": true,
|
|
15
|
+
".codex": true,
|
|
16
|
+
"**/node_modules": true,
|
|
17
|
+
"**/apollo.service.json": true
|
|
9
18
|
}
|
|
10
19
|
}
|
|
@@ -6,5 +6,14 @@
|
|
|
6
6
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
7
7
|
"editor.formatOnSave": true,
|
|
8
8
|
"editor.wordWrap": "on"
|
|
9
|
+
},
|
|
10
|
+
// Hide harness-authoring scaffolding from the file explorer
|
|
11
|
+
"files.exclude": {
|
|
12
|
+
".claude": true,
|
|
13
|
+
".vscode": true,
|
|
14
|
+
".agents": true,
|
|
15
|
+
".codex": true,
|
|
16
|
+
"**/node_modules": true,
|
|
17
|
+
"**/apollo.service.json": true
|
|
9
18
|
}
|
|
10
19
|
}
|
|
@@ -18,7 +18,7 @@ A ratified class-3 doc **is** the goal-track canon — never double-book it unde
|
|
|
18
18
|
|
|
19
19
|
**Status vocabulary**: `**DRAFT — not yet ratified**` / `Not yet user-ratified` · `(ratified YYYY-MM-DD)` — at doc, section, or extract granularity · `**Superseded note (YYYY-MM-DD):** … kept as record` · `deferred — blocked on <gate>` vs the exclusion grades.
|
|
20
20
|
|
|
21
|
-
**Immutability**: raw dumps immutable once filed (corrections land in critic or synthesis) · unratified syntheses may be rewritten in place as v2, recorded in provenance · ratified material changes by dated append or supersession only · nothing valuable is deleted — superseded → banner + keep; broken → flagged; closed threads → struck through. Every lifecycle event carries an ISO date.
|
|
21
|
+
**Immutability**: raw dumps immutable once filed (corrections land in critic or synthesis) · unratified syntheses may be rewritten in place as v2, recorded in provenance · ratified material changes by dated append or supersession only · nothing valuable is deleted — superseded → banner + keep; a retired topic → `git mv` to `archived/`, dated archive banner, kept frozen as record; broken → flagged; closed threads → struck through. Every lifecycle event carries an ISO date.
|
|
22
22
|
|
|
23
23
|
## The topic README
|
|
24
24
|
|
|
@@ -9,6 +9,7 @@ How sessions run in this workspace. What gets written is [artifacts.md](artifact
|
|
|
9
9
|
- **External instruments** — code/data a topic depends on (scraper, corpus, pipeline) lives **outside the workspace**, never at the root: this folder holds only topics and the harness. Record the dependency on the depending topic's README (external path + ratification status); exclusions are recorded on the excluder's side.
|
|
10
10
|
- **Git** — the workspace **is a git repository** (the `discussion-baseline` agent initializes one). The SessionEnd hook auto-commits each session's changes as one `📝 session close` batch — never per-file commits. Milestone commits by hand are still welcome: `✨` content, `🔧` workspace/method. Root `.gitignore` stays minimal.
|
|
11
11
|
- **Grandfathering** — when the method changes, never reshuffle existing artifacts to match. New artifacts follow current rules; old layouts stay, flagged where confusing.
|
|
12
|
+
- **Archiving** — a topic whose inquiry is *closed by outside reality* — its design **realized**, its understanding **superseded** — is retired, not deleted: `git mv` its folder into `archived/<topic>/` (history preserved; the folder frozen, kept as record), add a dated archive banner to its README (`📦 ARCHIVED (YYYY-MM-DD) — realized/superseded`: state · reason · pointer to the live source of truth), and move its `TOPICS.md` entry into the **Archived topics** section. Archiving records that a topic is done; live work stops there and nothing valuable is lost.
|
|
12
13
|
|
|
13
14
|
## Session protocol
|
|
14
15
|
|
|
@@ -61,6 +62,7 @@ Method lives in the rules (timeless); state lives in `metadata/` (current); topi
|
|
|
61
62
|
- Per topic: `` ## `topic-folder/` — <state phrase> `` — the suffix encodes **who it's waiting on** ("synthesis filed, awaiting user reaction").
|
|
62
63
|
- One dense routing paragraph: what it is, which artifacts exist (files, dates), headline findings — enough to route without opening the folder.
|
|
63
64
|
- Bullets: `**Open:**` (typically "user disposition on the N questions closing `<file>`") · exclusion grades, dated when user rulings · `**Note:**` caveats.
|
|
65
|
+
- Retired topics move to an `## Archived topics` section at the bottom — one condensed entry each (why archived: realized/superseded · the live source of truth); a one-line archiving note sits in the preamble. The active list holds only live topics.
|
|
64
66
|
|
|
65
67
|
### `SESSIONS.md` — session ledger
|
|
66
68
|
|
|
@@ -6,5 +6,14 @@
|
|
|
6
6
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
7
7
|
"editor.formatOnSave": true,
|
|
8
8
|
"editor.wordWrap": "on"
|
|
9
|
+
},
|
|
10
|
+
// Hide harness-authoring scaffolding from the file explorer
|
|
11
|
+
"files.exclude": {
|
|
12
|
+
".claude": true,
|
|
13
|
+
".vscode": true,
|
|
14
|
+
".agents": true,
|
|
15
|
+
".codex": true,
|
|
16
|
+
"**/node_modules": true,
|
|
17
|
+
"**/apollo.service.json": true
|
|
9
18
|
}
|
|
10
19
|
}
|
|
@@ -143,7 +143,7 @@ docs/
|
|
|
143
143
|
│ ├── brainstorms/ # Harvested brainstorm sessions ({topic}/) — user decides kept (committed) or deleted; Readme.md is the index
|
|
144
144
|
│ └── backlog.md # Tracked work items by priority (P0-P3)
|
|
145
145
|
├── memory/ # index.md, main.md, {agent}.md — project knowledge + per-agent strategy memory
|
|
146
|
-
└── tests/ #
|
|
146
|
+
└── tests/ # Behavioral test checklists ({feature}.md, step → **Assert**), derived at plan Completion via the `documentation` skill; author-maintained, present only when test docs exist
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
There is **no `docs/routing.md`**, **no `docs/modules/{m}/`**, **no `docs/pillars/`**, **no `docs/boundaries/`**. Code ownership lives in feature `owns:` frontmatter; a module is a `module:` label, not a folder; boundaries are diagnosed from source; brainstorm records live in `docs/research/` (active) and `docs/history/brainstorms/` (harvested).
|
|
@@ -152,7 +152,7 @@ There is **no `docs/routing.md`**, **no `docs/modules/{m}/`**, **no `docs/pillar
|
|
|
152
152
|
|
|
153
153
|
### Step B1 — Inventory and classify
|
|
154
154
|
|
|
155
|
-
Glob every markdown file. Classify each: Architecture, Features, Operations, Plans, Changelogs, Backlog, Brainstorm records (stay), Stale/Deprecated (delete), Root docs (stay). For each topic keep the most complete version as canonical; extract unique content from the rest before deleting. **No redirect stubs** — when you move a file, delete the original and fix every reference (cross-doc links, memory files). Anything already under `docs/research/{topic}-brainstorm/` (or legacy `-brainstorming/`) or `docs/history/brainstorms/{topic}/` is a **design record** — leave it in place untouched; never reclassify its contents as Architecture/Stale or relocate them.
|
|
155
|
+
Glob every markdown file. Classify each: Architecture, Features, Operations, Tests (behavioral checklists — stay), Plans, Changelogs, Backlog, Brainstorm records (stay), Stale/Deprecated (delete), Root docs (stay). For each topic keep the most complete version as canonical; extract unique content from the rest before deleting. **No redirect stubs** — when you move a file, delete the original and fix every reference (cross-doc links, memory files). Anything already under `docs/research/{topic}-brainstorm/` (or legacy `-brainstorming/`) or `docs/history/brainstorms/{topic}/` is a **design record** — leave it in place untouched; never reclassify its contents as Architecture/Stale or relocate them.
|
|
156
156
|
|
|
157
157
|
While reading each markdown doc to classify it, if the doc names a code symbol it asserts exists that **statically resolves to nothing**, note it as an observation for the Part A audit — **only** on stacks where static resolution is reliable (stay silent on codegen/reflection/DI/barrel stacks); scoped to docs already being read here, never a whole-repo prose sweep; **docs only**, never code-file comments.
|
|
158
158
|
|
|
@@ -11,7 +11,8 @@ const RULES = [
|
|
|
11
11
|
{ id: 'progress-marker', severity: 'block', scope: 'truth', pattern: /\bphases?\s+\d[^.\n]{0,40}\b(complete|done)\b|\b(build|implementation) (is )?(\d+%|partially) (complete|done)\b/i, message: 'progress marker — progress lives in the plan/backlog record' },
|
|
12
12
|
{ id: 'temporal-hedge', severity: 'warn', scope: 'truth', pattern: /\bnot yet\b|\bfor now\b|\bcurrently\b|\bstill (unsupported|missing|pending|unimplemented|in progress)\b/i, message: 'temporal hedge — state the fact without "currently / not yet / for now"' },
|
|
13
13
|
{ id: 'predecessor-frame', severity: 'warn', scope: 'truth', pattern: /\bpreviously\b|\bthe old \w+\b|\blegacy \w+ (was|is)\b/i, message: 'predecessor framing — describe the one current design' },
|
|
14
|
-
|
|
14
|
+
// docs/tests/ behavioral checklists use `- [ ]` as test-step notation (step → **Assert**), not progress tracking — exempt that one folder from this rule; every other truth doc still bans checkboxes.
|
|
15
|
+
{ id: 'status-glyph', severity: 'warn', scope: 'truth', exempt: /(^|\/)docs\/tests\//, pattern: /(^|\s)(✅|⏳|- \[[ x]\])/u, message: 'status glyph / checklist in a truth doc — tracking belongs in a record' },
|
|
15
16
|
{ id: 'date-in-truth-doc', severity: 'warn', scope: 'truth', pattern: /\b20\d{2}-\d{2}-\d{2}\b/, message: 'date in a truth doc — only the AD Decision Changelog and structure.md Health Log tails carry dates' },
|
|
16
17
|
];
|
|
17
18
|
|
|
@@ -71,6 +72,7 @@ function lintFile(path, findings) {
|
|
|
71
72
|
|
|
72
73
|
for (const rule of RULES) {
|
|
73
74
|
if (rule.scope !== scope || disabled.has(rule.id)) continue;
|
|
75
|
+
if (rule.exempt && rule.exempt.test(path)) continue;
|
|
74
76
|
for (const { n, line } of lines) {
|
|
75
77
|
if (!rule.pattern.test(line)) continue;
|
|
76
78
|
if (line.includes(`doc-lint-disable: ${rule.id}`) || new RegExp(`doc-lint-disable:[^>]*\\b${rule.id}\\b`).test(line)) continue;
|
|
@@ -6,5 +6,14 @@
|
|
|
6
6
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
7
7
|
"editor.formatOnSave": true,
|
|
8
8
|
"editor.wordWrap": "on"
|
|
9
|
+
},
|
|
10
|
+
// Hide harness-authoring scaffolding from the file explorer
|
|
11
|
+
"files.exclude": {
|
|
12
|
+
".claude": true,
|
|
13
|
+
".vscode": true,
|
|
14
|
+
".agents": true,
|
|
15
|
+
".codex": true,
|
|
16
|
+
"**/node_modules": true,
|
|
17
|
+
"**/apollo.service.json": true
|
|
9
18
|
}
|
|
10
19
|
}
|
|
@@ -6,5 +6,14 @@
|
|
|
6
6
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
7
7
|
"editor.formatOnSave": true,
|
|
8
8
|
"editor.wordWrap": "on"
|
|
9
|
+
},
|
|
10
|
+
// Hide harness-authoring scaffolding from the file explorer
|
|
11
|
+
"files.exclude": {
|
|
12
|
+
".claude": true,
|
|
13
|
+
".vscode": true,
|
|
14
|
+
".agents": true,
|
|
15
|
+
".codex": true,
|
|
16
|
+
"**/node_modules": true,
|
|
17
|
+
"**/apollo.service.json": true
|
|
9
18
|
}
|
|
10
19
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nurix/etna",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.24",
|
|
4
4
|
"description": "Bootstrap a project with a Nurix harness — copies the harness's CLAUDE.md, skills, agents, rules, and hooks directly into the project from a bundled payload (no marketplace fetch, no network).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|