@naswerks/engineering-loop 0.5.1 → 0.7.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.
@@ -10,7 +10,7 @@
10
10
  {
11
11
  "name": "naswerks",
12
12
  "description": "The engineering loop: docs-* knowledge loop, spec-* execution loop, and init for a fresh repository.",
13
- "version": "0.5.1",
13
+ "version": "0.7.0",
14
14
  "source": "./"
15
15
  }
16
16
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naswerks",
3
- "version": "0.5.1",
3
+ "version": "0.7.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,93 @@ version is the one in `package.json` and `.claude-plugin/plugin.json`, which mus
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.7.0] - 2026-10-01
9
+
10
+ ### Changed
11
+
12
+ - **The hosted plane runs the hand-cranked ceremony: one file, two voices, a poke each way.** A seat writes
13
+ its `plan.md` and `tldr.md` with its own tools and posts each by `path` the first time — the control plane
14
+ reads the file from the seat's runner (periscope 1.4.0's `workspace_read`) — then with nothing. The
15
+ coordinator appends its ruling to the seat's `tldr.md` under its coordinator-voice heading and calls
16
+ `accept_plan`, `accept` or `callout` with a one-line summary; the control plane mirrors the file before it
17
+ records the act, and the seat is pointed at the section added. The act still decides. A runner older than
18
+ periscope 1.4.0 keeps the old form: the seat posts `content`, and the verb carries the coordinator's words.
19
+ `spec-seat`, `spec-coordinator` and `engineering-loop.md` § The blackboard say so.
20
+ - `spec-coordinator`: `seat_acts` is the coordinator's census (its mint carries `acts:read`, and
21
+ `spec-retro` says so too); `read_blackboard` returns a window of a file or only the sections new since a
22
+ revision; `accept` carries the commit call, and an acceptance written into the seat's file binds to that
23
+ revision.
24
+ - `spec-seat`: a search is data only as a quoted literal given to a read-only search tool, with no `$`, no
25
+ backtick, no interpreter and no `xargs` in the pipeline. On a periscope 1.4.0 runner the native reads
26
+ reach outside the workspace, credential material excepted; on an older one a file outside it is read with
27
+ a shell command.
28
+
29
+ ### Added
30
+
31
+ - **`## [seat·…] ABOUT TO PARK`, the seat's third anchor:** the next call waits on a human, and here it is —
32
+ the verbatim command and what it waits on, posted before the call runs. Non-staling, like `HOLDING`.
33
+ - Both skills: a control-plane verb that fails with a transport error (`ECONNRESET`) is called once more,
34
+ then disclosed; a refusal has a name and a dropped connection does not.
35
+
36
+ ### Upgrading from 0.6
37
+
38
+ 1. Take the release: `claude plugin marketplace update engineering-loop`, then
39
+ `claude plugin update naswerks@engineering-loop --scope <scope>`, and a new session.
40
+ 2. Re-copy the loop text of `references/engineering-loop.md` into `docs/_meta/engineering-loop.md` — § The
41
+ blackboard changed — keeping your repository-owned sections. Then `init refresh` restamps both copies; it
42
+ never touches loop text.
43
+ 3. A control plane that reads the seat anchors treats `## [seat·…] ABOUT TO PARK` as it treats
44
+ `## [seat·…] HOLDING` — a post-acceptance append that does not stale the acceptance. Until it does, the
45
+ append stales one.
46
+
47
+ ## [0.6.0] - 2026-09-29
48
+
49
+ ### Changed
50
+
51
+ - **Two skills are named for the seat kinds that run them.** `spec-parent` is now `spec-coordinator`, and
52
+ `spec-child` is now `spec-build`. A spec seat's skill is `spec-{kind}` for every kind; `fixit` rides
53
+ `spec-build`, and `docs-process` is its own skill. Invoke them as `/naswerks:spec-coordinator` and
54
+ `/naswerks:spec-build`. A control plane that seeds skills by name seeds the new names, with this version
55
+ as its pack floor.
56
+ - **The coordinator's voice is spelled `COORDINATOR`.** The anchors are `## COORDINATOR RULING`,
57
+ `## COORDINATOR NOTE` and `## COORDINATOR ACCEPT` (beside `## OPERATOR RULING`), the ruling templates open
58
+ `COORDINATOR RESPONSE`, and a seat's own anchors are `## [seat·…] CLOSE-OUT RECEIPT` and
59
+ `## [seat·…] HOLDING`. A board written before this version spells them `## PARENT …` and `[child·…]`:
60
+ every grep the skills run reads both spellings, a parser that reads the anchors should too, and no seat
61
+ rewrites a heading already on its board.
62
+ - The coordinator's close-out audit is filed as `sessions/coordinator-retrospective.md`; a reader takes
63
+ `sessions/parent-retrospective.md` in an effort begun before this version.
64
+ - `spec-coordinator` no longer names a `parallel` spawn argument: a shared worktree holds one live seat at a
65
+ time, and parallel seats need separate worktrees, decided when the pipeline is created.
66
+ - The retired-vocabulary census retires `parent` beside `chain` and `child`.
67
+ - `docs-workflow.md` says the ignition brief is written whole at authoring and verified at ignition. The
68
+ sentence sits in § Templates, which a copy owns: see Upgrading, step 3.
69
+
70
+ ### Added
71
+
72
+ - `pins/names.test.mjs`: every skill name the pack writes is a skill it carries, and every seat kind runs a
73
+ skill named for it. The kinds, their skills and the words shaped like a skill name that name none are one
74
+ list in `skills/init/scripts/owned.mjs`, which the doctor reads too.
75
+ - `init doctor` degrades a brief whose `## The kick` names a skill this pack does not carry, and names the
76
+ coordinator's skill.
77
+
78
+ ### Upgrading from 0.5
79
+
80
+ 1. Take the release (`claude plugin marketplace update engineering-loop`, then
81
+ `claude plugin update naswerks@engineering-loop`) between efforts: a session that invokes `spec-parent`
82
+ or `spec-child` after the update finds no such skill.
83
+ 2. Re-copy the loop text of both references into `docs/_meta/`, keeping your repository-owned sections;
84
+ `init refresh` restamps and never touches loop text.
85
+ 3. Three places are yours, so neither `init refresh` nor a re-copy reaches them. Change each one that still
86
+ carries the old text:
87
+ - § The archive convention (repository-owned): `parent-retrospective.md` is `coordinator-retrospective.md`.
88
+ - § Templates (repository-owned): the ignition brief is "the coordinator seat's standing orders for the
89
+ run — written whole at authoring, verified at ignition" (it read "the parent seat's standing orders
90
+ for the run — two sections completed at ignition").
91
+ - `docs/_meta/doc-index.md`: the build-seat paragraph `init` wrote from its template names `spec-build`
92
+ (it read `spec-child`).
93
+ 4. Run `init doctor`. A brief whose kick still names a retired skill shows a DEGRADE row; change the kick.
94
+
8
95
  ## [0.5.1] - 2026-09-24
9
96
 
10
97
  ### Fixed
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-parent` | spec | the coordinator seat |
72
+ | `spec-coordinator` | spec | the coordinator seat |
73
73
  | `spec-seat` | spec | the contract every armed seat follows |
74
- | `spec-child` | spec | the build job for one 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.5.1",
3
+ "version": "0.7.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.5.1 -->
1
+ <!-- naswerks-loop: version=0.7.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.
@@ -204,9 +204,9 @@ one** (via `spec-pipeline`). The trigger for the layout is simply *"does this ef
204
204
  (TWO files — questions ride the PLAN TLDR, and there are no
205
205
  status snapshots, because the tracker is committed live and
206
206
  its git history carries every revision)
207
- code-review.md the review seat's outside look, if
208
- the pipeline had a review row
209
- parent-retrospective.md the parent's inside look
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
210
210
  These are TESTIMONY: an archive pass repairs links elsewhere and
211
211
  leaves sessions/** byte-untouched. Older archived sessions may
212
212
  still hold questions.md / status-after-*.md; that is historical
@@ -430,8 +430,8 @@ once the code is fixed. Keep entries one line, dated, and pointing at a `file:li
430
430
  ### Spec doc (a numbered pipeline — `spec-pipeline`)
431
431
 
432
432
  When a research idea graduates into a real plan, `spec-pipeline` explodes it into a numbered pipeline in
433
- `docs/research/{topic}/`: `00-ignition-brief.md` (the parent seat's standing orders for the run — two
434
- sections completed at ignition) · `01-{topic}.md` (the umbrella) · `02..NN-{slice}.md` (**one
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
435
435
  self-contained spec per planned session**) · `project-status.md` (the tracker, which carries its own
436
436
  section notes) · `live-checks.md` (the human's verification rows).
437
437
  Each per-session spec uses this template (carries `Builds on`/`Source:` so the chain stays navigable):
@@ -472,13 +472,13 @@ come from this copy, so a repository changes them once, here.
472
472
  |-------|------|-------------|
473
473
  | `docs-backlog` | Have an idea | Capture it as a research doc in `research/` |
474
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 |
475
- | `spec-parent` | 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 |
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 |
476
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 |
477
- | `spec-child` | 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` |
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` |
478
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 |
479
- | `spec-ignite` | Right after `spec-pipeline` when the pipeline runs HOSTED (vs `spec-parent` 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 |
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 |
480
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** |
481
- | `spec-retro` | The parent'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 |
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 |
482
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 |
483
483
  | `docs-write` | End of a session | Finalize the working doc; it stays in `working/` (records its `## Archive` target) |
484
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.5.1 -->
1
+ <!-- naswerks-loop: version=0.7.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-parent` skill; the seat contract in `spec-seat`.
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-parent -> this session becomes the COORDINATOR and cuts
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-parent` takes the seat, build seats build one spec each. **Each spec is sized
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-parent` skill are the requirements; the control plane follows.
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-parent {topic}`).
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-parent` | 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-parent` does NOT compose it: the coordinator writes rulings rather than receiving them |
143
- | `spec-child` | spec | igniting a build or fixit seat — manual (`spec-child {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-parent` 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 |
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-child` · `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 |
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-parent` skill.)
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 -> PARENT RESPONSE — PLAN {ACCEPTED / ACCEPTED-WITH-RIDERS / …} · rulings 1..n
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
- `## PARENT NOTE`s, then the FINAL TLDR. Read top-to-bottom it IS the session's whole
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 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
- parent-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
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
@@ -253,51 +253,59 @@ repo files, staged at close-out; the coordinator appends rulings with its own fi
253
253
  human's relay is a one-line nudge each way).
254
254
 
255
255
  <!-- control-plane-specific -->
256
- **Hosted — there is no folder tree the controller can read.** A seat **carries its bytes** to
257
- `post_plan` / `post_tldr` (verbs under `acts:write`, so a witness ticket structurally cannot post), and
258
- everything identifying is **server-derived** — pipeline and seat from the verified ticket, and **the rev**
259
- (newest + 1). *A rev nobody can misstate cannot regress by accident.* The append-only guard is what
260
- survives the transport: the ladder only climbs, a shorter file is refused, and the same rev with
261
- different bytes is refused — so the mirror **cannot be clobbered or rewound by a caller that lies, even
262
- though it can be fed.**
263
-
264
- **The coordinator's voice is NOT in the seat's file on the hosted plane.** A coordinator answers with
265
- `callout` (its sixth drive verb, machine-written, the only downward path), which persists on its own
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 parent-voice heading into its own
268
- TLDR closes nothing. `callout` **refuses when the charge has no live session** — a coordinator must
256
+ **Hosted — the same files, read by the controller.** A seat edits its two files with its own tools and
257
+ **pokes**: `post_plan` / `post_tldr` with the file's `path` inside its workspace the first time, then with
258
+ nothing — the control plane reads the file from the seat's runner and mirrors it (verbs under `acts:write`,
259
+ so a witness ticket structurally cannot post). A runner older than periscope 1.4.0 cannot be read; there the
260
+ seat carries its bytes as `content`. Everything identifying is **server-derived** — pipeline and seat from
261
+ the verified ticket, and **the rev** (newest + 1). *A rev nobody can misstate cannot regress by accident.*
262
+ The append-only guard is what survives the transport: the ladder only climbs, a revision that drops a section
263
+ is refused, and the same rev with different bytes is refused — so the mirror **cannot be clobbered or rewound
264
+ by a caller that lies, even though it can be fed.**
265
+
266
+ **The coordinator's voice is in the seat's file in both modes.** The coordinator appends its ruling to the
267
+ seat's `tldr.md` under its coordinator-voice heading, then calls its verb (`accept_plan` · `accept` ·
268
+ `callout`) with a one-line summary: the control plane mirrors the file as a revision in the coordinator's
269
+ voice BEFORE it records the act, and the seat is pointed at the section added. **The file says what was
270
+ said; the ledger says what was decided** — a question closes only on the coordinator's act, so a seat typing
271
+ a coordinator-voice heading into its own TLDR closes nothing, and a revision in the coordinator's voice never
272
+ counts as the seat asking. `callout` **refuses when the charge has no live session** — a coordinator must
269
273
  `seat_spawn` before it can rule — and reports **"RULING RECORDED, NOT DELIVERED"** in those words if the
270
- transport drops it.
274
+ transport drops it. Where the coordinator cannot write the seat's file (a runner older than 1.4.0, a seat in
275
+ its own workspace), the verb carries the words, as before.
271
276
 
272
- The parent-voice block (machinery-written hosted; the hand-cranked coordinator writes it — hand-cranked
273
- there is no rev counter, so the heading is `## PARENT RULING (plan — {verdict})`; the stable grep prefix
274
- `## PARENT RULING` is the contract in both modes):
277
+ The coordinator-voice block (the coordinator writes it in both modes; hand-cranked there is no rev counter,
278
+ so the heading is `## COORDINATOR RULING (plan — {verdict})`; the stable grep prefix
279
+ `## COORDINATOR RULING` is the contract in both modes, and an older board's `## PARENT RULING` is read as
280
+ the same heading):
275
281
 
276
282
  ```
277
- ## PARENT RULING (plan rev N — {accept | accept-with-riders | blocking-precondition})
283
+ ## COORDINATOR RULING (plan rev N — {accept | accept-with-riders | blocking-precondition})
278
284
 
279
- PARENT RESPONSE — PLAN TLDR {VERDICT}. {one sentence naming the best thing in the plan}
285
+ COORDINATOR RESPONSE — PLAN TLDR {VERDICT}. {one sentence naming the best thing in the plan}
280
286
  Answers:
281
287
  1. {RULING IN CAPS} — {decision first, then the why}.
282
288
  ...
283
289
  Go build. Fold the riders in — no re-approval round-trip.
284
290
  ```
285
291
 
286
- Callouts and nudges append as `## PARENT NOTE` (actor-suffixed when known, e.g. `(operator)`).
292
+ Callouts and nudges append as `## COORDINATOR NOTE` (actor-suffixed when known, e.g. `(operator)`).
287
293
 
288
- **The seat's own two anchors:**
294
+ **The seat's own three anchors:**
289
295
 
290
296
  ```
291
- ## [child·{slug} · run {id}] CLOSE-OUT RECEIPT <- after the push
292
- ## [child·{slug} · run {id}] HOLDING <- "I have pushed and I am waiting"
297
+ ## [seat·{slug} · run {id}] CLOSE-OUT RECEIPT <- after the push
298
+ ## [seat·{slug} · run {id}] HOLDING <- "I have pushed and I am waiting"
299
+ ## [seat·{slug} · run {id}] ABOUT TO PARK <- "the next call waits on a human, and here it is"
293
300
  ```
294
301
 
295
- **A question heading, a HOLDING and a close-out receipt are ALL non-staling** — the gates bind an
296
- acceptance to the accepted CONTENT, not to the revision number, so appending your own *state* leaves it
297
- intact. Only **new content** stales. A seat that has pushed and is waiting is otherwise indistinguishable
302
+ **A question heading, a HOLDING, an ABOUT TO PARK and a close-out receipt are ALL non-staling** — the gates
303
+ bind an acceptance to the accepted CONTENT, not to the revision number, so appending your own *state* leaves
304
+ it intact. Only **new content** stales. A seat that has pushed and is waiting is otherwise indistinguishable
298
305
  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 `child·`
300
- whatever seat you are — it is a protocol anchor, not a description of you.
306
+ there is nothing to rule — the HOLDING anchor is what prevents that, and ABOUT TO PARK does the same for a
307
+ seat the gate is holding. The token is the literal `seat·` whatever seat you are — it is a protocol anchor,
308
+ not a description of you.
301
309
 
302
310
  ### Questions ride the same blackboard
303
311
 
@@ -321,4 +329,4 @@ the credential: **a receipt the audited party can mint is not a receipt** — wh
321
329
  door takes the human's own token and never a seat's job ticket.
322
330
 
323
331
  ## Lineage
324
- - **Related** — [docs-workflow](docs-workflow.md) · [doc-index](doc-index.md) · the `spec-parent` and `spec-seat` skills (the operational templates and the seat contract)
332
+ - **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 `parent-retrospective.md`. Read both. **Every review finding gets a
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 /
@@ -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-child` (reads the role rows), `spec-pipeline`
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, compareMinor, frontmatterListCount, 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)), '..', '..', '..');
@@ -194,7 +194,9 @@ for (const name of ['CLAUDE.md', 'AGENTS.md']) {
194
194
  }
195
195
 
196
196
  // ── 7. every ignition brief, checked the way the control plane checks it ────────────────────────
197
- const KINDS = new Set(['coordinator', 'build', 'fixit', 'review', 'retro', 'witness', 'docs-process']);
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')) : []);
198
200
  const researchDir = join(repo, 'docs', 'research');
199
201
  if (existsSync(researchDir)) {
200
202
  for (const topic of readdirSync(researchDir)) {
@@ -205,6 +207,7 @@ if (existsSync(researchDir)) {
205
207
  const kick = sectionOf(text, '## The kick');
206
208
  if (kick === null) problems.push('the brief has no `## The kick` section, so the task text could not be read');
207
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));
208
211
  const seq = sectionOf(text, '## The sequence');
209
212
  if (seq === null) problems.push('the brief has no `## The sequence` section, so no seats could be read');
210
213
  else {
@@ -213,13 +216,14 @@ if (existsSync(researchDir)) {
213
216
  for (const line of tableRows.slice(1)) {
214
217
  const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => c.trim().replace(/^`|`$/g, '').trim());
215
218
  if (cells.every((c) => /^[-: ]*$/.test(c))) continue;
216
- if (!cells.some((c) => KINDS.has(c))) { problems.push(`a sequence row names no seat kind and composes nothing: \`${line.trim()}\``); continue; }
219
+ if (!cells.some((c) => KINDS.includes(c))) { problems.push(`a sequence row names no seat kind and composes nothing: \`${line.trim()}\``); continue; }
217
220
  const spec = cells.map((c) => /\b(\d{2}-[A-Za-z0-9._-]*?\.md)\b/.exec(c)).find(Boolean);
218
221
  if (spec && !existsSync(join(researchDir, topic, spec[1]))) problems.push(`the sequence names \`${spec[1]}\` and it is missing from disk`);
219
222
  }
220
223
  }
221
- if (problems.length) for (const pr of problems) row('FAIL', `docs/research/${topic}/00-ignition-brief.md`, pr);
222
- else row('ok', `docs/research/${topic}/00-ignition-brief.md`, 'the kick and the sequence read clean');
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');
223
227
  }
224
228
  }
225
229
 
@@ -21,6 +21,32 @@ 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); }
@@ -1,9 +1,9 @@
1
1
  ---
2
- name: spec-child
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-child {role} {spec-path}. Roles: backend | frontend | mixed.
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-child
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-child {role} {spec-path}`, or told "you are a build session" with the spec path.
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-child fixit`)
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-parent` (the seat that arms you) ·
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.