@isonimus/stele 0.1.2

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.
@@ -0,0 +1,43 @@
1
+ ---
2
+ description: Scaffold a new Architecture Decision Record with frontmatter pre-filled
3
+ argument-hint: <short title of the decision>
4
+ ---
5
+
6
+ Create a new ADR recording a decision that later work must obey — a mechanism, data
7
+ format, or boundary. An ADR asserts *"on date X we chose Y because Z"*: a historical
8
+ claim that stays true forever (ADR-0001). If you are recording a **feature work-unit**
9
+ rather than a durable decision, use `/slice` instead.
10
+
11
+ Steps:
12
+
13
+ 1. Compute the next free id: the highest ordinal in `adr/` plus one, zero-padded to four
14
+ digits. Do not reuse or renumber.
15
+ 2. Choose the `type`: `architecture` for a durable decision, `slice` for a feature unit,
16
+ `batch` only if it genuinely bundles several unrelated decisions that cannot map 1:1.
17
+ 3. Write `adr/NNNN-<kebab-title>.md` opening with the ADR-0002 frontmatter block:
18
+
19
+ ```
20
+ ---
21
+ id: 'NNNN'
22
+ title: "<title>"
23
+ type: architecture
24
+ status: accepted
25
+ date: <today, YYYY-MM-DD>
26
+ supersedes: []
27
+ superseded_by: []
28
+ ---
29
+ ```
30
+
31
+ 4. Body sections: `## Context` (the forces and the observed problem — cite data, not
32
+ estimates), `## Decision`, `## Consequences`.
33
+ 5. If this decision **supersedes** an existing ADR: add its id to this ADR's `supersedes`,
34
+ set the old ADR's `status: superseded` and add this id to its `superseded_by`, and in
35
+ the body state *why the old reasoning was wrong* — that record is the point. Both
36
+ directions must match or rule 4 fails. Never edit the old ADR's prose body; only its
37
+ status/supersession fields may change.
38
+ 6. If the decision defers something, record it as one line in `LEDGER.md` citing this ADR.
39
+ Do not track it anywhere else.
40
+ 7. Regenerate the index (`npm run index`) and run the linter (`npm run lint`) — both must
41
+ be green before you are done.
42
+
43
+ Title argument: $ARGUMENTS
@@ -0,0 +1,25 @@
1
+ ---
2
+ description: Full-corpus health check — run every invariant and surface warnings and drift
3
+ argument-hint: [repo-root]
4
+ ---
5
+
6
+ Run a full audit of the ADR corpus and report its health. Unlike `/wrap-up` (a quick gate
7
+ on the current change), this looks at the whole corpus and reports advisory findings the
8
+ build tolerates, so the operator can decide what to promote or clean up.
9
+
10
+ 1. `npm run lint` — report every error (must be zero) and every warning. Rule 9 warnings
11
+ (bare `ADR NNNN` prose references that do not resolve) are expected on a legacy corpus;
12
+ list them so a genuine typo can be told from a deliberate cross-repo reference.
13
+
14
+ 2. `npm run index -- --check` — confirm `adr/INDEX.md` is current. If stale, regenerate.
15
+
16
+ 3. `npm test` — the invariant checks are themselves tested; the suite must pass.
17
+
18
+ 4. Cross-check the ledger against reality: for each `LEDGER.md` open item, confirm the ADR
19
+ it cites still exists (rule 8 covers this) and the deferral is genuinely still open. A
20
+ closed item that was never deleted is the dual-write failure this method exists to fix.
21
+
22
+ 5. Summarise: error count, warning count, index freshness, and any ledger items that look
23
+ resolved-but-undeleted. Recommend concrete next actions; do not fix silently.
24
+
25
+ Repo root argument (default: current repo): $ARGUMENTS
@@ -0,0 +1,105 @@
1
+ ---
2
+ description: Install the method kit (CLAUDE.md, LEDGER.md, linter, index, pre-commit hook) into a git repo
3
+ argument-hint: [repo-root] [--check | --update]
4
+ ---
5
+
6
+ Install this kit into a repo, or verify an existing install (ADR-0006). The script does
7
+ the mechanical half; you do the two halves that need judgement — filling the scaffolded
8
+ `CLAUDE.md`, and wiring verify scripts.
9
+
10
+ Target repo: `$ARGUMENTS` (default: the current repo root).
11
+
12
+ ## 1. Look before writing
13
+
14
+ Run the dry run first and read what it plans:
15
+
16
+ ```
17
+ node scripts/init-method.mjs <target>
18
+ ```
19
+
20
+ It writes nothing. `WOULD` lines are the plan, `KEEP` means a file already exists and
21
+ will be left alone, `PROBLEM` means something needs a decision. Report the plan to the
22
+ operator before applying it if anything is being scaffolded over a repo that already has
23
+ conventions of its own.
24
+
25
+ If the target is not a git repository the run refuses. Offer `git init`; do not work
26
+ around it — the hook has nowhere to live.
27
+
28
+ ## 2. Apply
29
+
30
+ ```
31
+ node scripts/init-method.mjs <target> --apply
32
+ ```
33
+
34
+ This scaffolds `adr/`, `CLAUDE.md` and `LEDGER.md` (never overwriting), vendors
35
+ `lint-docs.mjs` / `build-index.mjs` / the hook and the slash commands, generates
36
+ `adr/INDEX.md`, links `~/.claude/CLAUDE.md`, and installs the pre-commit hook **only if
37
+ the corpus lints clean**.
38
+
39
+ The commands are vendored under softer rules than the machinery (ADR-0007): a repo may
40
+ edit its own copy of `/slice` or `/wrap-up` to say something repo-specific, and an
41
+ install keeps that edit rather than overwriting it. If you edit one, say so — an edit
42
+ made in an installed repo does not travel back to the toolkit.
43
+
44
+ ## 3. If the repo already has a hook framework
45
+
46
+ A target with a `.pre-commit-config.yaml` gets the doc checks **composed into it** as a
47
+ `repo: local` block rather than a symlink (ADR-0008) — the framework owns
48
+ `.git/hooks/pre-commit`, and a symlink there is silently erased by the next
49
+ `pre-commit install`. The append is idempotent and additive; the existing config is
50
+ never reordered or rewritten, and `--update` leaves it alone.
51
+
52
+ Two things to watch:
53
+
54
+ - A config the script does not recognise (no top-level `repos:`) is **refused**, not
55
+ guessed at. Add the block by hand and say you did.
56
+ - `--check` reports a problem when the framework is configured but never installed. That
57
+ is not our hook failing — it means *no* hook runs, including the repo's own ruff and
58
+ mypy. Tell the operator to run `pre-commit install`; do not paper over it by
59
+ symlinking ours instead.
60
+
61
+ ## 4. If it refused the hook
62
+
63
+ A refusal is normal on a repo that already has work in it, and the reason is almost
64
+ always **rule 11: a `scripts/*-verify.mjs` that no `package.json` command runs**. An
65
+ unwired verify script ran once, the day it was written; the linter treats that as an
66
+ error, so the corpus is red and a hook would block every commit.
67
+
68
+ Fix it, in this order — the order is the whole point:
69
+
70
+ 1. Add one `"verify:<name>": "node scripts/<name>-verify.mjs"` per unwired script. Use
71
+ the name a human would recognise, not the filename verbatim. A `verify:all` chaining
72
+ the headless ones is worth adding; leave out any script that needs a human watching
73
+ or listening, and say so in a comment or in `CLAUDE.md`.
74
+ 2. Fix any other lint errors (dangling supersessions, bad frontmatter). Never silence a
75
+ rule to get green.
76
+ 3. Re-run `--apply`. The hook installs.
77
+
78
+ Do **not** edit the target's `package.json` without telling the operator what you added.
79
+
80
+ ## 5. Finish the scaffold
81
+
82
+ If `CLAUDE.md` was newly scaffolded it contains `{{PLACEHOLDER}}` fields. Fill them from
83
+ the repo itself — read the README, the manifest, the source layout. Do not invent a
84
+ project description. Then delete the sections that describe machinery the repo does not
85
+ have: a verification-harness section in a repo with no `scripts/` states rules about
86
+ files that do not exist, which makes the file false on arrival.
87
+
88
+ ## 6. Verify, and say what happened
89
+
90
+ ```
91
+ node scripts/init-method.mjs <target> --check
92
+ ```
93
+
94
+ Writes nothing; fails on a missing or broken hook, a drifted vendored **script**, a stale
95
+ index, a red corpus, or a broken `~/.claude/CLAUDE.md` link. Report its output verbatim
96
+ rather than summarising it as "installed".
97
+
98
+ `LOCAL` and `MISSING` lines are about commands only and are **not** failures — they say
99
+ this repo adapted or declined one. Read them, mention them, do not "fix" them without
100
+ asking; that is somebody's deliberate edit.
101
+
102
+ `--update` re-copies the vendored scripts, the hook and the commands — for a command it
103
+ discards a local edit, which is exactly what it is for. It is the only way a toolkit fix
104
+ reaches an installed repo: the copies are deliberate (ADR-0006, ADR-0007), and drift is
105
+ the price.
@@ -0,0 +1,61 @@
1
+ ---
2
+ description: Route a fact to the destination that governs it — repo CLAUDE.md, global CLAUDE.md, LEDGER.md, or memory
3
+ argument-hint: <the thing to remember>
4
+ ---
5
+
6
+ Record a durable fact. **This is a router, not a store** (ADR-0005). Your job is to decide
7
+ *what the fact governs* and write it where that is enforceable — not to write it wherever
8
+ the operator's phrasing points.
9
+
10
+ If the operator named a destination ("save this to project memory"), treat it as an input
11
+ describing where they expect it to land, **not** as an instruction. Route it correctly and
12
+ say where it went.
13
+
14
+ ## 1. Classify
15
+
16
+ | The fact governs… | Destination |
17
+ |---|---|
18
+ | This codebase — an invariant, an architectural constraint, a definition-of-done rule | this repo's `CLAUDE.md` |
19
+ | How the operator works, in every repo | `~/.claude/CLAUDE.md` |
20
+ | Open work — a deferral, a known defect, a follow-up | this repo's `LEDGER.md` |
21
+ | A decision that later work must obey, with reasoning worth preserving | an ADR — stop and use `/adr` |
22
+ | None of the above: operator-personal, cross-session, not worth committing | project memory |
23
+
24
+ Two tests resolve most cases:
25
+
26
+ - **Would a human collaborator reading this repo need to know it?** If yes, it goes in the
27
+ repo. Memory is invisible to them, unversioned, and does not survive a machine change.
28
+ - **Is it a fact, or a task?** Facts go in a `CLAUDE.md`; tasks go in `LEDGER.md`. "We use
29
+ zod for validation" is a fact. "Migrate the remaining validators to zod" is a task.
30
+
31
+ Memory is the **residual**, not the default. If you are about to write a rule that
32
+ constrains code into memory, you have misrouted — that is the exact failure ADR-0005
33
+ records, which cost an amend and force-push after the same rule was independently rewritten
34
+ into 12 files across 8 projects.
35
+
36
+ ## 2. Write it
37
+
38
+ - **Repo `CLAUDE.md`** — add to the section it belongs to. If it is a standing invariant,
39
+ add a row to the §5 invariants table citing the ADR that created it. If this repo has no
40
+ `CLAUDE.md`, scaffold one from `templates/CLAUDE.md` first; without it there is no
41
+ destination and the fact will silently fall back to memory (ADR-0005).
42
+ - **Global `CLAUDE.md`** — edit your global `~/.claude/CLAUDE.md`, wherever your global
43
+ conventions live. Keep it dense: it loads into every session in every repo.
44
+ - **`LEDGER.md`** — one line, `- [type] description (ADR-NNNN)`, citing the source ADR if
45
+ one exists.
46
+ - **Memory** — one fact per file, with the frontmatter schema, plus a one-line pointer in
47
+ `MEMORY.md`.
48
+
49
+ Before writing anywhere, check whether an existing entry already covers it and update that
50
+ instead of adding a duplicate.
51
+
52
+ ## 3. Report
53
+
54
+ State which destination it went to and why in one line — e.g. *"→ repo `CLAUDE.md` §3: it
55
+ constrains code, so it needs to be greppable and reviewable."*
56
+
57
+ This step is not optional. Routing cannot be linted (ADR-0005), so saying the decision out
58
+ loud is the only thing that makes a misroute correctable in the moment rather than
59
+ discoverable in a survey weeks later.
60
+
61
+ Fact to record: $ARGUMENTS
@@ -0,0 +1,72 @@
1
+ ---
2
+ description: Scaffold a new slice (one feature work-unit) with frontmatter pre-filled
3
+ argument-hint: <short title of the feature>
4
+ ---
5
+
6
+ Create a new **slice**: one feature work-unit, written *before* implementation and frozen
7
+ at merge (ADR-0001). A slice is a current-state claim while open, so it goes stale by
8
+ nature — freezing it to past tense at merge ("this is what shipped") converts it into a
9
+ historical claim that never goes stale. If you are recording a durable decision rather
10
+ than a unit of feature work, use `/adr` instead.
11
+
12
+ Steps:
13
+
14
+ 1. Compute the next free id: highest ordinal in `adr/` (and `slices/` if present) plus
15
+ one, zero-padded to four digits.
16
+ 2. Scan `LEDGER.md` for open items this slice touches — a `[feature]` it delivers, a
17
+ `[bug]` it fixes, a `[deferred]` it finally picks up. Fold each relevant one into
18
+ `## Goal` so the slice knows the history it is answering, and **delete the line from the
19
+ ledger for any item this slice closes** — closing an item means deleting its line (see
20
+ the ledger's own header). If nothing relates, note nothing; this is a read, not a
21
+ requirement to invent links.
22
+ 3. Write the file opening with the ADR-0002 frontmatter block, `type: slice`:
23
+
24
+ ```
25
+ ---
26
+ id: 'NNNN'
27
+ title: "<title>"
28
+ type: slice
29
+ status: accepted
30
+ date: <today, YYYY-MM-DD>
31
+ supersedes: []
32
+ superseded_by: []
33
+ ---
34
+ ```
35
+
36
+ 4. Body sections, written before you code:
37
+ - `## Goal` — what ships and why, in one paragraph. Fold in any ledger items from step 2.
38
+ - `## Definition of Done` — **required.** The acceptance criteria as one or more
39
+ **Given / When / Then** scenarios, written before you code (ADR-0011). This is the
40
+ grammar, not a toolchain: plain markdown steps, no Cucumber. State the observable
41
+ outcome — a criterion with no observable "Then" is not done-able. Each scenario's
42
+ proof is named below in `## Verification`; a scenario with no proof is an unmet
43
+ criterion, not a finished one. Example:
44
+
45
+ ```
46
+ ## Definition of Done
47
+
48
+ - **Given** a fresh world
49
+ - **When** the player places the block
50
+ - **Then** it renders with its mesher shape, a hotbar icon, and a hand-held model
51
+ ```
52
+
53
+ - `## Design` — the approach; cite measured numbers from a probe where a design
54
+ question has a measurable answer, not estimates. State **what existing code was
55
+ considered**: what you searched for, what you reused, and what you deliberately did
56
+ *not* reuse and why (ADR-0013). This is not a mandate to reuse — the wrong abstraction
57
+ costs more than a duplication (global §3, rule of three); it makes the
58
+ reuse-or-duplicate choice *visible*, so an un-considered duplication is caught at
59
+ review. It is checked by human read-through, not the linter: whether you truly searched
60
+ is the coverage question no machine decides.
61
+ - `## Verification` — **required.** Name the unit tests, and if the behaviour cannot be
62
+ asserted in a unit test (rendering, worldgen, physics, timing), name the
63
+ `scripts/<slice>-verify.mjs` script and confirm it is wired into `package.json`
64
+ (ADR-0004). A slice with no `## Verification` section is incomplete. The linter
65
+ enforces both required sections (R12/R13): a `type: slice` must carry `## Verification`
66
+ and a `## Definition of Done` holding at least one full Given/When/Then triad.
67
+ - `## As built` — filled in at merge: what actually shipped, in past tense, confirming
68
+ each Definition-of-Done scenario was met or recording the deviation. This is the
69
+ freeze step.
70
+ 5. Regenerate the index and run the linter; both green before done.
71
+
72
+ Title argument: $ARGUMENTS
@@ -0,0 +1,30 @@
1
+ ---
2
+ description: End-of-task gate — run the checks and ask the four questions that get forgotten
3
+ ---
4
+
5
+ Run before finishing a task. The point is to make mechanical what memory keeps dropping:
6
+ the linter catches structural drift, and four questions catch the follow-ups that never
7
+ get recorded until they have gone stale (ADR-0003).
8
+
9
+ 1. Run the linter and the index check:
10
+ - `npm run lint` — must be green (rule 9 warnings are acceptable on a legacy corpus).
11
+ - `npm run index` — regenerate `adr/INDEX.md` and stage it if it changed. A stale
12
+ generated index is drift by another name.
13
+ - `npm test` — the test suite must pass; docs changes must not disturb code.
14
+
15
+ 2. Then answer these four out loud, and act on each:
16
+ - **Did this change a user- or dev-facing API or feature?** If so, update `README.md`
17
+ (and any docs) in the same change — it is a live document.
18
+ - **Did this make a decision later work must obey?** If so, write it with `/adr` now,
19
+ while the reasoning is fresh. An unrecorded decision is re-litigated later from
20
+ nobody's memory.
21
+ - **Did this defer something** — a follow-up, a known-but-unfixed bug, a TODO? If so,
22
+ add one line to `LEDGER.md` citing the relevant ADR. The ledger is the only place
23
+ deferrals live; a TODO in code or a note in your head is not tracked.
24
+ - **Did this write code a later operator would plausibly try to "fix"?** — a deliberate
25
+ deviation, a non-obvious constraint, a hard-won exception. If so, cite the governing
26
+ ADR at that site in a comment (ADR-0012), so the choice announces it is on purpose
27
+ where the edit happens, not only in the §4 table nobody thinks to open.
28
+
29
+ 3. Report what you found and did for each of the four, so the operator can confirm
30
+ nothing was silently skipped.
@@ -0,0 +1,29 @@
1
+ #!/bin/sh
2
+ # Stele pre-commit hook — the load-bearing enforcement decision (ADR-0003).
3
+ #
4
+ # A linter nobody runs is another convention resting on memory, which is the failure this
5
+ # project exists to fix. This hook makes the checks mechanical: no green corpus, no commit.
6
+ # CI (.github/workflows/docs.yml) is the backstop for clones without the hook installed.
7
+ #
8
+ # Install: ln -sf ../../.claude/hooks/pre-commit .git/hooks/pre-commit
9
+ # (/init-method does this; see ADR-0003. Do NOT install on a linter-red corpus — fix the
10
+ # reds first, or every commit is blocked. See LEDGER.)
11
+ #
12
+ # Bypass in a genuine emergency with `git commit --no-verify`; CI still catches it.
13
+
14
+ set -e
15
+
16
+ root=$(git rev-parse --show-toplevel)
17
+ cd "$root"
18
+
19
+ if ! command -v node >/dev/null 2>&1; then
20
+ echo "pre-commit: node not found on PATH — cannot run doc checks." >&2
21
+ exit 1
22
+ fi
23
+
24
+ # 1. Invariants must hold (rules 1–8 block; rule 9 warns). Zero-dependency, so no install.
25
+ node scripts/lint-docs.mjs .
26
+
27
+ # 2. The generated index must match the corpus. A stale INDEX.md is drift by another name;
28
+ # regenerate and stage it rather than letting it fall behind.
29
+ node scripts/build-index.mjs --check .
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Isonimus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,206 @@
1
+ # Stele
2
+
3
+ A zero-dependency workflow that keeps a codebase's **decisions, work, and definition of
4
+ done** honest — enforced by a git hook and CI, not by anyone remembering to.
5
+
6
+ It is meant to be **vendored into any git repo**: one command drops in a decision-record
7
+ tree, a worklist, a linter, and a pre-commit hook, and from then on the repo refuses commits
8
+ that let its own records rot.
9
+
10
+ > This README is a **live doc** — the *how-to* and the vision. The *why* behind every rule
11
+ > lives in [`adr/`](adr/), and each section below links to the decision that governs it.
12
+ > Where the two ever disagree, the ADR wins; tell us so we can fix the README.
13
+
14
+ ---
15
+
16
+ ## The problem
17
+
18
+ Conventions kept in a model's memory, a wiki, or someone's head hold until the first busy
19
+ afternoon. They are invisible in review, unversioned, and gone on a change of machine. Three
20
+ failures recur:
21
+
22
+ - **Decisions drift.** A choice recorded as prose gets quietly edited until the record no
23
+ longer says what was actually decided, and the reasoning that made it right is lost.
24
+ - **Work-in-progress lies.** A doc written in the present tense ("the system does X") is
25
+ true the day it is written and slowly false forever after.
26
+ - **"Done" is assumed, not defined.** A feature with three required outputs and no written
27
+ acceptance criteria ships with two of them, because the intent was never in a form that
28
+ made the omission visible.
29
+
30
+ Stele's answer is a single principle: **if a convention matters, it is executable;
31
+ if it genuinely cannot be checked, that limit is stated out loud rather than trusted.**
32
+
33
+ ---
34
+
35
+ ## The mental model — four kinds of document
36
+
37
+ Every document is exactly one kind. There is no fifth. ([ADR-0010](adr/0010-live-docs-are-the-fourth-kind.md), superseding [ADR-0001](adr/0001-immutable-or-generated.md))
38
+
39
+ | Kind | Files | Rule |
40
+ |---|---|---|
41
+ | **Immutable** | `adr/*.md`, `slices/*.md` | Written once. Body prose is never edited — only status/supersession fields change. |
42
+ | **Generated** | `adr/INDEX.md` | Built from frontmatter by script. Never hand-edited. |
43
+ | **Mutable** | `LEDGER.md` | Exactly one per repo. The only file maintained by hand. |
44
+ | **Live** | `README.md`, `docs/*` | Describes how something behaves *now*; updated in the same change as the code it describes. |
45
+
46
+ - An **ADR** records a decision later work must obey: *"on date X we chose Y because Z"* — a
47
+ historical claim, true forever.
48
+ - A **slice** is one feature work-unit, written *before* implementation and **frozen to past
49
+ tense at merge** ("this is what shipped") — which converts a going-stale claim into a
50
+ never-stale one.
51
+
52
+ **Single writer, one direction.** An ADR records a deferral once, as a fact. `LEDGER.md`
53
+ cites the ADR. You never reach back into an ADR to close a ledger item — **closing an item
54
+ means deleting its line from the ledger.** Changing your mind means a **new** ADR that
55
+ supersedes the old one and says *why the old reasoning was wrong* — that record is the most
56
+ valuable thing this workflow produces, and an in-place edit destroys it.
57
+
58
+ ---
59
+
60
+ ## Quickstart
61
+
62
+ Install into a git repo — dry-run first, always ([ADR-0006](adr/0006-init-method-bootstrap.md)).
63
+ Stele is delivered by `npx`, then **vendored** into the repo; it is never a runtime
64
+ dependency ([ADR-0015](adr/0015-distribution-by-npx-delivered-vendoring.md)):
65
+
66
+ ```
67
+ npx @isonimus/stele <repo-root> # dry run: shows what it would do
68
+ npx @isonimus/stele <repo-root> --apply # install the kit
69
+ npx @isonimus/stele <repo-root> --check # verify an install is intact
70
+ npx @isonimus/stele <repo-root> --update # re-sync vendored machinery
71
+ ```
72
+
73
+ From a clone of this repo, the same entry point runs directly:
74
+
75
+ ```
76
+ node scripts/init-method.mjs <repo-root> --apply
77
+ ```
78
+
79
+ It installs `CLAUDE.md`, `LEDGER.md`, the linter, the index builder, and a pre-commit hook —
80
+ and **refuses to install the hook on a linter-red corpus**, because a hook that blocks every
81
+ commit is the tool bricking the repo it was meant to protect. If a hook framework already
82
+ owns the pre-commit slot, the doc checks join it rather than fight for the file
83
+ ([ADR-0008](adr/0008-compose-with-an-existing-hook-framework.md)).
84
+
85
+ The linter and slash commands are **vendored per repo** and a repo's local edits to the
86
+ commands survive re-runs ([ADR-0007](adr/0007-commands-are-vendored-and-adaptable.md)).
87
+
88
+ ---
89
+
90
+ ## Commands
91
+
92
+ Run in Claude Code as `/<name>`.
93
+
94
+ | Command | What it does |
95
+ |---|---|
96
+ | `/adr <title>` | Scaffold a new Architecture Decision Record, frontmatter pre-filled. |
97
+ | `/slice <title>` | Scaffold a new slice (one feature work-unit). |
98
+ | `/audit` | Full-corpus health check — run every invariant, surface warnings and drift. |
99
+ | `/wrap-up` | End-of-task gate — run the checks and ask the four questions that get forgotten. |
100
+ | `/remember <fact>` | Route a fact to the destination that governs it (see [Where things live](#where-things-live)). |
101
+ | `/init-method` | Install the kit into a git repo. |
102
+
103
+ Under the hood, the npm scripts are the enforcement surface:
104
+
105
+ ```
106
+ npm run lint # node scripts/lint-docs.mjs . — the invariant checker
107
+ npm run index # regenerate adr/INDEX.md
108
+ npm test # the regression suite (every rule has a fixture)
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Scenarios
114
+
115
+ **Record a decision.** `/adr "Use a hand-rolled frontmatter parser"`. Fill Context /
116
+ Decision / Consequences, cite measured numbers where a question has a measurable answer, and
117
+ commit. Never edit it afterward.
118
+
119
+ **A decision changed.** Do *not* edit the old ADR. `/adr` a new one, set `supersedes: [NNNN]`
120
+ on it and `superseded_by` on the old, and spend a paragraph on *why the old reasoning was
121
+ wrong*. The linter enforces that the supersession is bidirectional and that a superseded ADR
122
+ no longer reads as "accepted" (rules R4–R7).
123
+
124
+ **Start a feature.** `/slice "CSV export"`. Before you write code, fill:
125
+ - `## Goal` — what ships and why;
126
+ - `## Definition of Done` — the acceptance criteria as **Given / When / Then** scenarios
127
+ ([ADR-0011](adr/0011-slices-carry-a-definition-of-done.md));
128
+ - `## Design` — the approach, citing probe numbers not estimates;
129
+ - `## Verification` — the unit tests, or a `scripts/<slice>-verify.mjs` for behaviour a unit
130
+ test can't assert.
131
+
132
+ At merge, freeze `## As built` in past tense, confirming each Definition-of-Done scenario was
133
+ met. The linter requires both `## Verification` (R12) and a `## Definition of Done` holding a
134
+ real Given/When/Then triad (R13).
135
+
136
+ **Behaviour a unit test can't assert** (rendering, worldgen, physics, timing). Write
137
+ `scripts/<slice>-verify.mjs` that drives the real system headlessly, **fails on any console
138
+ error**, and writes artifacts (screenshots, numbers) for human review. Name it in
139
+ `## Verification` and **wire it into `package.json`** — an unwired verify script runs once and
140
+ is dead thereafter, so the linter fails if any is unwired (R11). ([ADR-0004](adr/0004-verification-harness-and-in-repo-invariants.md))
141
+
142
+ **Adopt into an existing repo.** `/init-method <repo> --apply`. Migrate the corpus and hand-
143
+ fix any red supersession pairs *before* the hook goes on. Legacy documents that predate a
144
+ rule warn rather than error, so adoption is never blocked by history.
145
+
146
+ **A rule you must break, with reason.** Don't take a silent exception. Record the
147
+ justification as a new or superseding ADR — in this workflow, a justified violation *is* a
148
+ decision, and decisions are immutable records.
149
+
150
+ ---
151
+
152
+ ## Enforcement, in three honest layers
153
+
154
+ The linter ([`scripts/lint-docs.mjs`](scripts/lint-docs.mjs)) runs from the pre-commit hook
155
+ and in CI. Its rules are graded by what can actually be mechanised
156
+ ([ADR-0003](adr/0003-enforcement-by-hook.md)):
157
+
158
+ 1. **Machine-checked (error — blocks the commit).** Frontmatter shape and completeness
159
+ (R1), id/filename agreement and uniqueness (R2), closed status/type vocabulary (R3), the
160
+ supersession graph (R4–R7), ledger citations resolve (R8), the linter isn't pointed at an
161
+ empty corpus (R10), verify scripts are wired (R11), and slices carry their required
162
+ sections (R12/R13).
163
+ 2. **Legacy-aware (warning, not error).** Bare prose cross-references (R9) and slice-section
164
+ rules on documents that predate them warn instead of failing, so a repo's history never
165
+ blocks its next commit — while *new* work is held to the full bar.
166
+ 3. **Coverage — unenforceable, and said so.** Whether a verify script tests something *true*,
167
+ whether a §4 invariant actually holds, whether the acceptance scenarios are *complete* —
168
+ none can be decided by reading one version of a file. These are surfaced by `/wrap-up` for
169
+ a human read-through, never claimed as guaranteed. A linter that pretended to check them
170
+ would be a false green, the exact failure this project exists to prevent.
171
+
172
+ The dividing line is the whole point: **a rule is enforced, legacy-tolerated, or explicitly
173
+ declared uncheckable — never silently trusted.**
174
+
175
+ ---
176
+
177
+ ## Where things live
178
+
179
+ `/remember` routes a fact to whatever *governs* it, never to wherever the conversation
180
+ happened ([ADR-0005](adr/0005-write-routing-and-the-bounds-of-memory.md)):
181
+
182
+ | The fact governs… | Goes in |
183
+ |---|---|
184
+ | A codebase — invariants, architecture, definition-of-done | that repo's `CLAUDE.md` |
185
+ | How you work, everywhere | global `~/.claude/CLAUDE.md` |
186
+ | Open work — deferrals, defects, follow-ups | that repo's `LEDGER.md` |
187
+ | Operator-personal, cross-session facts (e.g. git identity) | assistant memory |
188
+
189
+ A rule that governs a codebase never belongs in assistant memory: memory is invisible to
190
+ every other reader of the repo, unversioned, and lost on a change of machine.
191
+
192
+ ---
193
+
194
+ ## Design principles
195
+
196
+ - **Zero dependencies.** The whole kit is Node's standard library. It drops into any repo
197
+ regardless of package manager, and the frontmatter schema is small enough to parse by hand.
198
+ - **Enforce with a hook, remember nothing.** A convention that matters becomes a rule; one
199
+ that can't be checked is stated as such, out loud.
200
+ - **Grammar over toolchain.** Given/When/Then is adopted as *writing discipline*, not a test
201
+ framework — the verify scripts are the executable layer.
202
+ - **The diff is the audit trail.** Immutable records, generated indexes, and single-writer
203
+ ledgers mean the git log *is* the history — no hand-maintained changelog to drift.
204
+
205
+ For the reasoning behind any of these, read the ADR it links to. That is what the ADRs are
206
+ for.
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@isonimus/stele",
3
+ "version": "0.1.2",
4
+ "type": "module",
5
+ "description": "A linted, installable ADR workflow for git projects using Claude as an assistant",
6
+ "keywords": [
7
+ "adr",
8
+ "architecture-decision-record",
9
+ "claude",
10
+ "pre-commit",
11
+ "documentation",
12
+ "workflow",
13
+ "linter"
14
+ ],
15
+ "author": "Iker Laforga",
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+ssh://git@github.com/Isonimus/stele.git"
20
+ },
21
+ "homepage": "https://github.com/Isonimus/stele#readme",
22
+ "bugs": "https://github.com/Isonimus/stele/issues",
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "bin": {
27
+ "stele": "scripts/init-method.mjs"
28
+ },
29
+ "files": [
30
+ "scripts/",
31
+ "templates/",
32
+ ".claude/commands/",
33
+ ".claude/hooks/",
34
+ "README.md",
35
+ "LICENSE"
36
+ ],
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "scripts": {
41
+ "test": "node --test test/*.test.mjs",
42
+ "lint": "node scripts/lint-docs.mjs .",
43
+ "index": "node scripts/build-index.mjs .",
44
+ "scan": "node scripts/scan-legacy.mjs",
45
+ "migrate": "node scripts/migrate-adrs.mjs",
46
+ "init": "node scripts/init-method.mjs"
47
+ }
48
+ }