@naswerks/engineering-loop 0.5.0 → 0.6.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 +58 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/references/docs-workflow.md +15 -13
- package/references/engineering-loop.md +32 -31
- package/skills/docs-process/SKILL.md +2 -1
- package/skills/docs-status/SKILL.md +2 -1
- package/skills/init/SKILL.md +3 -3
- package/skills/init/scripts/doctor.mjs +13 -10
- package/skills/init/scripts/owned.mjs +52 -0
- package/skills/init/scripts/refresh.mjs +3 -2
- package/skills/{spec-child → spec-build}/SKILL.md +6 -6
- package/skills/{spec-parent → spec-coordinator}/SKILL.md +32 -27
- package/skills/spec-ignite/SKILL.md +5 -5
- package/skills/spec-pipeline/SKILL.md +7 -7
- package/skills/spec-retro/SKILL.md +18 -15
- package/skills/spec-review/SKILL.md +7 -6
- package/skills/spec-seat/SKILL.md +39 -34
- package/skills/spec-witness/SKILL.md +8 -5
- package/templates/docs/_meta/doc-index.md +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "naswerks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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,64 @@ version is the one in `package.json` and `.claude-plugin/plugin.json`, which mus
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.6.0] - 2026-09-29
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **Two skills are named for the seat kinds that run them.** `spec-parent` is now `spec-coordinator`, and
|
|
13
|
+
`spec-child` is now `spec-build`. A spec seat's skill is `spec-{kind}` for every kind; `fixit` rides
|
|
14
|
+
`spec-build`, and `docs-process` is its own skill. Invoke them as `/naswerks:spec-coordinator` and
|
|
15
|
+
`/naswerks:spec-build`. A control plane that seeds skills by name seeds the new names, with this version
|
|
16
|
+
as its pack floor.
|
|
17
|
+
- **The coordinator's voice is spelled `COORDINATOR`.** The anchors are `## COORDINATOR RULING`,
|
|
18
|
+
`## COORDINATOR NOTE` and `## COORDINATOR ACCEPT` (beside `## OPERATOR RULING`), the ruling templates open
|
|
19
|
+
`COORDINATOR RESPONSE`, and a seat's own anchors are `## [seat·…] CLOSE-OUT RECEIPT` and
|
|
20
|
+
`## [seat·…] HOLDING`. A board written before this version spells them `## PARENT …` and `[child·…]`:
|
|
21
|
+
every grep the skills run reads both spellings, a parser that reads the anchors should too, and no seat
|
|
22
|
+
rewrites a heading already on its board.
|
|
23
|
+
- The coordinator's close-out audit is filed as `sessions/coordinator-retrospective.md`; a reader takes
|
|
24
|
+
`sessions/parent-retrospective.md` in an effort begun before this version.
|
|
25
|
+
- `spec-coordinator` no longer names a `parallel` spawn argument: a shared worktree holds one live seat at a
|
|
26
|
+
time, and parallel seats need separate worktrees, decided when the pipeline is created.
|
|
27
|
+
- The retired-vocabulary census retires `parent` beside `chain` and `child`.
|
|
28
|
+
- `docs-workflow.md` says the ignition brief is written whole at authoring and verified at ignition. The
|
|
29
|
+
sentence sits in § Templates, which a copy owns: see Upgrading, step 3.
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- `pins/names.test.mjs`: every skill name the pack writes is a skill it carries, and every seat kind runs a
|
|
34
|
+
skill named for it. The kinds, their skills and the words shaped like a skill name that name none are one
|
|
35
|
+
list in `skills/init/scripts/owned.mjs`, which the doctor reads too.
|
|
36
|
+
- `init doctor` degrades a brief whose `## The kick` names a skill this pack does not carry, and names the
|
|
37
|
+
coordinator's skill.
|
|
38
|
+
|
|
39
|
+
### Upgrading from 0.5
|
|
40
|
+
|
|
41
|
+
1. Take the release (`claude plugin marketplace update engineering-loop`, then
|
|
42
|
+
`claude plugin update naswerks@engineering-loop`) between efforts: a session that invokes `spec-parent`
|
|
43
|
+
or `spec-child` after the update finds no such skill.
|
|
44
|
+
2. Re-copy the loop text of both references into `docs/_meta/`, keeping your repository-owned sections;
|
|
45
|
+
`init refresh` restamps and never touches loop text.
|
|
46
|
+
3. Three places are yours, so neither `init refresh` nor a re-copy reaches them. Change each one that still
|
|
47
|
+
carries the old text:
|
|
48
|
+
- § The archive convention (repository-owned): `parent-retrospective.md` is `coordinator-retrospective.md`.
|
|
49
|
+
- § Templates (repository-owned): the ignition brief is "the coordinator seat's standing orders for the
|
|
50
|
+
run — written whole at authoring, verified at ignition" (it read "the parent seat's standing orders
|
|
51
|
+
for the run — two sections completed at ignition").
|
|
52
|
+
- `docs/_meta/doc-index.md`: the build-seat paragraph `init` wrote from its template names `spec-build`
|
|
53
|
+
(it read `spec-child`).
|
|
54
|
+
4. Run `init doctor`. A brief whose kick still names a retired skill shows a DEGRADE row; change the kick.
|
|
55
|
+
|
|
56
|
+
## [0.5.1] - 2026-09-24
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- A doc on a curated menu meets the loop's standard when it names the code it describes in either form: a
|
|
61
|
+
`## Key Files` section, or a frontmatter `related_files` list. The doctor checked for the heading alone and
|
|
62
|
+
flagged docs that name their code in frontmatter.
|
|
63
|
+
- A patch release never asks a copy of the method docs to change, so `init doctor` and `init refresh` read a
|
|
64
|
+
stamp as behind only when its minor version is.
|
|
65
|
+
|
|
8
66
|
## [0.5.0] - 2026-09-24
|
|
9
67
|
|
|
10
68
|
### Added
|
package/README.md
CHANGED
|
@@ -69,9 +69,9 @@ and never touches the rest of the text.
|
|
|
69
69
|
| `docs-process` | docs | patch living docs from working docs, verified against code, then archive |
|
|
70
70
|
| `docs-audit-feature` · `docs-audit-full` | docs | audit living docs against code |
|
|
71
71
|
| `spec-pipeline` | spec | explode an approved plan into numbered specs |
|
|
72
|
-
| `spec-
|
|
72
|
+
| `spec-coordinator` | spec | the coordinator seat |
|
|
73
73
|
| `spec-seat` | spec | the contract every armed seat follows |
|
|
74
|
-
| `spec-
|
|
74
|
+
| `spec-build` | spec | the build job for one spec |
|
|
75
75
|
| `spec-review` | spec | the outside look before the retrospective |
|
|
76
76
|
| `spec-retro` | spec | the coordinator's close-out audit |
|
|
77
77
|
| `spec-witness` | spec | a read-only verifier |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naswerks/engineering-loop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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.6.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.
|
|
@@ -110,8 +110,9 @@ it once and never again. A line near its top says which of two shapes it has, an
|
|
|
110
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
111
|
|
|
112
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
|
|
114
|
-
checks exactly that, for every doc
|
|
113
|
+
prescribes at a decided status (not the word for `draft`), and it names the code it describes — a
|
|
114
|
+
`## Key Files` section, or a frontmatter `related_files` list. `init doctor` checks exactly that, for every doc
|
|
115
|
+
a curated menu names.
|
|
115
116
|
|
|
116
117
|
**Silence.** A section that says nothing about an item leaves the loop's default: a menu with no shape line
|
|
117
118
|
lists every living doc (`menu=all`), no gate runs on a commit, the base branch is the remote's
|
|
@@ -148,7 +149,8 @@ Keep what the skills need to find, change the words to yours:
|
|
|
148
149
|
the menu then lists only the docs its seats read, each meeting the loop's standard, and every other doc
|
|
149
150
|
stays in the repository's own index, kept the way it already is.
|
|
150
151
|
|
|
151
|
-
**The version stamp** on line 1 names the pack version whose repository-owned sections this copy carries.
|
|
152
|
+
**The version stamp** on line 1 names the pack version whose repository-owned sections this copy carries. A
|
|
153
|
+
patch release never asks a copy to change, so a stamp is behind only when its minor version is.
|
|
152
154
|
The loop text around them is this repository's copy from whenever it was taken: `init doctor` never grades
|
|
153
155
|
it, and `init refresh` never touches it — refresh adds a section or a rails item a newer pack reads, and
|
|
154
156
|
restamps.
|
|
@@ -202,9 +204,9 @@ one** (via `spec-pipeline`). The trigger for the layout is simply *"does this ef
|
|
|
202
204
|
(TWO files — questions ride the PLAN TLDR, and there are no
|
|
203
205
|
status snapshots, because the tracker is committed live and
|
|
204
206
|
its git history carries every revision)
|
|
205
|
-
code-review.md
|
|
206
|
-
|
|
207
|
-
|
|
207
|
+
code-review.md the review seat's outside look, if
|
|
208
|
+
the pipeline had a review row
|
|
209
|
+
coordinator-retrospective.md the coordinator's inside look
|
|
208
210
|
These are TESTIMONY: an archive pass repairs links elsewhere and
|
|
209
211
|
leaves sessions/** byte-untouched. Older archived sessions may
|
|
210
212
|
still hold questions.md / status-after-*.md; that is historical
|
|
@@ -428,8 +430,8 @@ once the code is fixed. Keep entries one line, dated, and pointing at a `file:li
|
|
|
428
430
|
### Spec doc (a numbered pipeline — `spec-pipeline`)
|
|
429
431
|
|
|
430
432
|
When a research idea graduates into a real plan, `spec-pipeline` explodes it into a numbered pipeline in
|
|
431
|
-
`docs/research/{topic}/`: `00-ignition-brief.md` (the
|
|
432
|
-
|
|
433
|
+
`docs/research/{topic}/`: `00-ignition-brief.md` (the coordinator seat's standing orders for the run —
|
|
434
|
+
written whole at authoring, verified at ignition) · `01-{topic}.md` (the umbrella) · `02..NN-{slice}.md` (**one
|
|
433
435
|
self-contained spec per planned session**) · `project-status.md` (the tracker, which carries its own
|
|
434
436
|
section notes) · `live-checks.md` (the human's verification rows).
|
|
435
437
|
Each per-session spec uses this template (carries `Builds on`/`Source:` so the chain stays navigable):
|
|
@@ -470,13 +472,13 @@ come from this copy, so a repository changes them once, here.
|
|
|
470
472
|
|-------|------|-------------|
|
|
471
473
|
| `docs-backlog` | Have an idea | Capture it as a research doc in `research/` |
|
|
472
474
|
| `spec-pipeline` | A research idea is graduating into a real plan | Explode the plan into a numbered spec pipeline (umbrella + one self-contained spec per session + `project-status`) in `research/{topic}/`; the plan becomes ephemeral |
|
|
473
|
-
| `spec-
|
|
475
|
+
| `spec-coordinator` | Right after `spec-pipeline` (same session, context-loaded) or to resume an effort fresh | Assume the coordinator seat: cut seat prompts (one per spec), review/verify TLDRs, give commit calls, own `project-status.md` — the same loop hand-cranked or hosted |
|
|
474
476
|
| `spec-seat` | **Every** seat a coordinator arms — invoked FIRST, before the job skill | The CONTRACT, not a seat: the shared half of every seat's procedure — beliefs, plain-text PLAN TLDR, numbered questions w/ recommendations, six-section FINAL TLDR, commit only on the relayed call |
|
|
475
|
-
| `spec-
|
|
477
|
+
| `spec-build` | Igniting a build or fixit seat — manually or platform-spawned, after `spec-seat` | The BUILD JOB only: one spec, the role's reading list, the pin taxonomy, the suite protocol; `fixit` variant for no-spec defect sessions. Its ceremony comes from `spec-seat` |
|
|
476
478
|
| `spec-witness` | A pipeline looks wedged, actors' claims conflict, or you want a substrate-verified state report | The read-only fourth seat: the control plane's read lanes, transcripts, git; zero writes, zero rulings — the seats act, the human judges, the witness testifies |
|
|
477
|
-
| `spec-ignite` | Right after `spec-pipeline` when the pipeline runs HOSTED (vs `spec-
|
|
479
|
+
| `spec-ignite` | Right after `spec-pipeline` when the pipeline runs HOSTED (vs `spec-coordinator` for hand-cranked) | The ignition copilot: verify the committed brief, derive the stage card (the human clicks Create), "kick it", a hosted coordinator runs the pipeline; two verbal gates, everything announced, every UI gap a named finding |
|
|
478
480
|
| `spec-review` | A pipeline's builds are done and it has a review row | The outside look, before the retro and before the PR: code + patterns + composition across session seams + the loop's own comms, in two independent passes. Files `sessions/code-review.md` and classifies which findings earn a fixit. **Its value is having none of the pipeline's context** |
|
|
479
|
-
| `spec-retro` | The
|
|
481
|
+
| `spec-retro` | The coordinator's LAST act, after any review and any fixit | The inside look: a substrate-verified grade, the ceremony audit (did the acts the seat believes it performed actually write rows?), the ledger reconciliation, the true economics, the blindnesses, and the ranked fixit backlog |
|
|
480
482
|
| `docs-status` | Session start | Report what's recently archived, what's in `working/`, what's in `research/`; sweep the meta-comments for the condensed status table |
|
|
481
483
|
| `docs-write` | End of a session | Finalize the working doc; it stays in `working/` (records its `## Archive` target) |
|
|
482
484
|
| `docs-process` | Start of a session, `working/` has docs | Patch living docs from the working doc's checklist (verified vs code), then file the doc into `archive/` |
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
<!-- naswerks-loop: version=0.
|
|
1
|
+
<!-- naswerks-loop: version=0.6.0 -->
|
|
2
2
|
# The Engineering Loop — flow, seats, skills, and the ceremony
|
|
3
3
|
|
|
4
4
|
> The quick intro to how work gets built with this loop: the two loops (`docs-*` knowledge, `spec-*`
|
|
5
5
|
> execution), the seats (Human / Coordinator / Seat, plus the read-only Witness), the ceremony per
|
|
6
6
|
> session, and a bare-bones exchange showing the back-and-forth. Point newcomers at this plus
|
|
7
7
|
> [doc-index](doc-index.md) + [docs-workflow](docs-workflow.md). Operational templates live in the
|
|
8
|
-
> `spec-
|
|
8
|
+
> `spec-coordinator` skill; the seat contract in `spec-seat`.
|
|
9
9
|
<!-- meta: type=guide; status=current; verified={date}; lineage=1 -->
|
|
10
10
|
|
|
11
11
|
---
|
|
@@ -38,7 +38,7 @@ Plan graduates -> spec-pipeline -> docs/research/{topic}/: umbr
|
|
|
38
38
|
per session + project-status.md (the plan is
|
|
39
39
|
now throwaway — the specs carry every crumb)
|
|
40
40
|
Same session — CHOOSE THE SEAT:
|
|
41
|
-
hand-cranked -> spec-
|
|
41
|
+
hand-cranked -> spec-coordinator -> this session becomes the COORDINATOR and cuts
|
|
42
42
|
Prompt #1 (you relay every message)
|
|
43
43
|
hosted -> spec-ignite -> this session becomes the IGNITION COPILOT:
|
|
44
44
|
verify the brief, derive the stage card, YOU
|
|
@@ -108,13 +108,13 @@ What a commit and a PR pass here, beyond the loop. These add to the Hard rules b
|
|
|
108
108
|
just *plan, build, `docs-write`, `docs-process`* — no pipeline, no coordinator, a flat working
|
|
109
109
|
doc. This is the default and it never requires a control plane.
|
|
110
110
|
- **`spec-*` is the execution loop** for work that is more than one session: `spec-pipeline`
|
|
111
|
-
graduates the plan, `spec-
|
|
111
|
+
graduates the plan, `spec-coordinator` takes the seat, build seats build one spec each. **Each spec is sized
|
|
112
112
|
for ONE session** — recon, plan, build, suite and FINAL TLDR before the session's context compacts —
|
|
113
113
|
and the spec count falls out of that: a pipeline of two specs is a pipeline, and so is one of ten. It
|
|
114
114
|
hands off to the knowledge loop at both ends (research docs in, working docs out).
|
|
115
115
|
- **Either loop runs by hand or hosted.** The manual version is this document's ceremony with the human
|
|
116
116
|
relaying; the hosted version is the control plane running the same ceremony, machine-relayed. When they
|
|
117
|
-
drift, this doc + the `spec-
|
|
117
|
+
drift, this doc + the `spec-coordinator` skill are the requirements; the control plane follows.
|
|
118
118
|
|
|
119
119
|
## The seats
|
|
120
120
|
|
|
@@ -126,7 +126,7 @@ What a commit and a PR pass here, beyond the loop. These add to the Hard rules b
|
|
|
126
126
|
| **Witness** *(optional, `spec-witness`)* | a read-only session with substrate access | testifies from rows/transcripts/git when claims conflict or a pipeline looks wedged; answers "whose turn is it" | writes anything; injects anything; rules on anything |
|
|
127
127
|
|
|
128
128
|
The seat, not the chat, is the product: `project-status.md` must stay current enough that a
|
|
129
|
-
fresh session can resume as coordinator losslessly (`spec-
|
|
129
|
+
fresh session can resume as coordinator losslessly (`spec-coordinator {topic}`).
|
|
130
130
|
|
|
131
131
|
## The skills
|
|
132
132
|
|
|
@@ -138,10 +138,10 @@ fresh session can resume as coordinator losslessly (`spec-parent {topic}`).
|
|
|
138
138
|
| `docs-process` | docs | effort done, fresh session | patch living docs (verified vs code), archive |
|
|
139
139
|
| `docs-audit-feature` / `docs-audit-full` | docs | doc suspected stale / monthly | deep-audit docs against code |
|
|
140
140
|
| `spec-pipeline` | spec | a plan came through and you're committing to it | explode the plan into a numbered spec pipeline — *spare no crumb* |
|
|
141
|
-
| `spec-
|
|
142
|
-
| `spec-seat` | spec | **every** seat the coordinator arms, invoked FIRST | **the CONTRACT, not a seat** — the shared half of every seat's procedure. `spec-
|
|
143
|
-
| `spec-
|
|
144
|
-
| `spec-ignite` | spec | when an authored pipeline runs HOSTED (the `spec-
|
|
141
|
+
| `spec-coordinator` | spec | right after `spec-pipeline` (same session) or fresh with the topic | assume the coordinator seat |
|
|
142
|
+
| `spec-seat` | spec | **every** seat the coordinator arms, invoked FIRST | **the CONTRACT, not a seat** — the shared half of every seat's procedure. `spec-coordinator` does NOT compose it: the coordinator writes rulings rather than receiving them |
|
|
143
|
+
| `spec-build` | spec | igniting a build or fixit seat — manual (`spec-build {role} {spec}`) or hosted | **the BUILD JOB only** — one spec, the role's adds, the pin taxonomy, the suite protocol, the fixit variant. Its ceremony comes from `spec-seat` |
|
|
144
|
+
| `spec-ignite` | spec | when an authored pipeline runs HOSTED (the `spec-coordinator` alternative) | the ignition copilot, and it can run COLD: gates, re-verify the brief's baselines + platform state against live HEAD, derive the stage card (human clicks Create), "kick it" with the brief's own kick text as the task; every UI gap a named finding |
|
|
145
145
|
| `spec-review` | spec | a pipeline's builds are done, if it has a review row | the OUTSIDE look, before the retro and before the PR: correctness/security/**composition across session seams** · the patterns, with every departure identified · the loop's own comms. Two independent passes, consolidated. Classifies which findings earn a fixit; **its value is having none of the pipeline's context** |
|
|
146
146
|
| `spec-retro` | spec | the coordinator's LAST act, after any review and any fixit | the INSIDE look: a substrate-verified grade, the ceremony audit (did the acts write rows?), the ledger reconciliation, the true economics, the blindnesses, the ranked fixit backlog — *a retrospective that cannot indict its own author is marketing* |
|
|
147
147
|
| `spec-witness` | spec | a pipeline looks wedged, or actors' claims conflict | the read-only fourth seat: substrate access, zero writes, zero rulings |
|
|
@@ -155,7 +155,7 @@ The thing that keeps prompts and skills from drifting apart:
|
|
|
155
155
|
|---|---|---|
|
|
156
156
|
| **POLICY** | the policy document § This repository's rails names — and, hosted, the un-skippable system-prompt suffix the control plane supplies at open | cannot be skipped, present on every turn |
|
|
157
157
|
| **PROCEDURE — shared** | `spec-seat` | the contract for ANY seat the coordinator arms: blackboard, question lane, parked-call discriminator, verdicts, BELIEFS sha, git shapes, six-section FINAL TLDR, close-out order |
|
|
158
|
-
| **PROCEDURE — per job** | `spec-
|
|
158
|
+
| **PROCEDURE — per job** | `spec-build` · `spec-review` · `docs-process` | what THIS seat does. The job skill **refines** the seat contract and never contradicts it; a genuine conflict is a finding, not a fork |
|
|
159
159
|
| **TASK** | the seed prompt + the pipeline's `00-ignition-brief.md` | per-run; dies with the run |
|
|
160
160
|
|
|
161
161
|
A seed's first act is therefore to **invoke its skills** — `spec-seat`, then the job — not to restate them.
|
|
@@ -174,7 +174,7 @@ diff.
|
|
|
174
174
|
## The ceremony (one build session)
|
|
175
175
|
|
|
176
176
|
1. **Coordinator cuts the seat prompt** from the PREVIOUS session's actual shipped shapes — never
|
|
177
|
-
from the spec's guesses alone. (Template: `spec-
|
|
177
|
+
from the spec's guesses alone. (Template: `spec-coordinator` skill.)
|
|
178
178
|
2. **Seat recons, states its beliefs** (HEAD sha, dirty files, versions, baselines), then a
|
|
179
179
|
**PLAN TLDR as plain text** — before any plan-approval presentation — with **questions as
|
|
180
180
|
numbered options + an embedded recommendation, then STOP**. Never an interactive question tool.
|
|
@@ -208,7 +208,7 @@ COORDINATOR -> Prompt #N: docs preamble, ##PROMPT##, ONE spec + context docs + p
|
|
|
208
208
|
+ CEREMONY block
|
|
209
209
|
SEAT -> PLAN TLDR (BELIEFS: head/dirty/versions/baselines · the plan, 5-10 lines ·
|
|
210
210
|
QUESTIONS 1..n, options + recommendation), STOP
|
|
211
|
-
COORDINATOR ->
|
|
211
|
+
COORDINATOR -> COORDINATOR RESPONSE — PLAN {ACCEPTED / ACCEPTED-WITH-RIDERS / …} · rulings 1..n
|
|
212
212
|
(DECISION — why) · riders · "Go build."
|
|
213
213
|
SEAT -> (builds free; mid-run question: numbered text, STOP; COORDINATOR ruling; resume)
|
|
214
214
|
SEAT -> FINAL TLDR — (a) SHIPPED+VERIFICATION (b) VERBATIM RUNNER SUMMARIES (c) HONEST GAPS
|
|
@@ -225,7 +225,7 @@ The exchange above is not just chat — it is a pair of FILES per build session:
|
|
|
225
225
|
|
|
226
226
|
- **`tldr.md` — the chat.** Append-only, two voices: the seat's PLAN TLDR + numbered
|
|
227
227
|
questions, the coordinator's ruling appended beneath them, optional mid-build updates +
|
|
228
|
-
`##
|
|
228
|
+
`## COORDINATOR NOTE`s, then the FINAL TLDR. Read top-to-bottom it IS the session's whole
|
|
229
229
|
conversation, greppable by anchor.
|
|
230
230
|
- **`plan.md` — the document.** The full plan, posted once, corrected on pushback. PULL-only:
|
|
231
231
|
the coordinator reads it on a `read-full-plan` verdict; it is never force-fed into a wake or chat.
|
|
@@ -237,11 +237,11 @@ with the effort:
|
|
|
237
237
|
|
|
238
238
|
```
|
|
239
239
|
docs/research/{topic}/sessions/ <- per-session work in {spec-slug}/, effort-wide at the root
|
|
240
|
-
{spec-slug}/plan.md
|
|
241
|
-
{spec-slug}/tldr.md
|
|
242
|
-
code-review.md
|
|
243
|
-
|
|
244
|
-
witness-ledger-*.md
|
|
240
|
+
{spec-slug}/plan.md each seat the full plan, posted once, corrected on pushback (PULL-only)
|
|
241
|
+
{spec-slug}/tldr.md each seat the two-voice chat — seat turns AND coordinator rulings interleaved
|
|
242
|
+
code-review.md the review the outside look, if the pipeline had a review row
|
|
243
|
+
coordinator-retrospective.md the coord. the close-out audit (the inside look)
|
|
244
|
+
witness-ledger-*.md a human the witness is zero-writes, so its ledger arrives by hand
|
|
245
245
|
```
|
|
246
246
|
|
|
247
247
|
Two files per build session; questions ride the TLDR, and there are no status snapshots because the
|
|
@@ -264,39 +264,40 @@ though it can be fed.**
|
|
|
264
264
|
**The coordinator's voice is NOT in the seat's file on the hosted plane.** A coordinator answers with
|
|
265
265
|
`callout` (its sixth drive verb, machine-written, the only downward path), which persists on its own
|
|
266
266
|
artifact lane and closes the question in the **acts ledger**. So **the file says whether a question was
|
|
267
|
-
ASKED; the ledger says whether it was ANSWERED**, and a seat typing a
|
|
268
|
-
TLDR closes nothing. `callout` **refuses when the charge has no live session** — a coordinator must
|
|
267
|
+
ASKED; the ledger says whether it was ANSWERED**, and a seat typing a coordinator-voice heading into its
|
|
268
|
+
own TLDR closes nothing. `callout` **refuses when the charge has no live session** — a coordinator must
|
|
269
269
|
`seat_spawn` before it can rule — and reports **"RULING RECORDED, NOT DELIVERED"** in those words if the
|
|
270
270
|
transport drops it.
|
|
271
271
|
|
|
272
|
-
The
|
|
273
|
-
there is no rev counter, so the heading is `##
|
|
274
|
-
`##
|
|
272
|
+
The coordinator-voice block (machinery-written hosted; the hand-cranked coordinator writes it —
|
|
273
|
+
hand-cranked there is no rev counter, so the heading is `## COORDINATOR RULING (plan — {verdict})`; the
|
|
274
|
+
stable grep prefix `## COORDINATOR RULING` is the contract in both modes, and an older board's
|
|
275
|
+
`## PARENT RULING` is read as the same heading):
|
|
275
276
|
|
|
276
277
|
```
|
|
277
|
-
##
|
|
278
|
+
## COORDINATOR RULING (plan rev N — {accept | accept-with-riders | blocking-precondition})
|
|
278
279
|
|
|
279
|
-
|
|
280
|
+
COORDINATOR RESPONSE — PLAN TLDR {VERDICT}. {one sentence naming the best thing in the plan}
|
|
280
281
|
Answers:
|
|
281
282
|
1. {RULING IN CAPS} — {decision first, then the why}.
|
|
282
283
|
...
|
|
283
284
|
Go build. Fold the riders in — no re-approval round-trip.
|
|
284
285
|
```
|
|
285
286
|
|
|
286
|
-
Callouts and nudges append as `##
|
|
287
|
+
Callouts and nudges append as `## COORDINATOR NOTE` (actor-suffixed when known, e.g. `(operator)`).
|
|
287
288
|
|
|
288
289
|
**The seat's own two anchors:**
|
|
289
290
|
|
|
290
291
|
```
|
|
291
|
-
## [
|
|
292
|
-
## [
|
|
292
|
+
## [seat·{slug} · run {id}] CLOSE-OUT RECEIPT <- after the push
|
|
293
|
+
## [seat·{slug} · run {id}] HOLDING <- "I have pushed and I am waiting"
|
|
293
294
|
```
|
|
294
295
|
|
|
295
296
|
**A question heading, a HOLDING and a close-out receipt are ALL non-staling** — the gates bind an
|
|
296
297
|
acceptance to the accepted CONTENT, not to the revision number, so appending your own *state* leaves it
|
|
297
298
|
intact. Only **new content** stales. A seat that has pushed and is waiting is otherwise indistinguishable
|
|
298
299
|
from a seat that has done nothing, and a coordinator reading a wake led by its own acceptance concludes
|
|
299
|
-
there is nothing to rule — the HOLDING anchor is what prevents that. The token is the literal `
|
|
300
|
+
there is nothing to rule — the HOLDING anchor is what prevents that. The token is the literal `seat·`
|
|
300
301
|
whatever seat you are — it is a protocol anchor, not a description of you.
|
|
301
302
|
|
|
302
303
|
### Questions ride the same blackboard
|
|
@@ -321,4 +322,4 @@ the credential: **a receipt the audited party can mint is not a receipt** — wh
|
|
|
321
322
|
door takes the human's own token and never a seat's job ticket.
|
|
322
323
|
|
|
323
324
|
## Lineage
|
|
324
|
-
- **Related** — [docs-workflow](docs-workflow.md) · [doc-index](doc-index.md) · the `spec-
|
|
325
|
+
- **Related** — [docs-workflow](docs-workflow.md) · [doc-index](doc-index.md) · the `spec-coordinator` and `spec-seat` skills (the operational templates and the seat contract)
|
|
@@ -45,7 +45,8 @@ branch, which is the truth for that PR, and you say so at close.
|
|
|
45
45
|
the seat that does it.
|
|
46
46
|
|
|
47
47
|
**If the effort was a spec pipeline, you have a SECOND input: `sessions/code-review.md`** — the review
|
|
48
|
-
seat's outside look, filed beside `
|
|
48
|
+
seat's outside look, filed beside the coordinator's retrospective (`coordinator-retrospective.md`, or
|
|
49
|
+
`parent-retrospective.md` in an effort begun before that name). Read both. **Every review finding gets a
|
|
49
50
|
disposition, and the disposition is TOTAL:** a living-doc patch here · an existing fixit seat · a
|
|
50
51
|
chartered follow-up · a new spec pipeline · or killed on record with the reason. **A finding with no named
|
|
51
52
|
home is dropped, not deferred** — and the review's own classification (fixit candidate / deferred /
|
|
@@ -39,7 +39,8 @@ just counts.
|
|
|
39
39
|
- **menu drift** — read the menu's shape line (`rg -n 'naswerks-loop: menu=' docs/_meta/doc-index.md`;
|
|
40
40
|
none means `menu=all`). On `menu=all`: a living doc the menu does not name. On `menu=curated`: a doc the
|
|
41
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`
|
|
42
|
+
repository-owned sections — a header at a decided status, and its code named in a `## Key Files`
|
|
43
|
+
section or a frontmatter `related_files` list).
|
|
43
44
|
|
|
44
45
|
5. **Open issues.** Grep living docs for an `## Open Issues` section; list which docs/features carry
|
|
45
46
|
unresolved, code-verified problems (and how many) so known risks surface at orientation instead of
|
package/skills/init/SKILL.md
CHANGED
|
@@ -190,13 +190,13 @@ FAIL, so a CI job or a control plane can call it.
|
|
|
190
190
|
| Row | FAIL | DEGRADE | ok |
|
|
191
191
|
|---|---|---|---|
|
|
192
192
|
| each of the eight folders | missing | README missing | present |
|
|
193
|
-
| `_meta/docs-workflow.md`, `_meta/engineering-loop.md` | missing, or no stamp on line 1 (or on the first line after a frontmatter block) | stamp behind the pack's
|
|
193
|
+
| `_meta/docs-workflow.md`, `_meta/engineering-loop.md` | missing, or no stamp on line 1 (or on the first line after a frontmatter block) | stamp's minor version behind the pack's (`init refresh` restamps; a patch never asks a copy to change) | stamp's minor equals the pack's |
|
|
194
194
|
| each repository-owned heading, in its file | — | unmarked (an earlier spelling) or missing — `init refresh` marks or adds it | present |
|
|
195
195
|
| each item of § This repository's rails (repository-owned) | — | missing, or still its absent-line | recorded |
|
|
196
196
|
| `_meta/doc-index.md` and its three slot sections | missing | a slot carries only the absent-line | filled |
|
|
197
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
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 |
|
|
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 named code (a `## Key Files` section or a frontmatter `related_files` list) | meets the loop's standard |
|
|
200
200
|
| the four floor docs | missing | the word `draft` (read from either header form), or an `<!-- init: -->` left in | a decided status |
|
|
201
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 |
|
|
202
202
|
| `CLAUDE.md` / `AGENTS.md` stanza · `.claude/settings.json` pin | — | missing | present |
|
|
@@ -250,5 +250,5 @@ write, offer — and ask before doing either — to fill each new absent-line fr
|
|
|
250
250
|
- The floor four are always drafted, even in a repository with no frontend — the reading floor must
|
|
251
251
|
RESOLVE for every seat kind, and "No frontend detected — fill this in" is an honest doc. The standard
|
|
252
252
|
seven are drafted only on evidence; their absence is recorded, never papered over.
|
|
253
|
-
- Counterpart skills: `spec-seat` (reads the floor), `spec-
|
|
253
|
+
- Counterpart skills: `spec-seat` (reads the floor), `spec-build` (reads the role rows), `spec-pipeline`
|
|
254
254
|
(writes the brief `doctor` checks), `docs-write` / `docs-process` (the queue `doctor` reads).
|
|
@@ -5,7 +5,7 @@ import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
|
5
5
|
import { join, resolve, dirname } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { execFileSync } from 'node:child_process';
|
|
8
|
-
import { OWNED, compareVersions, linesOf, bare, h2Index, findSection, railsItems } from './owned.mjs';
|
|
8
|
+
import { OWNED, KINDS, KIND_SKILLS, NOT_SKILLS, skillTokens, compareVersions, compareMinor, frontmatterListCount, linesOf, bare, h2Index, findSection, railsItems } from './owned.mjs';
|
|
9
9
|
|
|
10
10
|
const repo = resolve(process.argv[2] || process.cwd());
|
|
11
11
|
const packRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
|
|
@@ -40,9 +40,8 @@ for (const name of ['docs-workflow.md', 'engineering-loop.md']) {
|
|
|
40
40
|
const m = STAMP.exec(firstBodyLine(text));
|
|
41
41
|
if (!m) { row('FAIL', `docs/_meta/${name}`, 'no naswerks-loop stamp on line 1 (or on the first line after a frontmatter block) — an unstamped copy cannot be told from a stale one'); continue; }
|
|
42
42
|
if (!packVersion) { row('DEGRADE', `docs/_meta/${name}`, `stamped ${m[1]}; the pack's own version could not be read`); continue; }
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
else row('ok', `docs/_meta/${name}`, `stamp ${m[1]}`);
|
|
43
|
+
if (compareMinor(m[1], packVersion) < 0) row('DEGRADE', `docs/_meta/${name}`, `stamp ${m[1]} is behind the pack ${packVersion} — run init refresh: it adds this version's repository-owned sections and restamps, and never touches the loop text`);
|
|
44
|
+
else row('ok', `docs/_meta/${name}`, compareVersions(m[1], packVersion) < 0 ? `stamp ${m[1]} (the pack is ${packVersion}: a patch never asks a copy to change)` : `stamp ${m[1]}`);
|
|
46
45
|
|
|
47
46
|
// ── 2b. the repository-owned sections: present under the heading a skill looks them up by ─────────
|
|
48
47
|
const lines = linesOf(text);
|
|
@@ -123,7 +122,7 @@ function docRow(relPath, required) {
|
|
|
123
122
|
// every living doc; menu=curated lists the docs the seats read and names the index that lists every doc. A
|
|
124
123
|
// menu with no shape line is menu=all. Every doc a menu names must exist (a row saying `not detected` is
|
|
125
124
|
// 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.
|
|
125
|
+
// draft, and names the code it describes: a `## Key Files` section, or a frontmatter `related_files` list.
|
|
127
126
|
{
|
|
128
127
|
const menu = read(join(repo, 'docs', '_meta', 'doc-index.md'));
|
|
129
128
|
if (menu !== null) {
|
|
@@ -151,7 +150,7 @@ function docRow(relPath, required) {
|
|
|
151
150
|
const st = statusOf(text);
|
|
152
151
|
if (st === null) short.push('no status in either header form');
|
|
153
152
|
else if (st.value === 'draft') short.push('status draft');
|
|
154
|
-
if (!/^## Key Files\b/m.test(text)) short.push('no ## Key Files');
|
|
153
|
+
if (!/^## Key Files\b/m.test(text) && frontmatterListCount(text, 'related_files') === 0) short.push('names no code (no ## Key Files section, no related_files list)');
|
|
155
154
|
row(short.length ? 'DEGRADE' : 'ok', `menu: docs/${p}`, short.length ? `below the loop's standard: ${short.join(', ')}` : "meets the loop's standard");
|
|
156
155
|
}
|
|
157
156
|
} else if (shape === 'all') {
|
|
@@ -195,7 +194,9 @@ for (const name of ['CLAUDE.md', 'AGENTS.md']) {
|
|
|
195
194
|
}
|
|
196
195
|
|
|
197
196
|
// ── 7. every ignition brief, checked the way the control plane checks it ────────────────────────
|
|
198
|
-
|
|
197
|
+
// The kick is sent verbatim as the coordinator's task, so a skill it names that this pack does not carry
|
|
198
|
+
// (a renamed one, most often) tells the coordinator to invoke nothing.
|
|
199
|
+
const packSkills = new Set(existsSync(join(packRoot, 'skills')) ? readdirSync(join(packRoot, 'skills')) : []);
|
|
199
200
|
const researchDir = join(repo, 'docs', 'research');
|
|
200
201
|
if (existsSync(researchDir)) {
|
|
201
202
|
for (const topic of readdirSync(researchDir)) {
|
|
@@ -206,6 +207,7 @@ if (existsSync(researchDir)) {
|
|
|
206
207
|
const kick = sectionOf(text, '## The kick');
|
|
207
208
|
if (kick === null) problems.push('the brief has no `## The kick` section, so the task text could not be read');
|
|
208
209
|
else if (!kick.split('\n').some((l) => l.trimStart().startsWith('>'))) problems.push('the `## The kick` section carries no blockquote; its whole text stands in for the task');
|
|
210
|
+
const unknown = kick === null || packSkills.size === 0 ? [] : [...new Set(skillTokens(kick))].filter((t) => !packSkills.has(t) && !NOT_SKILLS.includes(t));
|
|
209
211
|
const seq = sectionOf(text, '## The sequence');
|
|
210
212
|
if (seq === null) problems.push('the brief has no `## The sequence` section, so no seats could be read');
|
|
211
213
|
else {
|
|
@@ -214,13 +216,14 @@ if (existsSync(researchDir)) {
|
|
|
214
216
|
for (const line of tableRows.slice(1)) {
|
|
215
217
|
const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => c.trim().replace(/^`|`$/g, '').trim());
|
|
216
218
|
if (cells.every((c) => /^[-: ]*$/.test(c))) continue;
|
|
217
|
-
if (!cells.some((c) => KINDS.
|
|
219
|
+
if (!cells.some((c) => KINDS.includes(c))) { problems.push(`a sequence row names no seat kind and composes nothing: \`${line.trim()}\``); continue; }
|
|
218
220
|
const spec = cells.map((c) => /\b(\d{2}-[A-Za-z0-9._-]*?\.md)\b/.exec(c)).find(Boolean);
|
|
219
221
|
if (spec && !existsSync(join(researchDir, topic, spec[1]))) problems.push(`the sequence names \`${spec[1]}\` and it is missing from disk`);
|
|
220
222
|
}
|
|
221
223
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
+
for (const pr of problems) row('FAIL', `docs/research/${topic}/00-ignition-brief.md`, pr);
|
|
225
|
+
for (const t of unknown) row('DEGRADE', `docs/research/${topic}/00-ignition-brief.md`, `the kick names \`${t}\`, a skill this pack does not carry; the coordinator's skill is \`${KIND_SKILLS.coordinator}\``);
|
|
226
|
+
if (!problems.length && !unknown.length) row('ok', `docs/research/${topic}/00-ignition-brief.md`, 'the kick and the sequence read clean');
|
|
224
227
|
}
|
|
225
228
|
}
|
|
226
229
|
|
|
@@ -21,12 +21,64 @@ export const OWNED = [
|
|
|
21
21
|
|
|
22
22
|
export const STAMP = /^(<!-- naswerks-loop: version=)([0-9]+\.[0-9]+\.[0-9]+[^ ]*)( -->)/;
|
|
23
23
|
|
|
24
|
+
// The seat kinds a pipeline row may name, checked the way the control plane checks a brief's sequence.
|
|
25
|
+
export const KINDS = ['coordinator', 'build', 'fixit', 'review', 'retro', 'witness', 'docs-process'];
|
|
26
|
+
|
|
27
|
+
// The skill each kind runs as its job. A spec seat's skill is named for its kind, so a skill's name says
|
|
28
|
+
// which seat runs it: `fixit` is the build job's no-spec variant, and `docs-process` is the knowledge
|
|
29
|
+
// loop's own skill, seated by a pipeline as its last row.
|
|
30
|
+
export const KIND_SKILLS = {
|
|
31
|
+
coordinator: 'spec-coordinator',
|
|
32
|
+
build: 'spec-build',
|
|
33
|
+
fixit: 'spec-build',
|
|
34
|
+
review: 'spec-review',
|
|
35
|
+
retro: 'spec-retro',
|
|
36
|
+
witness: 'spec-witness',
|
|
37
|
+
'docs-process': 'docs-process',
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Words shaped like a skill name that name no skill: the method doc, the placeholders, and the compounds
|
|
41
|
+
// the prose uses. Every other spec-* or docs-* word the pack writes is a skill it carries.
|
|
42
|
+
export const NOT_SKILLS = ['docs-workflow', 'spec-slug', 'spec-path', 'spec-doc', 'spec-driven', 'docs-only'];
|
|
43
|
+
|
|
44
|
+
// Every skill-shaped word in a text. A word followed by `*` or `-*` names a family (docs-*, docs-audit-*),
|
|
45
|
+
// never one skill.
|
|
46
|
+
export function skillTokens(text) {
|
|
47
|
+
return [...text.matchAll(/\b(?:spec|docs)-[a-z]+(?:-[a-z]+)*\b(?![-*])/g)].map((m) => m[0]);
|
|
48
|
+
}
|
|
49
|
+
|
|
24
50
|
export function compareVersions(a, b) {
|
|
25
51
|
const pa = a.split(/[.-]/).map((x) => parseInt(x, 10) || 0), pb = b.split(/[.-]/).map((x) => parseInt(x, 10) || 0);
|
|
26
52
|
for (let i = 0; i < 3; i++) { if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) - (pb[i] || 0); }
|
|
27
53
|
return 0;
|
|
28
54
|
}
|
|
29
55
|
|
|
56
|
+
// A patch release never changes what a copy of the method docs carries, so a stamp is behind only when its
|
|
57
|
+
// major.minor is.
|
|
58
|
+
export function compareMinor(a, b) {
|
|
59
|
+
const pa = a.split(/[.-]/).map((x) => parseInt(x, 10) || 0), pb = b.split(/[.-]/).map((x) => parseInt(x, 10) || 0);
|
|
60
|
+
for (let i = 0; i < 2; i++) { if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) - (pb[i] || 0); }
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// How many entries a frontmatter key lists: an inline `[a, b]`, indented `- item` lines, or one scalar. Zero
|
|
65
|
+
// when the key is absent or empty.
|
|
66
|
+
export function frontmatterListCount(text, key) {
|
|
67
|
+
const t = text.replace(/\r\n/g, '\n');
|
|
68
|
+
if (!t.startsWith('---\n')) return 0;
|
|
69
|
+
const end = t.indexOf('\n---', 4);
|
|
70
|
+
if (end < 0) return 0;
|
|
71
|
+
const lines = t.slice(4, end).split('\n');
|
|
72
|
+
const i = lines.findIndex((l) => l.startsWith(`${key}:`));
|
|
73
|
+
if (i < 0) return 0;
|
|
74
|
+
const inline = lines[i].slice(key.length + 1).trim();
|
|
75
|
+
if (inline.startsWith('[')) return inline.replace(/^\[|\]$/g, '').split(',').map((x) => x.trim()).filter(Boolean).length;
|
|
76
|
+
if (inline) return 1;
|
|
77
|
+
let n = 0;
|
|
78
|
+
for (let j = i + 1; j < lines.length && /^\s+-\s*\S/.test(lines[j]); j++) n++;
|
|
79
|
+
return n;
|
|
80
|
+
}
|
|
81
|
+
|
|
30
82
|
// A file as lines that keep their own terminators, so an untouched line is written back byte for byte.
|
|
31
83
|
export function linesOf(text) {
|
|
32
84
|
return text.match(/[^\n]*\n|[^\n]+$/g) ?? [];
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
7
7
|
import { join, resolve, dirname } from 'node:path';
|
|
8
8
|
import { fileURLToPath } from 'node:url';
|
|
9
|
-
import { OWNED, STAMP, compareVersions, linesOf, bare, eolOf, h2Index, findSection, railsItems, stampIndex, mapOf } from './owned.mjs';
|
|
9
|
+
import { OWNED, STAMP, compareVersions, compareMinor, linesOf, bare, eolOf, h2Index, findSection, railsItems, stampIndex, mapOf } from './owned.mjs';
|
|
10
10
|
|
|
11
11
|
const args = process.argv.slice(2);
|
|
12
12
|
const write = args.includes('--write');
|
|
@@ -92,13 +92,14 @@ for (const file of ['docs-workflow.md', 'engineering-loop.md']) {
|
|
|
92
92
|
const stampAt = stampIndex(lines);
|
|
93
93
|
const current = STAMP.exec(bare(lines[stampAt]))[2];
|
|
94
94
|
const cmp = compareVersions(current, packVersion);
|
|
95
|
-
if (
|
|
95
|
+
if (compareMinor(current, packVersion) < 0) {
|
|
96
96
|
const raw = lines[stampAt];
|
|
97
97
|
const bom = raw.startsWith('') ? '' : '';
|
|
98
98
|
lines[stampAt] = bom + raw.slice(bom.length).replace(STAMP, `$1${packVersion}$3`);
|
|
99
99
|
row(shown, 'stamp', `${current} to ${packVersion}`);
|
|
100
100
|
changed = true;
|
|
101
101
|
} else if (cmp > 0) row(shown, 'stamp', `${current} — ahead of this pack; left as it is`);
|
|
102
|
+
else if (cmp < 0) row(shown, 'stamp', `${current} — the pack is ${packVersion}, a patch: a patch never asks a copy to change`);
|
|
102
103
|
else row(shown, 'stamp', `${current} — current`);
|
|
103
104
|
|
|
104
105
|
if (changed) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: spec-
|
|
3
|
-
description: The BUILD JOB for a pipeline seat — one spec, the role's reading list, the pin taxonomy, the suite protocol, and the fixit variant. Composed AFTER spec-seat, which carries the seat contract (blackboard, question lane, verdicts, FINAL TLDR, close-out). Use when a prompt says you are a build or fixit session, or run spec-
|
|
2
|
+
name: spec-build
|
|
3
|
+
description: The BUILD JOB for a pipeline seat — one spec, the role's reading list, the pin taxonomy, the suite protocol, and the fixit variant. Composed AFTER spec-seat, which carries the seat contract (blackboard, question lane, verdicts, FINAL TLDR, close-out). Use when a prompt says you are a build or fixit session, or run spec-build {role} {spec-path}. Roles: backend | frontend | mixed.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# spec-
|
|
6
|
+
# spec-build
|
|
7
7
|
|
|
8
8
|
**Your seat contract is `spec-seat` — invoke it first if you have not.** It carries the blackboard, the
|
|
9
9
|
question lane, the parked-call discriminator, the verdict vocabulary, the BELIEFS sha, git shapes, the
|
|
@@ -14,7 +14,7 @@ You build exactly one spec. The coordinator judges and rules; you recon, plan, b
|
|
|
14
14
|
Hand-cranked, a human relays messages; hosted inside a pipeline, your coordinator drives you itself and
|
|
15
15
|
the relay is machine-carried — **your contract is identical either way.**
|
|
16
16
|
|
|
17
|
-
Invoked as `spec-
|
|
17
|
+
Invoked as `spec-build {role} {spec-path}`, or told "you are a build session" with the spec path.
|
|
18
18
|
|
|
19
19
|
## Step 1 — with your role and spec in hand
|
|
20
20
|
|
|
@@ -130,7 +130,7 @@ build over a tree they had not touched, and they got a **passing receipt that pr
|
|
|
130
130
|
false-green genus, embedded in the manual instead of in a test. Name the app you edited and run its
|
|
131
131
|
commands from its root.
|
|
132
132
|
|
|
133
|
-
## Fix-it variant (`spec-
|
|
133
|
+
## Fix-it variant (`spec-build fixit`)
|
|
134
134
|
|
|
135
135
|
No spec — your contract is the prompt + the coordinator's ledger (READ-ONLY). **STEP 0 — INVESTIGATE,
|
|
136
136
|
REPORT, STOP:** (1) reproduce/characterize cheaply, (2) find the cheapest reliable RECEIPT for the fixed
|
|
@@ -152,6 +152,6 @@ what a truthful partial looks like per named item, **ask for it** before you sta
|
|
|
152
152
|
- **The spec's anchors are hypotheses; HEAD is the truth.** The biggest catches — stale trees, false
|
|
153
153
|
greens, dropped rulings — are all belief-vs-ledger mismatches. Your beliefs block exists so the
|
|
154
154
|
coordinator can catch them cheaply.
|
|
155
|
-
- Counterpart skills: `spec-seat` (your contract) · `spec-
|
|
155
|
+
- Counterpart skills: `spec-seat` (your contract) · `spec-coordinator` (the seat that arms you) ·
|
|
156
156
|
`spec-pipeline` (how your spec came to exist). The hosted version of you runs THIS same pair — the
|
|
157
157
|
control plane's seed names both skills, in this order.
|