@nurix/etna 0.19.19 → 0.19.21
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/.prettierignore +6 -0
- package/data/harnesses/code/CLAUDE.md +17 -16
- package/data/harnesses/code/agents/maintenance.md +1 -1
- package/data/harnesses/code/codex/agents/maintenance.toml +1 -1
- package/data/harnesses/code/harness.json +2 -2
- package/data/harnesses/code/skills/documentation/SKILL.md +64 -107
- package/data/harnesses/code/skills/documentation/references/examples.md +83 -7
- package/data/harnesses/code/skills/documentation/references/frontmatter.md +44 -0
- package/data/harnesses/code/skills/documentation/references/style-guide.md +44 -19
- package/data/harnesses/code/skills/documentation/references/untemplated.md +38 -0
- package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +117 -0
- package/data/harnesses/code/skills/documentation/templates/Readme.md +31 -0
- package/data/harnesses/code/skills/documentation/templates/api-reference.md +20 -0
- package/data/harnesses/code/skills/documentation/templates/architecture-decision.md +121 -0
- package/data/harnesses/code/skills/documentation/templates/backlog.md +33 -0
- package/data/harnesses/code/skills/documentation/templates/changelog-entry.md +62 -0
- package/data/harnesses/code/skills/documentation/templates/changelog-index.md +41 -0
- package/data/harnesses/code/skills/documentation/templates/docs-readme.md +70 -0
- package/data/harnesses/code/skills/documentation/templates/domain-model.md +30 -0
- package/data/harnesses/code/skills/documentation/templates/environment-registry.md +15 -0
- package/data/harnesses/code/skills/documentation/templates/feature.md +149 -0
- package/data/harnesses/code/skills/documentation/templates/glossary.md +11 -0
- package/data/harnesses/code/skills/documentation/templates/memory-main.md +25 -0
- package/data/harnesses/code/skills/documentation/templates/migration-runbook.md +34 -0
- package/data/harnesses/code/skills/documentation/templates/non-goals.md +11 -0
- package/data/harnesses/code/skills/documentation/templates/operations-procedure.md +36 -0
- package/data/harnesses/code/skills/documentation/templates/plan.md +64 -0
- package/data/harnesses/code/skills/documentation/templates/protected-files.md +37 -0
- package/data/harnesses/code/skills/documentation/templates/root-readme.md +104 -0
- package/data/harnesses/code/skills/documentation/templates/security-overview.md +27 -0
- package/data/harnesses/code/skills/documentation/templates/structure.md +65 -0
- package/data/harnesses/code/skills/documentation/templates/system-overview.md +32 -0
- package/data/harnesses/code/skills/documentation/templates/testing-strategy.md +29 -0
- package/data/harnesses/discover/.prettierignore +6 -0
- package/data/harnesses/discover/CLAUDE.md +13 -12
- package/data/harnesses/discover/harness.json +2 -2
- package/data/harnesses/discover/tooling-recommendations.md +10 -10
- package/data/harnesses/discussion/.prettierignore +6 -0
- package/data/harnesses/discussion/CLAUDE.md +4 -0
- package/data/harnesses/discussion/harness.json +2 -2
- package/data/harnesses/etna/.prettierignore +6 -0
- package/data/harnesses/etna/CLAUDE.md +16 -15
- package/data/harnesses/etna/agents/maintenance.md +1 -1
- package/data/harnesses/etna/codex/agents/maintenance.toml +1 -1
- package/data/harnesses/etna/harness.json +2 -2
- package/data/harnesses/etna/skills/documentation/SKILL.md +64 -107
- package/data/harnesses/etna/skills/documentation/references/examples.md +83 -7
- package/data/harnesses/etna/skills/documentation/references/frontmatter.md +44 -0
- package/data/harnesses/etna/skills/documentation/references/style-guide.md +44 -19
- package/data/harnesses/etna/skills/documentation/references/untemplated.md +38 -0
- package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +117 -0
- package/data/harnesses/etna/skills/documentation/templates/Readme.md +31 -0
- package/data/harnesses/etna/skills/documentation/templates/api-reference.md +20 -0
- package/data/harnesses/etna/skills/documentation/templates/architecture-decision.md +121 -0
- package/data/harnesses/etna/skills/documentation/templates/backlog.md +33 -0
- package/data/harnesses/etna/skills/documentation/templates/changelog-entry.md +62 -0
- package/data/harnesses/etna/skills/documentation/templates/changelog-index.md +41 -0
- package/data/harnesses/etna/skills/documentation/templates/docs-readme.md +70 -0
- package/data/harnesses/etna/skills/documentation/templates/domain-model.md +30 -0
- package/data/harnesses/etna/skills/documentation/templates/environment-registry.md +15 -0
- package/data/harnesses/etna/skills/documentation/templates/feature.md +149 -0
- package/data/harnesses/etna/skills/documentation/templates/glossary.md +11 -0
- package/data/harnesses/etna/skills/documentation/templates/memory-main.md +25 -0
- package/data/harnesses/etna/skills/documentation/templates/migration-runbook.md +34 -0
- package/data/harnesses/etna/skills/documentation/templates/non-goals.md +11 -0
- package/data/harnesses/etna/skills/documentation/templates/operations-procedure.md +36 -0
- package/data/harnesses/etna/skills/documentation/templates/plan.md +64 -0
- package/data/harnesses/etna/skills/documentation/templates/protected-files.md +37 -0
- package/data/harnesses/etna/skills/documentation/templates/root-readme.md +104 -0
- package/data/harnesses/etna/skills/documentation/templates/security-overview.md +27 -0
- package/data/harnesses/etna/skills/documentation/templates/structure.md +65 -0
- package/data/harnesses/etna/skills/documentation/templates/system-overview.md +32 -0
- package/data/harnesses/etna/skills/documentation/templates/testing-strategy.md +29 -0
- package/data/harnesses/hr/.prettierignore +6 -0
- package/data/harnesses/hr/CLAUDE.md +23 -23
- package/data/harnesses/hr/harness.json +2 -2
- package/data/harnesses/kb-agi/.prettierignore +6 -0
- package/data/harnesses/kb-agi/CLAUDE.md +16 -12
- package/data/harnesses/kb-agi/harness.json +2 -2
- package/package.json +1 -1
- package/data/harnesses/code/skills/documentation/references/templates.md +0 -1091
- package/data/harnesses/etna/skills/documentation/references/templates.md +0 -1091
|
@@ -11,10 +11,16 @@ Writing rules for clear, maintainable documentation.
|
|
|
11
11
|
```markdown
|
|
12
12
|
### `functionName(param1, param2, options)`
|
|
13
13
|
|
|
14
|
-
**Goal:** One-line purpose
|
|
14
|
+
**Goal:** One-line purpose
|
|
15
|
+
|
|
16
|
+
**Input:**
|
|
15
17
|
|
|
16
18
|
- `param1` (type) - Description
|
|
17
|
-
- `param2` (type, optional) - Description
|
|
19
|
+
- `param2` (type, optional) - Description
|
|
20
|
+
|
|
21
|
+
**Output:** `{ field1, field2 }`
|
|
22
|
+
|
|
23
|
+
**Side Effects:** State updates, API calls, etc.
|
|
18
24
|
```
|
|
19
25
|
|
|
20
26
|
### Parameter Rules
|
|
@@ -62,34 +68,33 @@ Always specify:
|
|
|
62
68
|
```json
|
|
63
69
|
{
|
|
64
70
|
"activeField": "value"
|
|
65
|
-
// "deprecated": null // UNUSED:
|
|
71
|
+
// "deprecated": null // UNUSED: dead key; kept so old persisted state rehydrates
|
|
66
72
|
}
|
|
67
73
|
```
|
|
68
|
-
````
|
|
69
74
|
|
|
70
75
|
**Storage:** localStorage key `state_key`
|
|
71
76
|
|
|
72
77
|
**Backend:**
|
|
73
78
|
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
id
|
|
77
|
-
data
|
|
78
|
-
// old
|
|
79
|
-
}
|
|
79
|
+
```ts
|
|
80
|
+
export const table = pgTable('table', {
|
|
81
|
+
id: text('id').primaryKey(),
|
|
82
|
+
data: jsonb('data'),
|
|
83
|
+
// old: text('old'), // UNUSED: dead column; nothing reads it
|
|
84
|
+
});
|
|
80
85
|
```
|
|
81
|
-
|
|
82
86
|
````
|
|
83
87
|
|
|
84
88
|
### Mark Unused Keys
|
|
85
89
|
|
|
86
|
-
Always comment deprecated/unused fields:
|
|
90
|
+
Always comment deprecated/unused fields. The annotation states why the field is dead _now_ — never where it went, what replaced it, or when it will be removed (no "Remove in v3", no "Migrated to newField"; that is history narration, § The Two Natures below):
|
|
91
|
+
|
|
87
92
|
```json
|
|
88
93
|
{
|
|
89
94
|
"active": [],
|
|
90
|
-
// "legacy": {} // UNUSED:
|
|
95
|
+
// "legacy": {} // UNUSED: dead key; kept for rehydrate compatibility
|
|
91
96
|
}
|
|
92
|
-
|
|
97
|
+
```
|
|
93
98
|
|
|
94
99
|
---
|
|
95
100
|
|
|
@@ -102,6 +107,8 @@ Always comment deprecated/unused fields:
|
|
|
102
107
|
- Component interactions
|
|
103
108
|
- Process flows
|
|
104
109
|
|
|
110
|
+
**Docs only.** Diagrams belong in documents read on demand (feature docs, ADs, operations). Instruction files — `CLAUDE.md`, rules, agent definitions, `SKILL.md` prose — never carry them: they load on every turn or every invocation, and a table or a sentence carries the same information at a fraction of the tokens.
|
|
111
|
+
|
|
105
112
|
### Pattern
|
|
106
113
|
|
|
107
114
|
```
|
|
@@ -168,6 +175,8 @@ function extract(json) {
|
|
|
168
175
|
|
|
169
176
|
❌ Bad: Full 50-line implementation
|
|
170
177
|
|
|
178
|
+
(Worked example: [`examples.md`](examples.md) Example 1.)
|
|
179
|
+
|
|
171
180
|
---
|
|
172
181
|
|
|
173
182
|
## Writing Style
|
|
@@ -191,7 +200,7 @@ Prose is valuable for context that contracts can't capture:
|
|
|
191
200
|
| Non-obvious behavior | "The hook always fetches fresh data. There is no caching or version checking." |
|
|
192
201
|
| Design rationale | "Why parallel fetches? Each entity type is independent, reducing load time." |
|
|
193
202
|
| Edge cases | "If created then deleted before save, it's removed from `added[]` without adding to `removed[]`." |
|
|
194
|
-
|
|
|
203
|
+
| Limitations, stated as facts | "`useAppInit` is standalone — nothing in the mount path calls it." |
|
|
195
204
|
| Usage context | "Used to show 'Unsaved' badge, warn before navigation, enable save buttons." |
|
|
196
205
|
|
|
197
206
|
### When to Avoid Prose
|
|
@@ -224,7 +233,7 @@ Use for structured info:
|
|
|
224
233
|
|
|
225
234
|
Every document is a **truth document** (states current absolutes, updated in place) or a **record** (dated account of activity, frozen or append-only) — see `SKILL.md` § The Two Natures. The tense rules follow directly:
|
|
226
235
|
|
|
227
|
-
**Truth documents describe the present, not the past.** Everything outside `docs/history/` states what the system _is_ and how it works _today_. It must not narrate how it got there. The journey — the dates, the migrations — lives **only** in records (`docs/history/`: changelog, plans, migrations).
|
|
236
|
+
**Truth documents describe the present, not the past.** Everything outside `docs/history/` states what the system _is_ and how it works _today_. It must not narrate how it got there. The journey — the dates, the migrations — lives **only** in records (`docs/history/`: changelog, plans, migrations). Three sanctioned exceptions: `docs/memory/` is the agent's own dated ledger; an AD's **Decision Changelog** tail carries that decision's dated revision history; and `docs/structure.md`'s **Health Log** tail carries the maintenance agent's tooling-stamped audit rows. In both tail bridges the body stays current-truth-only — head is truth, tail is provenance.
|
|
228
237
|
|
|
229
238
|
**Records are never edited to match the present.** A frozen plan stays wrong-in-hindsight; a changelog entry describes what shipped as it shipped. A scope change gets a new dated record that links back.
|
|
230
239
|
|
|
@@ -236,16 +245,31 @@ Strip this language from truth documents:
|
|
|
236
245
|
| "Built 2026-06-14; shipped + verified 2026-06-13." | "`agentSessionWorkflow` runs an outer turn loop over `runAgentLoop`." |
|
|
237
246
|
| "The legacy worker was deleted in the 2026-06-14 fold." | "One worker interprets `WorkflowDefinition`." |
|
|
238
247
|
| "ex-`@nurix/mcp-runtime`, M9 Phase C; was X, now Y." | "MCP client runtime, wired into `agentTriggerWorkflow`." |
|
|
248
|
+
| "Build Sequence phases 0–4 are complete." | Nothing — progress lives in the plan/backlog record; the doc describes what exists |
|
|
249
|
+
| "There is no standalone `skills/components/` — treat any reference to it as stale." | Nothing — delete or repoint the stale references; state only the present design |
|
|
239
250
|
| "Read first: `docs/history/plans/2026-…-fold.md`" (routing) | "Read first: `docs/architecture/<current-truth-doc>.md`" |
|
|
240
251
|
|
|
241
252
|
Rules:
|
|
242
253
|
|
|
243
|
-
- **No dates in truth documents
|
|
254
|
+
- **No dates and no progress narration in truth documents.** Nothing about _when_ something shipped, was verified, folded, or was removed — and no completion markers either: no "phases 0–4 are complete", no per-item `✅ shipped` / `⏳ pending`. Progress tracking is a **record's** job (the plan, the backlog); a truth document describes what exists, as if it had always been so.
|
|
255
|
+
- **No stale-reference tripwires.** Never write "X is gone — treat references to X as stale"; that is a redirect doc compressed into one sentence. Delete or repoint every stale reference instead (same rule as the **No redirect docs** rule in `SKILL.md` § The Two Natures) and state only the present fact.
|
|
256
|
+
- **No version or phase framing.** A bare `v1`/`v2` as a label, "Phase 1/2", "the old X", "supersedes the earlier" — there is one design; describe it without its predecessors. (A version naming an external present-tense fact stays: a schema literal like `design-tokens/v2`, a dependency pin like `@0.14.0`.)
|
|
257
|
+
- **Temporal hedges are progress markers in prose.** "currently", "not yet", "still", "for now" smuggle a ⏳ into a truth document — state the fact without the hedge ("the hook is standalone; nothing in the mount path calls it") and let the backlog record carry the intention.
|
|
258
|
+
- **A published artifact's own version surface is external fact.** Release notes and upgrade guides for a shipped package may use version framing (`v1 → v2`) — that is the artifact's public contract, not the doc narrating its own past. They live beside the artifact or under `docs/operations/`, never in `docs/features/` or `docs/architecture/`.
|
|
244
259
|
- **Cite the AD for the _why_, never the _when_.** `Provenance: AD-002` is good (it points to the decision rationale). "Decided in AD-002 and shipped 2026-06-13" is not.
|
|
245
|
-
- **Routing/index docs point only to truth documents.** A row whose
|
|
260
|
+
- **Routing/index docs point only to truth documents as a topic's authority.** A row whose "read first" target for a topic is a historical plan or changelog is a redirect-for-history — repoint it to the current truth doc, or drop the row. (An index _of the records themselves_ — a History section listing `plans/`, `changelog/` — is sanctioned: that indexes records, it doesn't route a topic.)
|
|
246
261
|
- **When a doc changes because the system changed, the change-story goes in `docs/history/` (a changelog entry), and the truth document simply becomes correct in present tense.** Never leave the diff narrated in place.
|
|
247
262
|
- **A decision that moves is an edit, not a new document.** Rewrite the AD's affected sections to the new truth and append the revision to its Decision Changelog — never mint a parallel doc and never leave the old choice narrated in the body.
|
|
248
263
|
|
|
264
|
+
Worked example of the strip-and-relocate move: [`examples.md`](examples.md) Example 6.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Mechanics
|
|
269
|
+
|
|
270
|
+
- Inline-code every identifier, filename, command, and config key.
|
|
271
|
+
- Bullet rules take the form "**bold assertion** — explanation."
|
|
272
|
+
|
|
249
273
|
---
|
|
250
274
|
|
|
251
275
|
## Structure
|
|
@@ -309,7 +333,8 @@ Always end with:
|
|
|
309
333
|
| Parameters | Type + purpose |
|
|
310
334
|
| Returns | Type + shape |
|
|
311
335
|
| State | Persistence points only |
|
|
312
|
-
| Diagrams | ASCII for all flows
|
|
336
|
+
| Diagrams | ASCII for all flows — docs only, never instruction files |
|
|
313
337
|
| Code | Under 15 lines, contracts only |
|
|
314
338
|
| Voice | Imperative/active |
|
|
339
|
+
| Mechanics | Inline-code identifiers; bold-assertion bullets |
|
|
315
340
|
| Length | Concise, one line when possible |
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Writing an Untemplated Document
|
|
2
|
+
|
|
3
|
+
Load when: no template in [`templates/`](../templates/Readme.md) matches the document being written — a design note, a comparison, an investigation writeup, an index, a reference sheet, anything the Routing table sends to "no template matches".
|
|
4
|
+
|
|
5
|
+
A template is a pre-made set of shape decisions. With no template, make the same decisions explicitly, in this order.
|
|
6
|
+
|
|
7
|
+
## 1. Decide the nature — truth or record
|
|
8
|
+
|
|
9
|
+
Every document is exactly one (`SKILL.md` § The Two Natures). Ask: *when reality changes, is this file rewritten, or is it frozen?*
|
|
10
|
+
|
|
11
|
+
- Rewritten in place → **truth document**. Topic-named (`kebab-case.md`), lives outside `docs/history/`, present tense throughout — the iron rule in `SKILL.md` § Present tense, always.
|
|
12
|
+
- Frozen or append-only → **record**. Date-named (`YYYY-MM-DD-slug.md`), lives in `docs/history/` — inside an existing subfolder where it fits (an investigation or research writeup → `history/research/{topic}/`); a genuinely new category of record needs a `docs/structure.md` folder-rule row in the same change, never an invented folder.
|
|
13
|
+
|
|
14
|
+
When a draft wants to be both — the current state *and* the story of getting there — split it: the state becomes a truth document, the story becomes a record that links to it. Never mix the natures in one body.
|
|
15
|
+
|
|
16
|
+
## 2. Place and name it
|
|
17
|
+
|
|
18
|
+
- `docs/structure.md` is the placement authority — match the document's topic to its folder map before inventing a location.
|
|
19
|
+
- **One canonical home per topic.** If a document on the topic already exists, extend it; a second document on the same topic is drift waiting to happen. Link, don't copy.
|
|
20
|
+
- A new topic *folder* gets a `Readme.md` landing page that introduces and indexes it.
|
|
21
|
+
|
|
22
|
+
## 3. Shape it
|
|
23
|
+
|
|
24
|
+
- **Open with the goal.** The first line under the H1 states what the document is for and who reads it — a reader (or agent) landing here cold decides in one sentence whether to keep reading.
|
|
25
|
+
- **Headings are retrieval keys.** Sections get pulled out of context by search and agents; qualify generic headings with the topic (`## Merge rules — data sync`, not `## Rules`) so each stands alone.
|
|
26
|
+
- **Tables for enumerable facts, prose for judgment.** One row per item; anything needing a *because* lives in prose around the table, never inside a cell.
|
|
27
|
+
- **Every section earns its place.** No empty stubs — omit what has nothing real to say; write an explicit "None" only where the absence itself is information.
|
|
28
|
+
- **Cite sources and link neighbours.** If the document mentions a concept another document owns, link it; if it draws on code, name the files.
|
|
29
|
+
|
|
30
|
+
## 4. Write it in the house style
|
|
31
|
+
|
|
32
|
+
`references/style-guide.md` carries the rules: present tense with no history narration, progress markers, or stale-reference tripwires (§ The Two Natures); imperative/active voice; code examples under 15 lines showing contracts, not implementations; ASCII diagrams in docs only; inline-code every identifier, filename, command, and config key.
|
|
33
|
+
|
|
34
|
+
## 5. With a human in the loop — the co-authoring workflow
|
|
35
|
+
|
|
36
|
+
**Stage 1 — context gathering.** Ask: audience? purpose? existing format to match? Have the user dump all the context they hold, then ask 5–10 clarifying questions before proposing anything.
|
|
37
|
+
|
|
38
|
+
**Stage 2 — structure & draft.** Propose the section structure from §1–3 above, then draft section by section. The user indicates changes — learn their style from their edits; don't silently rewrite them.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// skills/documentation/scripts/doc-lint.mjs - mechanical checks over markdown docs (tense narration, dates, stubs, redirects)
|
|
2
|
+
// Usage: node doc-lint.mjs <file.md ...> | node doc-lint.mjs --changed (git-changed + untracked .md)
|
|
3
|
+
// Exit 1 when any block-severity finding survives. Escape hatch: <!-- doc-lint-disable: id[, id] --> (file-wide, or on the offending line).
|
|
4
|
+
|
|
5
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
|
+
|
|
8
|
+
const RULES = [
|
|
9
|
+
{ id: 'stale-tripwire', severity: 'block', scope: 'truth', pattern: /treat (any )?[\w`./-]+ ?(references? )?as stale|superseded (by|the)|\bused to (be|target|live|hold)\b/i, message: 'stale-reference tripwire / supersession talk — fix the references, state the present fact' },
|
|
10
|
+
{ id: 'history-narration', severity: 'block', scope: 'truth', pattern: /\bno longer\b|\bwas (renamed|removed|extracted|folded|migrated|replaced)\b|\bmoved (to|from|into) [\w`./-]+ \(?20\d{2}/i, message: 'history narration — the journey belongs in a dated record' },
|
|
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
|
+
{ 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
|
+
{ 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
|
+
{ id: 'status-glyph', severity: 'warn', scope: 'truth', pattern: /(^|\s)(✅|⏳|- \[[ x]\])/u, message: 'status glyph / checklist in a truth doc — tracking belongs in a record' },
|
|
15
|
+
{ 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
|
+
|
|
18
|
+
// Sections inside a truth doc where dated history is sanctioned (the two bridges).
|
|
19
|
+
const BRIDGE_HEADINGS = /^#{1,4}\s+(decision changelog|health log)\b/i;
|
|
20
|
+
// Instruction files and generated trees are a different genre — out of this linter's scope.
|
|
21
|
+
const SKIP = /(^|\/)(\.claude|node_modules|skills|rules|agents|harness|packages\/[^/]+\/data)\/|(^|\/)(CLAUDE|AGENTS)\.md$/;
|
|
22
|
+
const RECORD = /(^|\/)docs\/(history|research|memory)\//;
|
|
23
|
+
|
|
24
|
+
// Classify a path: skipped genre, record, or truth.
|
|
25
|
+
function scopeOf(path) {
|
|
26
|
+
if (SKIP.test(path)) return 'skip';
|
|
27
|
+
return RECORD.test(path) ? 'record' : 'truth';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Yield prose lines only: frontmatter, fenced code blocks, and bridge sections drop out.
|
|
31
|
+
function proseLines(text) {
|
|
32
|
+
const lines = text.split('\n');
|
|
33
|
+
const out = [];
|
|
34
|
+
let inFence = false;
|
|
35
|
+
let inFrontmatter = lines[0] === '---';
|
|
36
|
+
let inBridge = false;
|
|
37
|
+
let bridgeLevel = 0;
|
|
38
|
+
for (let i = inFrontmatter ? 1 : 0; i < lines.length; i++) {
|
|
39
|
+
const line = lines[i];
|
|
40
|
+
if (inFrontmatter) { if (line === '---') inFrontmatter = false; continue; }
|
|
41
|
+
if (/^\s*(```|~~~|````)/.test(line)) { inFence = !inFence; continue; }
|
|
42
|
+
if (inFence) continue;
|
|
43
|
+
const heading = line.match(/^(#{1,4})\s/);
|
|
44
|
+
if (heading) {
|
|
45
|
+
if (inBridge && heading[1].length <= bridgeLevel) inBridge = false;
|
|
46
|
+
if (BRIDGE_HEADINGS.test(line)) { inBridge = true; bridgeLevel = heading[1].length; }
|
|
47
|
+
}
|
|
48
|
+
if (!inBridge) out.push({ n: i + 1, line });
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Collect file-wide disabled rule ids from <!-- doc-lint-disable: a, b --> comments.
|
|
54
|
+
function fileDisables(text) {
|
|
55
|
+
const ids = new Set();
|
|
56
|
+
for (const m of text.matchAll(/<!--\s*doc-lint-disable:\s*([\w-,\s]+?)\s*-->/g))
|
|
57
|
+
m[1].split(',').forEach((id) => ids.add(id.trim()));
|
|
58
|
+
return ids;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function lintFile(path, findings) {
|
|
62
|
+
const scope = scopeOf(path);
|
|
63
|
+
if (scope === 'skip' || !existsSync(path)) return;
|
|
64
|
+
const text = readFileSync(path, 'utf8');
|
|
65
|
+
const disabled = fileDisables(text);
|
|
66
|
+
const lines = proseLines(text);
|
|
67
|
+
|
|
68
|
+
const nonEmpty = text.split('\n').filter((l) => l.trim()).length;
|
|
69
|
+
if (scope === 'truth' && nonEmpty <= 10 && /\b(moved to|relocated to|see [\w`./-]+ instead)\b/i.test(text) && !disabled.has('redirect-doc'))
|
|
70
|
+
findings.push({ path, n: 1, severity: 'block', id: 'redirect-doc', message: 'redirect stub — delete the file and repoint every inbound link' });
|
|
71
|
+
|
|
72
|
+
for (const rule of RULES) {
|
|
73
|
+
if (rule.scope !== scope || disabled.has(rule.id)) continue;
|
|
74
|
+
for (const { n, line } of lines) {
|
|
75
|
+
if (!rule.pattern.test(line)) continue;
|
|
76
|
+
if (line.includes(`doc-lint-disable: ${rule.id}`) || new RegExp(`doc-lint-disable:[^>]*\\b${rule.id}\\b`).test(line)) continue;
|
|
77
|
+
findings.push({ path, n, severity: rule.severity, id: rule.id, message: rule.message, excerpt: line.trim().slice(0, 90) });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (scope === 'truth' && /(^|\/)docs\/features\/[^/]+\.md$/.test(path)) {
|
|
82
|
+
const fm = text.startsWith('---') ? text.slice(0, text.indexOf('\n---', 3)) : '';
|
|
83
|
+
for (const key of ['owns:', 'keywords:', 'status:'])
|
|
84
|
+
if (!fm.includes(key)) findings.push({ path, n: 1, severity: 'block', id: 'feature-frontmatter', message: `feature doc missing \`${key}\` frontmatter` });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// A changed plan whose committed version is already frozen must not be edited.
|
|
89
|
+
function checkFrozenEdits(paths, findings) {
|
|
90
|
+
for (const path of paths) {
|
|
91
|
+
if (!/(^|\/)docs\/history\/(plans|changelog|migrations)\//.test(path)) continue;
|
|
92
|
+
try {
|
|
93
|
+
const head = execSync(`git show HEAD:"${path}"`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
|
|
94
|
+
if (/(^|\/)plans\//.test(path) && !/Status:\s*(Complete|Abandoned)/i.test(head)) continue;
|
|
95
|
+
findings.push({ path, n: 1, severity: 'block', id: 'frozen-record-edit', message: 'frozen record modified — records are never edited; write a new dated record that links back' });
|
|
96
|
+
} catch { /* new file — creating a record is fine */ }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const args = process.argv.slice(2);
|
|
101
|
+
let files = args.filter((a) => a !== '--changed');
|
|
102
|
+
const changedMode = args.includes('--changed');
|
|
103
|
+
if (changedMode) {
|
|
104
|
+
const out = execSync('git diff --name-only HEAD -- "*.md"; git ls-files --others --exclude-standard -- "*.md"', { encoding: 'utf8', shell: '/bin/bash' });
|
|
105
|
+
files = [...new Set([...files, ...out.split('\n').filter(Boolean)])];
|
|
106
|
+
}
|
|
107
|
+
if (!files.length) { console.error('usage: node doc-lint.mjs <file.md ...> | --changed'); process.exit(2); }
|
|
108
|
+
|
|
109
|
+
const findings = [];
|
|
110
|
+
files.forEach((f) => lintFile(f, findings));
|
|
111
|
+
if (changedMode) checkFrozenEdits(files.filter((f) => existsSync(f)), findings);
|
|
112
|
+
|
|
113
|
+
for (const f of findings.sort((a, b) => (a.severity === b.severity ? 0 : a.severity === 'block' ? -1 : 1)))
|
|
114
|
+
console.log(`${f.path}:${f.n} [${f.severity}] ${f.id} — ${f.message}${f.excerpt ? `: "${f.excerpt}"` : ''}`);
|
|
115
|
+
const blocks = findings.filter((f) => f.severity === 'block').length;
|
|
116
|
+
console.log(`\n${findings.length} finding(s), ${blocks} blocking, ${files.length} file(s) checked.`);
|
|
117
|
+
process.exit(blocks ? 1 : 0);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Documentation Templates
|
|
2
|
+
|
|
3
|
+
One file per template. Every template is either a **truth document** (states current absolutes, updated in place) or a **record** (dated account of activity, frozen or append-only) — the split is defined in `SKILL.md` § The Two Natures, and each template names its nature in its opening line.
|
|
4
|
+
|
|
5
|
+
| Template | File | Nature |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| Feature (LLD) | [feature.md](feature.md) | Truth |
|
|
8
|
+
| Architecture Decision (AD) | [architecture-decision.md](architecture-decision.md) | Truth (Decision Changelog tail sanctioned) |
|
|
9
|
+
| System overview | [system-overview.md](system-overview.md) | Truth |
|
|
10
|
+
| Domain model | [domain-model.md](domain-model.md) | Truth |
|
|
11
|
+
| Glossary | [glossary.md](glossary.md) | Truth |
|
|
12
|
+
| Non-goals | [non-goals.md](non-goals.md) | Truth |
|
|
13
|
+
| API reference | [api-reference.md](api-reference.md) | Truth |
|
|
14
|
+
| Environment registry | [environment-registry.md](environment-registry.md) | Truth |
|
|
15
|
+
| Security overview | [security-overview.md](security-overview.md) | Truth |
|
|
16
|
+
| Testing strategy | [testing-strategy.md](testing-strategy.md) | Truth |
|
|
17
|
+
| Operations procedure | [operations-procedure.md](operations-procedure.md) | Truth |
|
|
18
|
+
| Protected files | [protected-files.md](protected-files.md) | Truth |
|
|
19
|
+
| Project root README | [root-readme.md](root-readme.md) | Truth |
|
|
20
|
+
| docs/README.md (doc index) | [docs-readme.md](docs-readme.md) | Truth |
|
|
21
|
+
| docs/structure.md (authority map) | [structure.md](structure.md) | Truth (Health Log tail sanctioned) |
|
|
22
|
+
| Plan | [plan.md](plan.md) | Record |
|
|
23
|
+
| Changelog entry | [changelog-entry.md](changelog-entry.md) | Record |
|
|
24
|
+
| Migration runbook | [migration-runbook.md](migration-runbook.md) | Record |
|
|
25
|
+
| CHANGELOG master index | [changelog-index.md](changelog-index.md) | Record index (append-only) |
|
|
26
|
+
| Backlog | [backlog.md](backlog.md) | Record (append-only) |
|
|
27
|
+
| Memory (main agent) | [memory-main.md](memory-main.md) | Record (append-only ledger) |
|
|
28
|
+
|
|
29
|
+
**Baseline scaffolds** — files that must exist after every baseline; the `maintenance` agent (baseline mode) creates any that are missing: [docs-readme.md](docs-readme.md), [structure.md](structure.md), [changelog-index.md](changelog-index.md), [backlog.md](backlog.md), [memory-main.md](memory-main.md), and [root-readme.md](root-readme.md) (never overwriting an existing root README).
|
|
30
|
+
|
|
31
|
+
**Ownership.** A feature doc is the canonical reference for the code paths in its **`owns:` frontmatter** — the only ownership key. To find what a doc owns, read its `owns:` globs; to find the doc for a path, match the path against the injected ownership index (the union of every feature's `owns:` globs), narrower glob wins. See [`../references/frontmatter.md`](../references/frontmatter.md) for turning code into `owns:` globs.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# API Reference (docs/architecture/api.md)
|
|
2
|
+
|
|
3
|
+
Truth document. Use for: the consolidated catalog of the system's HTTP surface — every live route in one place. This is the **index**: request/response shapes stay in the owning feature docs; this doc answers "what exists and where is it documented." If the repo generates an OpenAPI spec, this doc points at it and carries only what the spec can't (auth model, conventions, ownership links).
|
|
4
|
+
|
|
5
|
+
Required: **Conventions**, the **route tables**. One row per live route; a route that isn't here doesn't exist.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
# API Reference
|
|
9
|
+
|
|
10
|
+
## Conventions
|
|
11
|
+
|
|
12
|
+
- Base path: `/api/v{N}` · Auth: {scheme} · Envelope: `{ "status": 1, "message": "...", "data": {...} }`
|
|
13
|
+
- Errors: `{ "status": 0, "message": "...", "data": null }` · Codes: 200/201/400/401/403/404/500
|
|
14
|
+
|
|
15
|
+
## {Resource group}
|
|
16
|
+
|
|
17
|
+
| Method | Path | Purpose | Auth | Owning doc |
|
|
18
|
+
| ------ | ---- | ------- | ---- | ---------- |
|
|
19
|
+
| GET | `/api/foo` | {one line} | {role} | [feature](../features/{feature}.md) |
|
|
20
|
+
```
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Architecture Decision (AD)
|
|
2
|
+
|
|
3
|
+
Truth document — with the one sanctioned dated tail. Use for: the standing decision on one architectural **subject** — a system shape, a contract, a substrate choice. Lives in `docs/architecture/decisions/`. The body always states the current decision and its current reasoning. When the decision moves, rewrite the affected sections to what is now true, bump `last-updated`, and append the revision to the **Decision Changelog** — the journey lives in that tail, never in the body. Head is truth; tail is provenance.
|
|
4
|
+
|
|
5
|
+
### One subject per AD
|
|
6
|
+
|
|
7
|
+
The unit is the subject, not the individual call. "The persistence layer", "auth", "the design-skill architecture" are subjects; every decision, reversal, and re-decision on a subject lands in that subject's one AD, however many times it moves. Write a second AD only when a genuinely separate subject emerges — never to record a revision of an existing one.
|
|
8
|
+
|
|
9
|
+
### When to write an AD
|
|
10
|
+
|
|
11
|
+
Write an AD when **all three** apply:
|
|
12
|
+
|
|
13
|
+
1. The decision is architectural — affects system shape, contracts, or substrate choice, not internal implementation
|
|
14
|
+
2. Alternatives were genuinely considered and rejected
|
|
15
|
+
3. Future maintainers will ask "why is it this way?"
|
|
16
|
+
|
|
17
|
+
Don't write an AD for:
|
|
18
|
+
|
|
19
|
+
- Standing principles or style preferences (those are stances, not decisions — put them in `docs/architecture/`)
|
|
20
|
+
- Routine implementation choices with no rejected alternatives
|
|
21
|
+
- Anything you'd be happy reversing without ceremony
|
|
22
|
+
|
|
23
|
+
### Lifecycle
|
|
24
|
+
|
|
25
|
+
`Proposed` → `Accepted` → `Deprecated`
|
|
26
|
+
|
|
27
|
+
Frontmatter `status:` is the single lifecycle source of truth. An AD stays `Accepted` for as long as its subject is live — revisions rewrite the body and never change the status, and there is **no `Superseded` state**: supersession is an edit plus a changelog entry, not a new document. Set `Deprecated` only when the subject itself is retired (the component is gone, the contract no longer exists); state where the replacement lives, if any.
|
|
28
|
+
|
|
29
|
+
### Naming
|
|
30
|
+
|
|
31
|
+
`AD-NNN-short-title.md` — three-digit zero-padded number, kebab-case subject. Numbers are assigned sequentially across the project and never reused.
|
|
32
|
+
|
|
33
|
+
### Structure
|
|
34
|
+
|
|
35
|
+
Three sections are required: **Decision**, **Context**, **Consequences** — plus the **Decision Changelog** once the AD has been revised at least once. Everything else is optional — include only when warranted. Decision-first: readers should see the answer in the first paragraph.
|
|
36
|
+
|
|
37
|
+
### Template
|
|
38
|
+
|
|
39
|
+
```markdown
|
|
40
|
+
---
|
|
41
|
+
title: "AD-NNN: Short title in sentence case"
|
|
42
|
+
status: Accepted # Proposed | Accepted | Deprecated
|
|
43
|
+
created: YYYY-MM-DD
|
|
44
|
+
last-updated: YYYY-MM-DD # bump on every revision
|
|
45
|
+
decision-owner: Name
|
|
46
|
+
reviewers: [] # optional
|
|
47
|
+
tags: [ad]
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
# AD-NNN: Short Title
|
|
51
|
+
|
|
52
|
+
## Decision
|
|
53
|
+
|
|
54
|
+
[One paragraph, active voice. "We will…" Lead with the answer — the current answer, whatever the journey was.]
|
|
55
|
+
|
|
56
|
+
## Context
|
|
57
|
+
|
|
58
|
+
[The forces in play. What is the problem? Why does it need a decision? Technical, political, social, and project-local pressures — stated as present-tense forces, not as a history of the subject.]
|
|
59
|
+
|
|
60
|
+
## Consequences
|
|
61
|
+
|
|
62
|
+
**Positive:**
|
|
63
|
+
|
|
64
|
+
- [What becomes easier]
|
|
65
|
+
|
|
66
|
+
**Negative:**
|
|
67
|
+
|
|
68
|
+
- [What becomes harder, what we accept reluctantly]
|
|
69
|
+
|
|
70
|
+
**Risks:**
|
|
71
|
+
|
|
72
|
+
- [Second-order effects you can already see, with mitigations if known]
|
|
73
|
+
|
|
74
|
+
<!-- Optional sections below. Include only when warranted by the decision's weight. -->
|
|
75
|
+
|
|
76
|
+
## Decision Drivers
|
|
77
|
+
|
|
78
|
+
[Hard non-negotiables vs. strong levers. Include when alternatives exist and the criteria aren't self-evident from Context.]
|
|
79
|
+
|
|
80
|
+
## Options Considered
|
|
81
|
+
|
|
82
|
+
| Option | Pros | Cons | Outcome |
|
|
83
|
+
| ------------ | ---- | ---- | --------------------- |
|
|
84
|
+
| **Option A** | … | … | **CHOSEN** |
|
|
85
|
+
| Option B | … | … | Eliminated — [reason] |
|
|
86
|
+
|
|
87
|
+
## Deep Comparison
|
|
88
|
+
|
|
89
|
+
[Honest framing of the strongest alternative(s). Include when the table doesn't make the reasoning obvious. State the *real* reason each was rejected — not a strawman.]
|
|
90
|
+
|
|
91
|
+
## Architecture in Detail
|
|
92
|
+
|
|
93
|
+
[Diagrams, code shape, layer responsibilities. Include when the decision implies non-trivial structure that readers need to understand alongside the *why*. Execution belongs in a plan doc (`docs/history/plans/`) — link to it, don't embed an implementation plan here.]
|
|
94
|
+
|
|
95
|
+
## Open Questions
|
|
96
|
+
|
|
97
|
+
1. **[Question]** — _Default_: [stated default until resolved].
|
|
98
|
+
|
|
99
|
+
## References
|
|
100
|
+
|
|
101
|
+
- [External link or cross-reference]
|
|
102
|
+
|
|
103
|
+
## Decision Changelog
|
|
104
|
+
|
|
105
|
+
<!-- The provenance tail — append-only, newest first. One entry per revision: what changed, the trigger, the previous choice, and the plan that implemented it. Added on the first revision; the initial acceptance is the last entry. -->
|
|
106
|
+
|
|
107
|
+
- YYYY-MM-DD — [What changed] because [trigger]. Implemented by [plan link]. (Previously: [old choice].)
|
|
108
|
+
- YYYY-MM-DD — Initial decision accepted. Implemented by [plan link].
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Rules
|
|
112
|
+
|
|
113
|
+
- **One subject per AD; all its revisions stay inside it.** Two genuinely different subjects means two ADs — a revision of an existing subject never does.
|
|
114
|
+
- **The body is always the current truth.** Rewrite affected sections on every revision; never leave a superseded choice narrated in the body — the Decision Changelog carries the journey, with each entry naming the old choice and the trigger.
|
|
115
|
+
- Decision-first. The first paragraph after the heading is the answer; readers should not need to scroll.
|
|
116
|
+
- **Consequences lists positives, negatives, AND risks.** Pros-only Consequences is the genre's classic failure — one AD's honest resulting context is the input context for the next.
|
|
117
|
+
- **Options Considered keeps every genuinely rejected alternative with the real reason.** Informed rejections are the most expensive knowledge to lose; a rejection made with strong prior art in view says so explicitly.
|
|
118
|
+
- **Keep it short — one to two pages.** Long decision docs don't get updated. How-it-works detail belongs in the owning feature doc; execution in a plan. Link, don't embed.
|
|
119
|
+
- No section numbering (`§5.1.1`). Use markdown headings only — inserting a section shouldn't renumber everything.
|
|
120
|
+
- Frontmatter `status` is the single source of truth for lifecycle. Don't also write "Status: Accepted" in the body.
|
|
121
|
+
- The Decision Changelog is append-only, newest first. Typo and formatting fixes don't get entries.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Scaffold: docs/history/backlog.md
|
|
2
|
+
|
|
3
|
+
Baseline scaffold — created if missing (the `maintenance` agent's baseline calls this). Target file: `docs/history/backlog.md`.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
# Backlog
|
|
7
|
+
|
|
8
|
+
Tracked work items, technical debt, and planned improvements. Append new items under the appropriate priority.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Format
|
|
13
|
+
|
|
14
|
+
- **[P0]** Description — discovered by [plan-name or agent-name]
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## P0 — Critical
|
|
19
|
+
|
|
20
|
+
{items here}
|
|
21
|
+
|
|
22
|
+
## P1 — High
|
|
23
|
+
|
|
24
|
+
{items here}
|
|
25
|
+
|
|
26
|
+
## P2 — Medium
|
|
27
|
+
|
|
28
|
+
{items here}
|
|
29
|
+
|
|
30
|
+
## P3 — Low
|
|
31
|
+
|
|
32
|
+
{items here}
|
|
33
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Record. Use for: recording code changes with function-level detail. Entries are frozen once written — they describe the change as it shipped, and are never edited to match later reality.
|
|
4
|
+
|
|
5
|
+
Change categories follow the Keep a Changelog vocabulary — **Added / Changed / Deprecated / Removed / Fixed / Security** — the same six the master `CHANGELOG.md` scaffold instructs. Include only the categories that apply; all six share the same bullet shape (function/file, then detail lines).
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
# YYYY-MM-DD - [Title]
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
Brief description of what changed and why.
|
|
13
|
+
|
|
14
|
+
## Changes
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- `newFunction(params)` in `path/to/file.js`
|
|
18
|
+
- Purpose: What it does
|
|
19
|
+
- Used by: Where it's called
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- `existingFunction()` in `path/to/file.js`
|
|
23
|
+
- Changed: What's different
|
|
24
|
+
- Reason: Why it changed
|
|
25
|
+
- Migration: What callers must do
|
|
26
|
+
|
|
27
|
+
### Removed
|
|
28
|
+
- `oldFunction()` from `path/to/file.js`
|
|
29
|
+
- Replaced by: `newFunction()`
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
- `buggyFunction()` in `path/to/file.js`
|
|
33
|
+
- Bug: What was wrong, when it manifested
|
|
34
|
+
- Fix: What changed
|
|
35
|
+
|
|
36
|
+
(Deprecated / Security follow the same shape.)
|
|
37
|
+
|
|
38
|
+
## Breaking Changes
|
|
39
|
+
|
|
40
|
+
⚠️ **[Change description]**
|
|
41
|
+
- Old: Previous behavior
|
|
42
|
+
- New: Current behavior
|
|
43
|
+
- Impact: What breaks
|
|
44
|
+
- Migration: How to fix
|
|
45
|
+
|
|
46
|
+
## Migration Steps
|
|
47
|
+
|
|
48
|
+
Numbered instructions for whoever applies the change — not a tracked task list (the entry is frozen once written):
|
|
49
|
+
|
|
50
|
+
1. {Step}
|
|
51
|
+
2. {Step}
|
|
52
|
+
|
|
53
|
+
## Verified
|
|
54
|
+
|
|
55
|
+
- What was exercised end-to-end and the observed result — real runs, real fixtures, measured timings
|
|
56
|
+
- A future agent diagnosing a regression starts from this evidence
|
|
57
|
+
|
|
58
|
+
## Files Changed
|
|
59
|
+
|
|
60
|
+
- `src/path/file1.js` - Core changes
|
|
61
|
+
- `src/path/file2.js` - Supporting updates
|
|
62
|
+
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Scaffold: docs/history/CHANGELOG.md
|
|
2
|
+
|
|
3
|
+
Baseline scaffold — created if missing (the `maintenance` agent's baseline calls this). Target file: `docs/history/CHANGELOG.md`.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
# Changelog
|
|
7
|
+
|
|
8
|
+
All notable changes to {Project Name} will be documented in this file.
|
|
9
|
+
|
|
10
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## [Unreleased]
|
|
15
|
+
|
|
16
|
+
{entries added here as work proceeds; promoting them under a release heading is the one sanctioned edit to this index — the dated entry files themselves stay frozen}
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Links
|
|
21
|
+
|
|
22
|
+
- [Setup Guide](../operations/setup.md)
|
|
23
|
+
- [Architecture](../architecture/)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## How to Update This Changelog
|
|
28
|
+
|
|
29
|
+
1. Create a dated change file in `docs/history/changelog/`:
|
|
30
|
+
`docs/history/changelog/YYYY-MM-DD-feature-name.md`
|
|
31
|
+
|
|
32
|
+
2. Update this CHANGELOG.md with a high-level summary linking to the detail file
|
|
33
|
+
|
|
34
|
+
3. Follow Keep a Changelog categories:
|
|
35
|
+
- **Added** for new features
|
|
36
|
+
- **Changed** for changes in existing functionality
|
|
37
|
+
- **Deprecated** for soon-to-be removed features
|
|
38
|
+
- **Removed** for now removed features
|
|
39
|
+
- **Fixed** for bug fixes
|
|
40
|
+
- **Security** for security improvements
|
|
41
|
+
```
|