@naswerks/engineering-loop 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +24 -0
- package/README.md +25 -12
- package/package.json +1 -1
- package/references/docs-workflow.md +21 -5
- package/references/engineering-loop.md +1 -1
- package/skills/docs-audit-full/SKILL.md +4 -1
- package/skills/docs-process/SKILL.md +7 -3
- package/skills/docs-status/SKILL.md +4 -0
- package/skills/init/SKILL.md +14 -5
- package/skills/init/scripts/doctor.mjs +58 -7
- package/skills/spec-seat/SKILL.md +1 -1
- package/templates/docs/_meta/doc-index.md +6 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naswerks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "The engineering loop: a knowledge loop (docs-*) and an execution loop (spec-*) for repositories driven by hosted agent sessions, plus init to make a repository ready for both.",
|
|
5
5
|
"author": { "name": "naswerks" },
|
|
6
6
|
"license": "MIT",
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ version is the one in `package.json` and `.claude-plugin/plugin.json`, which mus
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.5.0] - 2026-09-24
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **The menu declares its shape.** `docs/_meta/doc-index.md` carries `<!-- naswerks-loop: menu=all -->` (it
|
|
13
|
+
lists every living doc: the repository has no other index; the default, and what a menu with no such line
|
|
14
|
+
means) or `<!-- naswerks-loop: menu=curated; index={path} -->` (the repository already indexes every doc in
|
|
15
|
+
`{path}`; the menu lists the docs its seats read). `docs-workflow.md` § The repository-owned sections
|
|
16
|
+
explains both, and the loop's standard for a doc on a curated menu: a header at a decided status and a
|
|
17
|
+
`## Key Files` section. `init` writes `menu=curated` when its scan finds an index the repository already
|
|
18
|
+
keeps; `docs-process` registers a new doc where the shape says; `docs-status` and `docs-audit-full` flag a
|
|
19
|
+
menu that has drifted; `init doctor` reads the shape, FAILs a doc the menu names that does not exist,
|
|
20
|
+
flags a living doc an all-menu misses, and holds every doc a curated menu names to the standard.
|
|
21
|
+
`pins/menu.test.mjs` holds both shapes.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- The map's Doc metadata row no longer asks whether the menu is kept by hand; the menu's shape line says
|
|
26
|
+
what the menu is.
|
|
27
|
+
- `docs-workflow.md` § The repository-owned sections no longer says which words this repository writes for
|
|
28
|
+
the loop's states; that is § Doc metadata's to say, so a repository that pastes the section verbatim no
|
|
29
|
+
longer inherits a line that is untrue for it.
|
|
30
|
+
- README: taking a new release is `claude plugin update`; the pins and the release path read as they are.
|
|
31
|
+
|
|
8
32
|
## [0.4.0] - 2026-09-23
|
|
9
33
|
|
|
10
34
|
### Changed
|
package/README.md
CHANGED
|
@@ -20,7 +20,8 @@ claude plugin install naswerks@engineering-loop
|
|
|
20
20
|
|
|
21
21
|
The marketplace is this repository, and the plugin is sourced from it: no registry sits between you and a
|
|
22
22
|
release. Plugins load at session start, so start a new session (or run `/reload-plugins`) after installing.
|
|
23
|
-
To pick up a new release later: `claude plugin marketplace update engineering-loop`, then
|
|
23
|
+
To pick up a new release later: `claude plugin marketplace update engineering-loop`, then
|
|
24
|
+
`claude plugin update naswerks@engineering-loop` (an install over an existing install does nothing).
|
|
24
25
|
|
|
25
26
|
Skills are namespaced by the plugin: `/naswerks:docs-write`, `/naswerks:spec-pipeline`, `/naswerks:init`.
|
|
26
27
|
|
|
@@ -48,6 +49,11 @@ sections lists what each must answer. One grep lists the four:
|
|
|
48
49
|
rg -n '^#+ .*\(repository-owned\)' docs/_meta/
|
|
49
50
|
```
|
|
50
51
|
|
|
52
|
+
If the repository already keeps an index of every doc, declare the loop's menu curated — put
|
|
53
|
+
`<!-- naswerks-loop: menu=curated; index=docs/INDEX.md -->` (your index's path) in `docs/_meta/doc-index.md`
|
|
54
|
+
— and the menu lists only the docs your seats read, each held to the loop's standard; every other doc stays
|
|
55
|
+
in your own index. A repository with no index of its own keeps the default, a menu of every living doc.
|
|
56
|
+
|
|
51
57
|
Every skill names the section it needs and never states the format itself, so the pack stays generic and
|
|
52
58
|
your conventions live in your repository. **Taking a newer pack:** `/naswerks:init refresh` writes only
|
|
53
59
|
those four sections and the version stamp into your copies — a dry run first, then `--write` on your word —
|
|
@@ -70,7 +76,7 @@ and never touches the rest of the text.
|
|
|
70
76
|
| `spec-retro` | spec | the coordinator's close-out audit |
|
|
71
77
|
| `spec-witness` | spec | a read-only verifier |
|
|
72
78
|
| `spec-ignite` | spec | the ignition copilot for a hosted run |
|
|
73
|
-
| `init` | — | make a repository ready for both loops |
|
|
79
|
+
| `init` | — | make a repository ready for both loops; `init doctor` checks one, `init refresh` takes a newer pack's repository-owned sections |
|
|
74
80
|
|
|
75
81
|
`references/` carries the two method documents a repository copies into `docs/_meta/` and then
|
|
76
82
|
rewrites where they say (repository-owned);
|
|
@@ -84,19 +90,26 @@ or the child may resolve `docs/` paths against the parent session's workspace.
|
|
|
84
90
|
|
|
85
91
|
## Does it work
|
|
86
92
|
|
|
87
|
-
`npm test` runs the pins: one version across the package, the plugin manifest
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
`npm test` runs the pins: one version across the package, the plugin manifest, the marketplace entry and
|
|
94
|
+
both method documents' stamps; every skill directory carries a `SKILL.md` named for its folder; no
|
|
95
|
+
byte-order marks; the vocabulary and content censuses; no skill states a format a repository owns, and
|
|
96
|
+
every skill that needs one carries the same find-format line; exactly four repository-owned sections, found
|
|
97
|
+
by the one grep; the loop's own preferences stay stated in the method documents; the menu's two shapes; `init refresh` leaves
|
|
98
|
+
every line outside those sections byte for byte. `node bin/verify-staged.mjs --self-test` drives the
|
|
99
|
+
receipt against a throwaway repository and reports each case.
|
|
91
100
|
|
|
92
101
|
## Releasing
|
|
93
102
|
|
|
94
|
-
The version lives in `package.json
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
The version lives in `package.json`, `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`
|
|
104
|
+
(they must agree), in both method documents' stamps, and in a CHANGELOG section. The marketplace serves
|
|
105
|
+
the plugin from this repository at whatever `main` holds, so a CLI install never waits on npm; npm is the
|
|
106
|
+
runner lane.
|
|
107
|
+
|
|
108
|
+
To release: push the release commit to `main`, publish it to npm from the repository root (`npm login`,
|
|
109
|
+
then `npm publish --access public`), then push its `v*` tag. The tag's workflow runs the pins, finds the
|
|
110
|
+
version already on the registry and skips its own publish, and creates the GitHub release from the
|
|
111
|
+
CHANGELOG section. With a trusted publisher linked on npmjs.com for this repository and `release.yml`,
|
|
112
|
+
pushing the tag alone publishes; the workflow prints that remedy when a publish is refused.
|
|
100
113
|
|
|
101
114
|
## License
|
|
102
115
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naswerks/engineering-loop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "The engineering loop as a Claude Code plugin: a knowledge loop (docs-*), an execution loop (spec-*), and init for a fresh repository. The package root is the plugin root.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- naswerks-loop: version=0.
|
|
1
|
+
<!-- naswerks-loop: version=0.5.0 -->
|
|
2
2
|
# Docs Workflow
|
|
3
3
|
|
|
4
4
|
> How knowledge flows through `docs/`: the loop, the folder rules, the templates, and the `docs-*` skills.
|
|
@@ -84,12 +84,12 @@ that replaces the section rewrites alongside it):
|
|
|
84
84
|
| Section | What the skills read from it | Also echoed in |
|
|
85
85
|
|---------|------------------------------|----------------|
|
|
86
86
|
| **The archive convention** (this file) | the archive home for each kind of effort (hand-cranked, spec pipeline, follow-on round) and for audit reports · the shape inside it · what a working doc's `## Archive` line says, and whether an umbrella carries an `Archive:` line · the links a move repairs · what is frozen once filed | The Loop (the diagram and its prose) · Folders (`research/`, `working/`, `archive/`) · "Living docs vs session docs" · the skills table (`docs-process`) · Conventions (incident receipts, archived docs) · `engineering-loop.md` "The loop at a glance" |
|
|
87
|
-
| **Doc metadata** (this file) | the header — its form and its place · the sweep: one command, one line per doc · the word for each loop state (below) · where the verified date lives, and the rule that moves it · whether `## Lineage` stays · the gates a doc passes, and the folders they exempt
|
|
87
|
+
| **Doc metadata** (this file) | the header — its form and its place · the sweep: one command, one line per doc · the word for each loop state (below) · where the verified date lives, and the rule that moves it · whether `## Lineage` stays · the gates a doc passes, and the folders they exempt | the skills table (`docs-status`) · Conventions (the machine layer; dates in the header) · `engineering-loop.md`'s own header line and its `## Lineage` |
|
|
88
88
|
| **Templates** (this file) | each template's header lines, at the state its writer names | — |
|
|
89
89
|
| **This repository's rails** (`engineering-loop.md`) | the policy document, and how a seat reaches it · the gates a commit passes · the suite before a PR · the base branch, the PR target, the branch names and how a PR is opened · the staging rule · the repository's own skills a seat meets | the POLICY row of "Where a rule lives" · the Hard rules, which the rails add to and never subtract from |
|
|
90
90
|
|
|
91
91
|
**The loop's states.** The skills speak in four states, and § Doc metadata says which word this repository
|
|
92
|
-
writes for each.
|
|
92
|
+
writes for each.
|
|
93
93
|
|
|
94
94
|
| State | Written by | Means |
|
|
95
95
|
|-------|------------|-------|
|
|
@@ -100,8 +100,21 @@ writes for each. Here the words are the states themselves.
|
|
|
100
100
|
|
|
101
101
|
*Stale* is a date, not a state: `docs-status` computes it from the verified date, and no seat writes it.
|
|
102
102
|
|
|
103
|
-
**
|
|
104
|
-
|
|
103
|
+
**The menu.** `doc-index.md` is the menu the seats read, and the whole file is the repository's: `init` writes
|
|
104
|
+
it once and never again. A line near its top says which of two shapes it has, and one grep reads it:
|
|
105
|
+
`rg -n 'naswerks-loop: menu=' docs/_meta/doc-index.md`.
|
|
106
|
+
|
|
107
|
+
| Shape line | The menu lists | A new living doc goes |
|
|
108
|
+
|------------|----------------|-----------------------|
|
|
109
|
+
| `<!-- naswerks-loop: menu=all -->` | every living doc — the repository has no other index | onto the menu |
|
|
110
|
+
| `<!-- naswerks-loop: menu=curated; index={path} -->` | the docs this repository's seats read; `{path}` indexes every doc | into `{path}`, the way that index is kept, and onto the menu only when it meets the loop's standard and a seat should read it |
|
|
111
|
+
|
|
112
|
+
**The loop's standard for a doc on a curated menu:** it exists, it carries the header § Doc metadata
|
|
113
|
+
prescribes at a decided status (not the word for `draft`), and it has a `## Key Files` section. `init doctor`
|
|
114
|
+
checks exactly that, for every doc a curated menu names.
|
|
115
|
+
|
|
116
|
+
**Silence.** A section that says nothing about an item leaves the loop's default: a menu with no shape line
|
|
117
|
+
lists every living doc (`menu=all`), no gate runs on a commit, the base branch is the remote's
|
|
105
118
|
default (`git symbolic-ref refs/remotes/origin/HEAD`), and a seat with no PR route asks the human once and
|
|
106
119
|
suggests recording the answer in the rails. Any other unanswered item is a question for the human, never a
|
|
107
120
|
guess.
|
|
@@ -131,6 +144,9 @@ Keep what the skills need to find, change the words to yours:
|
|
|
131
144
|
`spec-pipeline` then records the resolved path in the umbrella's blockquote as an `Archive:` line.
|
|
132
145
|
- **Your rails.** Fill each item of `engineering-loop.md` § This repository's rails from what the
|
|
133
146
|
repository already wrote down — a policy document, hooks, CI, a pull-request template.
|
|
147
|
+
- **Your menu.** A repository that already indexes its docs declares its menu curated and names that index:
|
|
148
|
+
the menu then lists only the docs its seats read, each meeting the loop's standard, and every other doc
|
|
149
|
+
stays in the repository's own index, kept the way it already is.
|
|
134
150
|
|
|
135
151
|
**The version stamp** on line 1 names the pack version whose repository-owned sections this copy carries.
|
|
136
152
|
The loop text around them is this repository's copy from whenever it was taken: `init doctor` never grades
|
|
@@ -18,7 +18,10 @@ what to audit next.
|
|
|
18
18
|
oldest-first.
|
|
19
19
|
|
|
20
20
|
2. **Feature completeness** — quick `docs/features/` folder pass (per `docs-audit-feature`'s
|
|
21
|
-
folder mode): any features in code with no doc, or docs whose feature is gone.
|
|
21
|
+
folder mode): any features in code with no doc, or docs whose feature is gone. Then the menu against its
|
|
22
|
+
shape line (`rg -n 'naswerks-loop: menu=' docs/_meta/doc-index.md`; none means `menu=all`): on
|
|
23
|
+
`menu=all`, living docs it does not name; on `menu=curated`, docs it names that fall short of the loop's
|
|
24
|
+
standard (`docs/_meta/docs-workflow.md` § The repository-owned sections).
|
|
22
25
|
|
|
23
26
|
3. **Convention spot-check** — sample 2-3 source files per major convention against the
|
|
24
27
|
`docs/patterns/` docs; note drift.
|
|
@@ -92,9 +92,13 @@ branch, which is the truth for that PR, and you say so at close.
|
|
|
92
92
|
speculation. A sizable design idea goes to `docs-backlog` (research/) instead. `docs-status`
|
|
93
93
|
surfaces these so they aren't lost.
|
|
94
94
|
|
|
95
|
-
3. **
|
|
96
|
-
feature)
|
|
97
|
-
(
|
|
95
|
+
3. **A new living doc?** If the work added a feature, ensure it has a doc in `docs/features/` (one doc per
|
|
96
|
+
feature). Then register every living doc this run created where the menu's shape line says
|
|
97
|
+
(`rg -n 'naswerks-loop: menu=' docs/_meta/doc-index.md`; `docs/_meta/docs-workflow.md` § The
|
|
98
|
+
repository-owned sections explains the two shapes). **`menu=all`, or no shape line:** add its row to the
|
|
99
|
+
menu in `docs/_meta/doc-index.md`. **`menu=curated; index={path}`:** add it to `{path}` the way that
|
|
100
|
+
index is kept — a generated index is never edited by hand — and add a menu row only when the doc meets
|
|
101
|
+
the loop's standard and a seat should read it; say which you did in the summary.
|
|
98
102
|
|
|
99
103
|
4. **Closing grep** — if the session doc notes deletions or renames, grep all living
|
|
100
104
|
docs (`guides/ patterns/ infrastructure/ features/ _meta/`) for stale references and fix them. Do
|
|
@@ -36,6 +36,10 @@ just counts.
|
|
|
36
36
|
broken breadcrumb).
|
|
37
37
|
- **low-lineage feature** — where the section keeps `## Lineage`: a feature doc with no Lineage rows =
|
|
38
38
|
ungrounded; worth wiring its lineage.
|
|
39
|
+
- **menu drift** — read the menu's shape line (`rg -n 'naswerks-loop: menu=' docs/_meta/doc-index.md`;
|
|
40
|
+
none means `menu=all`). On `menu=all`: a living doc the menu does not name. On `menu=curated`: a doc the
|
|
41
|
+
menu names that falls short of the loop's standard (`docs/_meta/docs-workflow.md` § The
|
|
42
|
+
repository-owned sections — a header at a decided status and a `## Key Files` section).
|
|
39
43
|
|
|
40
44
|
5. **Open issues.** Grep living docs for an `## Open Issues` section; list which docs/features carry
|
|
41
45
|
unresolved, code-verified problems (and how many) so known risks surface at orientation instead of
|
package/skills/init/SKILL.md
CHANGED
|
@@ -38,7 +38,8 @@ docs/
|
|
|
38
38
|
_meta/docs-workflow.md a copy of the pack's references/docs-workflow.md (stamped); its sections marked
|
|
39
39
|
(repository-owned) are the repository's to rewrite to its own conventions
|
|
40
40
|
_meta/engineering-loop.md a copy of references/engineering-loop.md (stamped), under the same rule
|
|
41
|
-
_meta/doc-index.md the menu, written LAST — every doc that exists
|
|
41
|
+
_meta/doc-index.md the menu, written LAST — its shape line (menu=all: every doc that exists;
|
|
42
|
+
menu=curated: the docs init drafted, when the repository indexes its own) + three repo slots:
|
|
42
43
|
## Role reading lists · ## Apps and their build commands · ## The evidence tools
|
|
43
44
|
research/ working/ archive/ guides/ patterns/ infrastructure/ features/ each with a README.md
|
|
44
45
|
patterns/code-organization.md patterns/backend-patterns.md
|
|
@@ -104,7 +105,9 @@ what it says` — and cite from it. A draft written mid-scan is a draft written
|
|
|
104
105
|
analyzer configs (`.stylecop`, `.globalconfig`, `Directory.Build.props` rules such as
|
|
105
106
|
`TreatWarningsAsErrors`, `Nullable`, banned-API lists) · `tsconfig*.json` strictness ·
|
|
106
107
|
`BannedSymbols.txt`, `FLAKY-TESTS.md` · CODEOWNERS · `.husky/`, `.githooks/` · PR templates · the
|
|
107
|
-
existing `docs/` tree · `.claude/settings.json
|
|
108
|
+
existing `docs/` tree · `.claude/settings.json` · an index the repository already keeps of every doc (a
|
|
109
|
+
`docs/INDEX.md` or `docs/index.md` that lists them, a `docs/SUMMARY.md`, a `mkdocs.yml` nav, a
|
|
110
|
+
docusaurus or vitepress sidebar).
|
|
108
111
|
|
|
109
112
|
## The write order — create-if-missing at every step
|
|
110
113
|
|
|
@@ -134,8 +137,12 @@ not. `init` never overwrites, never reorders, never deletes.
|
|
|
134
137
|
Evidence per concern: a `generated/` folder or codegen config; a state library; a UI library; a tokens
|
|
135
138
|
file or token layer; a saga / workflow / state-machine mechanism; a realtime library on either side; a
|
|
136
139
|
queue, worker or scheduler.
|
|
137
|
-
5. `docs/_meta/doc-index.md` from its template, LAST,
|
|
138
|
-
|
|
140
|
+
5. `docs/_meta/doc-index.md` from its template, LAST, with its shape line (`docs/_meta/docs-workflow.md`
|
|
141
|
+
§ The repository-owned sections, the menu). **`menu=all`** when the repository keeps no other index: the
|
|
142
|
+
menu lists every doc that now exists under the four living folders (drafted or repo-owned). **`menu=curated;
|
|
143
|
+
index={path}`** when pass 4 found an index the repository already keeps of every doc: the menu lists only
|
|
144
|
+
the docs `init` drafted, and the report says the owner adds each further doc that meets the loop's
|
|
145
|
+
standard. Either way, the three repo slots are filled from the scan.
|
|
139
146
|
6. The Start-here stanza (`templates/claude-md-stanza.md`) appended to `CLAUDE.md` (created if absent)
|
|
140
147
|
and to `AGENTS.md` (only if present); skipped when the opening marker
|
|
141
148
|
`<!-- naswerks-loop:start-here -->` is already there.
|
|
@@ -187,9 +194,11 @@ FAIL, so a CI job or a control plane can call it.
|
|
|
187
194
|
| each repository-owned heading, in its file | — | unmarked (an earlier spelling) or missing — `init refresh` marks or adds it | present |
|
|
188
195
|
| each item of § This repository's rails (repository-owned) | — | missing, or still its absent-line | recorded |
|
|
189
196
|
| `_meta/doc-index.md` and its three slot sections | missing | a slot carries only the absent-line | filled |
|
|
197
|
+
| the menu's shape line | an unknown shape | a curated menu names no index, or an index that does not exist | `menu=all` (also a menu with no shape line), or `menu=curated` with its index |
|
|
198
|
+
| every living doc a `menu=all` menu should list | — | a doc under the four living folders the menu does not name | named |
|
|
199
|
+
| every doc a menu names | the path does not exist (a row saying `not detected` is exempt) | on a curated menu: no status in either header form, the word `draft`, or no `## Key Files` | meets the loop's standard |
|
|
190
200
|
| the four floor docs | missing | the word `draft` (read from either header form), or an `<!-- init: -->` left in | a decided status |
|
|
191
201
|
| the seven standard docs | absent AND no doc-index row saying `not detected` | the word `draft` when present; absent with its row | present and decided, or absent with its row |
|
|
192
|
-
| every path a § Role reading lists row names | the row's path does not exist | — | resolves |
|
|
193
202
|
| `CLAUDE.md` / `AGENTS.md` stanza · `.claude/settings.json` pin | — | missing | present |
|
|
194
203
|
| `verify-staged` | — | not on PATH (the row carries the fallback) | resolvable |
|
|
195
204
|
| every `docs/research/*/00-ignition-brief.md` | the five problems the control plane's parser emits (no `## The kick`; the kick has no blockquote; no `## The sequence`; the sequence holds no table; a row names no kind from `coordinator build fixit review retro witness docs-process`), or a spec a row names is missing from disk | — | clean |
|
|
@@ -118,13 +118,46 @@ function docRow(relPath, required) {
|
|
|
118
118
|
if (/<!-- init:/.test(text)) row('DEGRADE', `docs/${relPath}`, 'an <!-- init: --> instruction was left in the file');
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
// ── 4b.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
// ── 4b. the menu: its shape, and every doc it names ────────────────────────────────────────────
|
|
122
|
+
// doc-index.md declares one of two shapes (docs-workflow.md § The repository-owned sections): menu=all lists
|
|
123
|
+
// every living doc; menu=curated lists the docs the seats read and names the index that lists every doc. A
|
|
124
|
+
// menu with no shape line is menu=all. Every doc a menu names must exist (a row saying `not detected` is
|
|
125
|
+
// exempt); a curated menu's docs must also meet the loop's standard: a status in either header form, not
|
|
126
|
+
// draft, and a `## Key Files` section.
|
|
127
|
+
{
|
|
128
|
+
const menu = read(join(repo, 'docs', '_meta', 'doc-index.md'));
|
|
129
|
+
if (menu !== null) {
|
|
130
|
+
const line = /<!-- naswerks-loop: menu=([A-Za-z-]+)(?:;\s*index=(\S+?))?\s*-->/.exec(menu);
|
|
131
|
+
const shape = line ? line[1] : 'all';
|
|
132
|
+
if (!['all', 'curated'].includes(shape)) row('FAIL', 'menu shape', `menu=${shape} is not a shape the loop knows (all | curated)`);
|
|
133
|
+
else if (shape === 'curated') {
|
|
134
|
+
const index = line[2];
|
|
135
|
+
if (!index) row('DEGRADE', 'menu shape', 'menu=curated names no index of every doc, so docs-process cannot say where a new doc is registered');
|
|
136
|
+
else if (!existsSync(join(repo, index))) row('DEGRADE', 'menu shape', `menu=curated; its index ${index} does not exist`);
|
|
137
|
+
else row('ok', 'menu shape', `menu=curated; index=${index}`);
|
|
138
|
+
} else row('ok', 'menu shape', line ? 'menu=all' : 'menu=all (no shape line: the default)');
|
|
139
|
+
|
|
140
|
+
const named = new Set();
|
|
141
|
+
for (const l of menu.split('\n')) {
|
|
142
|
+
if (/not detected/i.test(l)) continue;
|
|
143
|
+
for (const m of l.matchAll(/(?:\]\(|`)(?:\.\.\/)?((?:guides|patterns|infrastructure|features)\/[A-Za-z0-9_./-]+\.md)/g)) named.add(m[1]);
|
|
144
|
+
}
|
|
145
|
+
const missing = [...named].filter((p) => !existsSync(join(repo, 'docs', p))).sort();
|
|
146
|
+
for (const p of missing) row('FAIL', `menu: docs/${p}`, 'the menu names a doc that does not exist');
|
|
147
|
+
if (shape === 'curated') {
|
|
148
|
+
for (const p of [...named].filter((q) => !missing.includes(q)).sort()) {
|
|
149
|
+
const text = read(join(repo, 'docs', p));
|
|
150
|
+
const short = [];
|
|
151
|
+
const st = statusOf(text);
|
|
152
|
+
if (st === null) short.push('no status in either header form');
|
|
153
|
+
else if (st.value === 'draft') short.push('status draft');
|
|
154
|
+
if (!/^## Key Files\b/m.test(text)) short.push('no ## Key Files');
|
|
155
|
+
row(short.length ? 'DEGRADE' : 'ok', `menu: docs/${p}`, short.length ? `below the loop's standard: ${short.join(', ')}` : "meets the loop's standard");
|
|
156
|
+
}
|
|
157
|
+
} else if (shape === 'all') {
|
|
158
|
+
const unnamed = livingDocs().filter((p) => !named.has(p) && !menu.includes(p));
|
|
159
|
+
for (const p of unnamed) row('DEGRADE', `menu: docs/${p}`, 'a living doc the menu does not name (menu=all) — add its row, or declare the menu curated');
|
|
160
|
+
if (!unnamed.length && !missing.length) row('ok', 'menu entries', `names every living doc (${named.size}), each present`);
|
|
128
161
|
}
|
|
129
162
|
}
|
|
130
163
|
}
|
|
@@ -249,6 +282,24 @@ function frontmatterOf(text) {
|
|
|
249
282
|
}
|
|
250
283
|
return out;
|
|
251
284
|
}
|
|
285
|
+
// The living docs a menu=all menu lists: every .md directly under the four living folders, and each
|
|
286
|
+
// feature folder's own docs one level down (a filed effort's notes sit deeper and are history, not living).
|
|
287
|
+
function livingDocs() {
|
|
288
|
+
const out = [];
|
|
289
|
+
for (const f of ['guides', 'patterns', 'infrastructure', 'features']) {
|
|
290
|
+
const dir = join(repo, 'docs', f);
|
|
291
|
+
if (!existsSync(dir)) continue;
|
|
292
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
293
|
+
if (e.isFile() && e.name.endsWith('.md') && e.name !== 'README.md') out.push(`${f}/${e.name}`);
|
|
294
|
+
else if (f === 'features' && e.isDirectory()) {
|
|
295
|
+
for (const g of readdirSync(join(dir, e.name), { withFileTypes: true })) {
|
|
296
|
+
if (g.isFile() && g.name.endsWith('.md') && g.name !== 'README.md') out.push(`features/${e.name}/${g.name}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return out.sort();
|
|
302
|
+
}
|
|
252
303
|
function firstBodyLine(text) {
|
|
253
304
|
let body = text;
|
|
254
305
|
if (text.startsWith('---\n')) { const end = text.indexOf('\n---', 4); if (end >= 0) body = text.slice(end + 4).replace(/^\n+/, ''); }
|
|
@@ -53,7 +53,7 @@ Do this immediately, whatever your assignment turns out to be. It needs no role
|
|
|
53
53
|
|
|
54
54
|
| Doc | What it gives you |
|
|
55
55
|
|---|---|
|
|
56
|
-
| `docs/_meta/doc-index.md` | the menu — which living doc answers which question |
|
|
56
|
+
| `docs/_meta/doc-index.md` | the menu — which living doc answers which question: every living doc, or on a curated menu the ones meant for seats (its shape line says which) |
|
|
57
57
|
| `docs/_meta/docs-workflow.md` | the folders, the templates, the two loops — and, in its sections marked (repository-owned), this repository's doc header and where an effort is filed |
|
|
58
58
|
| `docs/_meta/engineering-loop.md` | the seats, the ceremony, **the comms protocol you follow** — and this repository's rails (repository-owned) |
|
|
59
59
|
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
> The menu. Read this at session start, pick the 2-3 docs that match your task, read those for context.
|
|
4
4
|
> This is the curated short list — the compiled layer you read *before* diving into source.
|
|
5
|
+
<!-- naswerks-loop: menu=all -->
|
|
6
|
+
<!-- init: the shape line above stays in the output. It says menu=all unless the scan found an index the
|
|
7
|
+
repository already keeps of every doc (a docs/INDEX.md or docs/index.md that lists them, a
|
|
8
|
+
docs/SUMMARY.md, a mkdocs.yml nav, a docusaurus or vitepress sidebar); then it says
|
|
9
|
+
menu=curated; index={that path}, and the tables below list only the docs init drafted plus the Meta
|
|
10
|
+
rows. The owner adds each further doc that meets the loop's standard. -->
|
|
5
11
|
|
|
6
12
|
---
|
|
7
13
|
|