@naswerks/engineering-loop 0.6.0 → 0.7.1

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.6.0",
13
+ "version": "0.7.1",
14
14
  "source": "./"
15
15
  }
16
16
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "naswerks",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
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,65 @@ version is the one in `package.json` and `.claude-plugin/plugin.json`, which mus
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.7.1] - 2026-10-04
9
+
10
+ ### Changed
11
+
12
+ - `spec-coordinator` learns the control plane's verbs of 2026-10-04: `seat_spawn` defaults its host to the one
13
+ carrying the coordinator's own session and takes `model` and `effort` overrides; `accept_plan` and `accept`
14
+ take `answeringActId`, so one act rules and closes a question — and an act that names no question closes
15
+ none; the coordinator reads for itself what the owner used to check (`seat_timeline`, `needs_you`, argless
16
+ `seat_usage`, `seat_parks` with a charge's `seatId` or `all: true`, the roster's host and `re-raises`); it
17
+ commits its tracker itself, after each reap and never in a seat's commit call; and, hosted, `finish` is its
18
+ last call — the pipeline moves to `reviewing` and the PR's merge closes it.
19
+
20
+ ### Upgrading from 0.7.0
21
+
22
+ Take the release (`claude plugin marketplace update engineering-loop`, then
23
+ `claude plugin update naswerks@engineering-loop --scope <scope>`, and a new session). A patch: no loop text
24
+ changed, nothing is restamped, and a hand-cranked repository's coordinator acts as before — every change but
25
+ the tracker's commit discipline is the hosted plane's. A control plane older than 2026-10-04 refuses the new
26
+ arguments and verbs by name.
27
+
28
+ ## [0.7.0] - 2026-10-01
29
+
30
+ ### Changed
31
+
32
+ - **The hosted plane runs the hand-cranked ceremony: one file, two voices, a poke each way.** A seat writes
33
+ its `plan.md` and `tldr.md` with its own tools and posts each by `path` the first time — the control plane
34
+ reads the file from the seat's runner (periscope 1.4.0's `workspace_read`) — then with nothing. The
35
+ coordinator appends its ruling to the seat's `tldr.md` under its coordinator-voice heading and calls
36
+ `accept_plan`, `accept` or `callout` with a one-line summary; the control plane mirrors the file before it
37
+ records the act, and the seat is pointed at the section added. The act still decides. A runner older than
38
+ periscope 1.4.0 keeps the old form: the seat posts `content`, and the verb carries the coordinator's words.
39
+ `spec-seat`, `spec-coordinator` and `engineering-loop.md` § The blackboard say so.
40
+ - `spec-coordinator`: `seat_acts` is the coordinator's census (its mint carries `acts:read`, and
41
+ `spec-retro` says so too); `read_blackboard` returns a window of a file or only the sections new since a
42
+ revision; `accept` carries the commit call, and an acceptance written into the seat's file binds to that
43
+ revision.
44
+ - `spec-seat`: a search is data only as a quoted literal given to a read-only search tool, with no `$`, no
45
+ backtick, no interpreter and no `xargs` in the pipeline. On a periscope 1.4.0 runner the native reads
46
+ reach outside the workspace, credential material excepted; on an older one a file outside it is read with
47
+ a shell command.
48
+
49
+ ### Added
50
+
51
+ - **`## [seat·…] ABOUT TO PARK`, the seat's third anchor:** the next call waits on a human, and here it is —
52
+ the verbatim command and what it waits on, posted before the call runs. Non-staling, like `HOLDING`.
53
+ - Both skills: a control-plane verb that fails with a transport error (`ECONNRESET`) is called once more,
54
+ then disclosed; a refusal has a name and a dropped connection does not.
55
+
56
+ ### Upgrading from 0.6
57
+
58
+ 1. Take the release: `claude plugin marketplace update engineering-loop`, then
59
+ `claude plugin update naswerks@engineering-loop --scope <scope>`, and a new session.
60
+ 2. Re-copy the loop text of `references/engineering-loop.md` into `docs/_meta/engineering-loop.md` — § The
61
+ blackboard changed — keeping your repository-owned sections. Then `init refresh` restamps both copies; it
62
+ never touches loop text.
63
+ 3. A control plane that reads the seat anchors treats `## [seat·…] ABOUT TO PARK` as it treats
64
+ `## [seat·…] HOLDING` — a post-acceptance append that does not stale the acceptance. Until it does, the
65
+ append stales one.
66
+
8
67
  ## [0.6.0] - 2026-09-29
9
68
 
10
69
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naswerks/engineering-loop",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
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.6.0 -->
1
+ <!-- naswerks-loop: version=0.7.1 -->
2
2
  # Docs Workflow
3
3
 
4
4
  > How knowledge flows through `docs/`: the loop, the folder rules, the templates, and the `docs-*` skills.
@@ -1,4 +1,4 @@
1
- <!-- naswerks-loop: version=0.6.0 -->
1
+ <!-- naswerks-loop: version=0.7.1 -->
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-*`
@@ -253,26 +253,31 @@ 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 coordinator-voice heading into its
268
- own 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 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):
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):
276
281
 
277
282
  ```
278
283
  ## COORDINATOR RULING (plan rev N — {accept | accept-with-riders | blocking-precondition})
@@ -286,19 +291,21 @@ Go build. Fold the riders in — no re-approval round-trip.
286
291
 
287
292
  Callouts and nudges append as `## COORDINATOR NOTE` (actor-suffixed when known, e.g. `(operator)`).
288
293
 
289
- **The seat's own two anchors:**
294
+ **The seat's own three anchors:**
290
295
 
291
296
  ```
292
297
  ## [seat·{slug} · run {id}] CLOSE-OUT RECEIPT <- after the push
293
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"
294
300
  ```
295
301
 
296
- **A question heading, a HOLDING and a close-out receipt are ALL non-staling** — the gates bind an
297
- acceptance to the accepted CONTENT, not to the revision number, so appending your own *state* leaves it
298
- 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
299
305
  from a seat that has done nothing, and a coordinator reading a wake led by its own acceptance concludes
300
- there is nothing to rule — the HOLDING anchor is what prevents that. The token is the literal `seat·`
301
- 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.
302
309
 
303
310
  ### Questions ride the same blackboard
304
311
 
@@ -73,36 +73,56 @@ the tracker exists and cut Prompt #1. **Step 0 still applies.**
73
73
  ## Your hands
74
74
 
75
75
  **`read_blackboard` first, always** — argless it is your DAY-ONE SWEEP and your roster: every seat you
76
- decide for, its state, and its newest tldr rev. With a seat id it returns that seat's artifacts.
77
- Hand-cranked, your tools are normal file tools on the blackboard files plus the human relay.
76
+ decide for, its state, and its newest tldr rev. With a seat id it returns that seat's tldr (or
77
+ `kind: plan`): whole, a window of it (`offset`), or only the sections new since a revision (`sinceRev`) —
78
+ read what changed, not the whole conversation again. In a shared workspace the seat's `tldr.md` is also a
79
+ file you read with your own tools. Hand-cranked, your tools are normal file tools on the blackboard files
80
+ plus the human relay.
78
81
 
79
82
  <!-- control-plane-specific -->
80
83
  **The five that reach a seat, and what each one actually is:**
81
84
 
82
85
  | Verb | What it does | The part that bites |
83
86
  |---|---|---|
84
- | `seat_spawn` | Opens a DECLARED sibling's session | **It can never CREATE a seat** — the topology is the create door's, decided by a human — and you may only drive seats you `supervises`/`reviews` |
85
- | `accept_plan` | The verdict act: `accept` · `accept-with-riders` · `blocking-precondition`, **and NOTHING ELSE carries a go** — and an accepting verdict DELIVERS ITSELF: the seat's live session receives a one-line pointer to the ruling (the body is a `ruling` artifact on your act, on its board) | Pass the workspace HEAD you observed (a backticked sha is fine); an accepting verdict whose seat planned against a different tree is REFUSED, naming both shas. READ THE REPLY'S `delivery:` LINE — `NOT DELIVERED` means the seat is idle and only a `callout` reaches it |
86
- | `accept` | The rev-bound FINAL receipt | It binds to the seat's CURRENT tldr revision, so if the seat posts again your acceptance no longer covers what it publishes. **Re-accept — it is idempotent and cheap.** This receipt is what the push bar reads; prose is not a receipt |
87
+ | `seat_spawn` | Opens a DECLARED sibling's session | **It can never CREATE a seat** — the topology is the create door's, decided by a human — and you may only drive seats you `supervises`/`reviews`. With no `hostId` it opens on the host carrying your own session; `model` and `effort` override the seat's own for this session, judged against what the runner offers |
88
+ | `accept_plan` | The verdict act: `accept` · `accept-with-riders` · `blocking-precondition`, **and NOTHING ELSE carries a go** — and it DELIVERS ITSELF: the seat's live session receives a one-line pointer to the ruling, for an accepting verdict and for any verdict that filed a ruling in the seat's file | Write the ruling into the seat's `tldr.md` FIRST (`## COORDINATOR RULING (plan rev N — {verdict})`), then call with a one-line `ruling`: the file is mirrored before your act and the pointer names your section. Pass the workspace HEAD you observed (a backticked sha is fine); an accepting verdict whose seat planned against a different tree is REFUSED, naming both shas. READ THE REPLY'S `delivery:` and `file:` LINES — `NOT DELIVERED` means the seat is idle and only a `callout` reaches it. When the verdict also answers the seat's question, pass `answeringActId` — one act rules and closes it |
89
+ | `accept` | The rev-bound FINAL receipt — and it TELLS the seat, so it carries your commit call | Put the next instruction (commit, push, close-out) in `ruling`, or write it under `## COORDINATOR ACCEPT` in the seat's `tldr.md` first — the receipt then binds to that mirrored revision. It binds to the seat's CURRENT tldr revision, so if the seat posts NEW CONTENT again your acceptance no longer covers what it publishes. **Re-accept — it is idempotent and cheap.** This receipt is what the push bar reads; prose is not a receipt. `answeringActId` closes the question it answers, as on `accept_plan` |
87
90
  | `complete` | The reap, and it is HONEST | It re-reads the seat and reports ok only after the terminal state is OBSERVED |
88
- | `callout` | **THE ONLY WAY YOU REACH A CHARGE** (besides the pointer an accepting `accept_plan` sends) | A charge that posted and ended its turn cannot resume any other way — without this verb it waits for a human. Your ruling must carry a DETERMINATE NEXT ACTION it can execute without inference. KEEP IT TO A LINE: over 600 chars the body is stored as a `ruling` artifact on the board and the seat receives a POINTER — the seat reads the body once, from the board, never twice |
91
+ | `callout` | **THE ONLY WAY YOU REACH A CHARGE** (besides the pointers `accept_plan` and `accept` send) | A charge that posted and ended its turn cannot resume any other way — without this verb it waits for a human. Your ruling must carry a DETERMINATE NEXT ACTION it can execute without inference. **Write it into the seat's `tldr.md` first and pass a one-line `ruling`:** the file is mirrored before your act and the seat is pointed at your section. Without a file, KEEP IT TO A LINE: over 600 chars the body is stored as a `ruling` artifact on the board and the seat receives a POINTER — the seat reads the body once, never twice |
89
92
 
90
- **A QUESTION IS CLOSED BY AN ACT, NOT BY A FILE.** Answer a charge's question with a `callout` carrying
91
- that post's act id as `answeringActId` — **that act IS the answer**, and it is the only thing that stops
92
- the re-raise. A `callout` without one steers and closes nothing, and **nothing you write into a file
93
+ **A QUESTION IS CLOSED BY AN ACT, NOT BY A FILE.** Answer a charge's question with a `callout` — or an
94
+ `accept_plan` / `accept` that rules at the same time — carrying that post's act id as `answeringActId`:
95
+ **that act IS the answer**, and it is the only thing that stops the re-raise. An act without one steers or
96
+ rules and closes nothing — a forgotten id costs one re-raise — and **nothing you write into a file
93
97
  closes anything**: the latch reads the ledger, so a ruling you never sent is a question still open no
94
98
  matter what any blackboard says.
95
99
 
96
- **`seat_acts` IS NOT YOURS, and that is deliberate.** Your mint is `seat:read` · `pipeline:read` ·
97
- `drive:write` — the coordinator kind was the last one without `acts:read`, and granting it would have
98
- made that scope universal. **So do not plan a day-one argless act sweep you cannot run**; your sweep is
99
- `read_blackboard`, and any count you publish names the lane that produced it. The retro seat DOES carry
100
- `acts:read` — the sweep is its instrument, not yours.
100
+ **THE COORDINATOR'S APPEND, HOSTED.** In a shared workspace a seat that posts its `tldr.md` by `path` shares
101
+ the file with you: append your ruling with your own file tools under a coordinator-voice heading, then call
102
+ the verb. The control plane reads the file, mirrors it as the next revision in YOUR voice before it records
103
+ your act, and points the seat at the section you added; the reply's `file:` line says what was mirrored. A
104
+ seat that posts bytes (a runner older than periscope 1.4.0) or works in its own workspace keeps the old form:
105
+ the verb carries your words. **The act still decides** — a section closes nothing on its own.
106
+
107
+ **`seat_acts` IS YOUR CENSUS.** Your mint carries `acts:read`: take counts, human touches and receipts from
108
+ the ledger, never from a seat's report about itself, and name the lane any count you publish came from.
109
+ Beside it, read for yourself what the owner used to check for you: `seat_timeline` (a seat's two lanes —
110
+ *the reap ended the process* is the runner's `ended` row, cause `stop_requested`, after your reap act),
111
+ `needs_you` (the owner's Needs-you tab, Active and History), argless `seat_usage` (every seat's spend and
112
+ the pipeline's total), `seat_parks` with a charge's `seatId` or `all: true` (its parks, and the rows that
113
+ ADMITTED a push), and the argless roster's host and `re-raises` per seat.
101
114
 
102
115
  **WAKES:** you are woken when a charge posts its plan or its tldr, and a one-line pointer rides the wake —
103
- read the board and answer on first receipt rather than spending a round-trip.
116
+ the revision and the sections it added. Read what is new (`read_blackboard` with `sinceRev`, or the file) and
117
+ answer on first receipt rather than spending a round-trip.
104
118
 
105
- **THE TRACKER IS YOURS ALONE.** Advance it at every seat boundary; never reconstruct it at the close.
119
+ **A verb that fails with a transport error** (`ECONNRESET`, a socket closed under the call) is the transport,
120
+ not a refusal: call it once more, and journal it if it fails again. A refusal has a name; a dropped
121
+ connection does not.
122
+
123
+ **THE TRACKER IS YOURS ALONE.** Advance it at every seat boundary and **commit it yourself — after each
124
+ reap, before the next spawn — never in a seat's commit call**, so it is never frozen between an acceptance
125
+ and a seat's close-out. Never reconstruct it at the close.
106
126
 
107
127
  ## The loop (per seat)
108
128
 
@@ -329,6 +349,12 @@ read the board and answer on first receipt rather than spending a round-trip.
329
349
  is not reversible by the seat that does it, and a check the human runs afterwards confirms the damage
330
350
  instead of preventing it. **A live-check row is confirmation; this is prevention.** Never hold the
331
351
  run waiting for a human to verify this first — you have the list, so you are the gate.
352
+ 8. **Hosted, `finish` is your last call** — once every other seat, P included, is reaped. Pass the PR's
353
+ URL: it records the pipeline done with evidence it reads from the ledger, moves it to `reviewing`,
354
+ closes your own session and ends your process, and the PR's merge closes the pipeline. **Nothing you
355
+ send after it is admitted** — commit your tracker and report first. It refuses, recording nothing,
356
+ while any seat is still live or when none shipped. Hand-cranked there is no `finish`; the human closes
357
+ the effort.
332
358
 
333
359
  **The human hand-tests.** Live verification is the human's role, not a seat's claim — when a slice
334
360
  ships something clickable, tell the human concretely what to drive (the 2-3 highest-value clicks), and
@@ -342,10 +368,9 @@ so read them as run-time obligations, not close-out ones:
342
368
 
343
369
  - **Your zero-touch / ceremony claims must cite an ARGLESS sweep**, never a verb list you picked
344
370
  yourself. **Check on day one that the sweep is runnable at your run's scale — and that it is runnable
345
- BY SOMEONE.** Hosted, your ticket carries no `acts:read`, so the argless act sweep is not yours; your
346
- day-one check is whether the retro seat, a witness or an operator can produce it. If none can, that is
347
- a day-one escalation, not a close-out discovery — a verb-filtered audit can only confirm what you
348
- suspected.
371
+ BY SOMEONE.** Hosted, `seat_acts` is that sweep and your ticket carries it; hand-cranked, check that the
372
+ retro seat, a witness or an operator can produce it. If none can, that is a day-one escalation, not a
373
+ close-out discovery — a verb-filtered audit can only confirm what you suspected.
349
374
  - **A rev-bound acceptance goes stale on a post-acceptance append of NEW CONTENT — from either actor.**
350
375
  The receipt covers the tldr rev + HEAD it was written against; a later content post invalidates it and
351
376
  the cheap repair is an **argless re-accept sent WITH the retry order in the same act**. A re-accept with
@@ -541,8 +566,9 @@ still open, however good the prose; the seat is still holding and the re-raise i
541
566
  **The blackboard append.** Hand-cranked, the seat's `plan.md` and `tldr.md` are live FILES at
542
567
  `docs/research/{topic}/sessions/{spec-slug}/` — read the TLDR there with normal file tools instead of
543
568
  asking for a paste (`read-full-plan` = read `plan.md`, a pull, never force-fed). Deliver every ruling by
544
- **APPENDING it verbatim to the seat's `tldr.md`**, right under the questions it answers. Hosted, your
545
- verbs ARE the appends and the machinery writes them for you.
569
+ **APPENDING it verbatim to the seat's `tldr.md`**, right under the questions it answers. Hosted, the same
570
+ append, then the verb: the control plane mirrors the file before your act and points the seat at the
571
+ section (§ Your hands, the coordinator's append).
546
572
 
547
573
  **Anchor grammar (canonical — the witness greps these):** rulings land under
548
574
  `## COORDINATOR RULING (plan — {verdict})` hand-cranked, `## COORDINATOR RULING (plan rev N — {verdict})`
@@ -45,12 +45,10 @@ human and exception rows included. **Start every audit with it: "what happened t
45
45
  not ask about."** A verb-filtered read can only confirm suspicions, never surface surprises — a run can
46
46
  certify itself "zero-touch" while `approved_by_human` rows sit one unqueried verb away.
47
47
 
48
- **Who holds the instrument, on the hosted plane:** a `retro` seat's ticket carries `acts:read`, so
49
- `seat_acts` argless IS your sweep. A `coordinator` ticket deliberately does **not** carry it — its mint is
50
- `seat:read` · `pipeline:read` · `drive:write` — so a coordinator writing this retro from its own seat
51
- reads `read_blackboard` (argless = the roster) and takes the act sweep from the retro seat, the
52
- effort's witness, or a credentialed operator. **Say which instrument produced every count in this
53
- document** — a number whose lane you cannot name is testimony, not an audit.
48
+ **Who holds the instrument, on the hosted plane:** a `retro` seat's ticket and a `coordinator` ticket both
49
+ carry `acts:read`, so `seat_acts` argless IS your sweep whichever of the two writes this retro. **Say which
50
+ instrument produced every count in this document** — a number whose lane you cannot name is testimony, not
51
+ an audit.
54
52
 
55
53
  **If the argless sweep is not runnable at your run's scale, that is a day-one escalation, not a
56
54
  close-out discovery.** A few thousand rows can exceed the lane's page budget; you cannot retroactively
@@ -98,16 +98,26 @@ that is your scope — do not widen it on the theory that more reading is safer.
98
98
  `plan.md` (the full plan document) and `tldr.md` (the chat: your PLAN TLDR, any mid-build updates, the
99
99
  FINAL TLDR — **append, never overwrite**) are FILES, not just chat text.
100
100
 
101
+ **ONE CEREMONY IN BOTH MODES: you write your file, then you poke; your coordinator appends its ruling to the
102
+ SAME file, then pokes you. Nobody pastes a document into a message.**
103
+
101
104
  - **Hand-cranked:** create them under `docs/research/{topic}/sessions/{slug}/` at plan time and end your
102
105
  turn announcing the path. The coordinator reads `tldr.md` with normal file tools and APPENDS its ruling
103
106
  under `## COORDINATOR RULING`, so read the answer in your own file and proceed. The full plan is PULL — it is
104
107
  read only on a read-full-plan verdict; never paste it into chat. (If your harness has a plan-approval
105
108
  mode that forbids writes: give the chat summary, get the human's approve, THEN post and stop.)
106
- - **Hosted (a seat inside a pipeline):** the same two files are yours to edit with normal file tools, then
107
- call **`post_plan` / `post_tldr` with the file's whole text as `content`** — the control plane runs on
108
- another machine and cannot read your file. It records a revision and wakes your coordinator with ONE
109
- LINE naming the act and the rev; the coordinator reads your file from the board; nothing you wrote is
110
- pasted into its context (the pointer protocol).
109
+ - **Hosted (a seat inside a pipeline):** the same two files are yours to edit with normal file tools. **Write,
110
+ then poke:** the first time, call `post_tldr` with `path` — the file's path inside your workspace, relative
111
+ to its root — and the control plane reads the file from your runner; after that, call `post_tldr` with
112
+ NOTHING and it reads the same path again (`post_plan` the same way). It records a revision and wakes your
113
+ coordinator with ONE LINE naming the rev and the sections you added. **Your coordinator's ruling arrives
114
+ IN YOUR FILE** — it appends under its coordinator-voice heading, and you are poked with one line naming
115
+ the section (*"Your tldr.md rev 5 adds `## COORDINATOR RULING (plan rev 1 — accept)`"*). Read it with
116
+ your own file tools; it is never pasted to you.
117
+ **If the post answers `host-lacks-workspace-read`** (a runner older than periscope 1.4.0) **or asks for
118
+ `content`** (a control plane that predates reading files), it cannot read your workspace: pass the whole
119
+ file as `content` instead, and read rulings from your board (`seat_content(kind: "ruling")`) as well as
120
+ your file.
111
121
  **THE FILE IS THE RECORD AND THE ROW MIRRORS IT** — one artifact row per rev carrying the whole file
112
122
  verbatim, so the two voices stay interleaved because nothing splits them. The control plane refuses
113
123
  `blackboard-rev-regressed` · `blackboard-shrank` · `blackboard-rev-content-mismatch` at the door: **an
@@ -194,9 +204,10 @@ question tool — the relay cannot copy it.
194
204
  **An unanswered question re-raises to your coordinator on a cooldown** until a ruling lands. Asking costs
195
205
  the pipeline a round-trip and nothing else.
196
206
 
197
- **WHAT ACTUALLY CLOSES A QUESTION.** On the hosted plane **nothing you write into a FILE closes anything.**
198
- The latch reads the ACTS LEDGER: a ruling closes your question only when it is delivered as a `callout`
199
- carrying your post's act id as `answeringActId`. A `callout` without one steers you and closes nothing.
207
+ **WHAT ACTUALLY CLOSES A QUESTION.** On the hosted plane **nothing written into a FILE closes anything** —
208
+ not yours, and not your coordinator's section in it. The latch reads the ACTS LEDGER: a ruling closes your
209
+ question only when the coordinator's act carries your post's act id as `answeringActId`. The words arrive in
210
+ your file; the act is what decides. A `callout` without one steers you and closes nothing.
200
211
  **The reason is a security property, not tidiness:** `tldr.md` is written BY YOU, so a file-side close
201
212
  predicate is a string the audited party can type — a seat could write `## COORDINATOR RULING` into its own file
202
213
  and switch off the very re-raise that exists to stop it waiting forever. The question is therefore split
@@ -250,6 +261,9 @@ Ask what the park is protecting:
250
261
  the platform's turn, not your failure; routing around it hides the defect the park is surfacing.
251
262
  - **A park on a boundary command** (push to the base branch, force-push, branch delete — or a command
252
263
  whose *text* merely tripped the scanner) — **never re-request; reshape the command.**
264
+ **A search is data only in one shape:** a quoted literal pattern given to a read-only search tool (`rg`,
265
+ `grep`, `git grep`, `Select-String`, `findstr`), with no `$`, no backtick, no interpreter and no `xargs`
266
+ in the pipeline. Any other command carrying boundary words parks — search for the symbol, not the verb.
253
267
  **THE SCANNER MATCHES TEXT, NOT PARSED VERBS, AND THIS IS THE MOST-PAID FENCE IN THE CEREMONY.** It
254
268
  fires on read-only commands a careful seat runs to PROVE its work: an `echo` label reading
255
269
  `=== remote tip ===` on a `git status`/`git log` block, or `git branch -r --contains <sha>` — a branch
@@ -266,6 +280,16 @@ Ask what the park is protecting:
266
280
  The test: is the park **blocking your work** (re-request + disclose), **guarding the boundary** (reshape),
267
281
  or **a wall** (reshape or escalate — never a click)?
268
282
 
283
+ **BEFORE A CALL YOU KNOW WILL PARK, SAY SO.** When your work needs a boundary command — a lane that tests the
284
+ park, a push your coordinator called for from a state the push bar cannot see — append
285
+
286
+ ```
287
+ ## [seat·{slug} · run {id}] ABOUT TO PARK
288
+ ```
289
+
290
+ with the verbatim command and what it waits on, post, then run it. A seat held by the gate without saying so
291
+ looks idle to everyone watching. Like `HOLDING`, it is a state report, and it does not stale your acceptance.
292
+
269
293
  **EVERY park is DISCLOSED in your next TLDR — including one you resolved yourself in seconds:** the
270
294
  verbatim command AND the verbatim park text. A self-resolved park looks like nothing happened and leaves
271
295
  the coordinator no signal, so the gate's false-park rate is only ever knowable from the seat that paid it.
@@ -379,7 +403,9 @@ doorbell with no follow-through**; if you receive one bare, retry anyway and say
379
403
  parks where the same three commands, sent one at a time, flow unattended. Keep the word "push" out of
380
404
  echo text — the deny layer matches text, not parsed verbs.
381
405
  - Prefer native read/glob/grep tools for reads; a shell read flows with its row like any other
382
- non-boundary call.
406
+ non-boundary call. On a periscope 1.4.0 runner they reach anything outside your workspace except
407
+ credential material; on an older runner Read is jailed to your workspace, so read a file outside it —
408
+ the runner's installed package, say — with a shell command.
383
409
  - **Codegen only through the command `patterns/codegen.md` § Running codegen names** (when the repo has
384
410
  one) — never hand-write a generated folder.
385
411
  - **Never improvise a qualifying path in.** If a push parks and the remedy you can think of is touching a
@@ -449,9 +475,10 @@ ANCHOR, not a description of you.** A descriptive tail after the marker IS
449
475
  blessed (`… CLOSE-OUT RECEIPT — addendum: the six staged, tree clean`) — an end-anchored pattern on the
450
476
  one heading seats are told to write would be a trap that fires on ordinary prose.
451
477
 
452
- **Any post-accept append that is NOT one of these four — close-out receipt, HOLDING, a question heading, or
453
- your coordinator's own voice — needs the argless re-accept.** The bar is content, not revision: it asks *is
454
- what you are publishing still what I approved?*, and a state report is not a change of work.
478
+ **Any post-accept append that is NOT one of these five — close-out receipt, HOLDING, ABOUT TO PARK, a
479
+ question heading, or your coordinator's own voice — needs the argless re-accept.** The bar is content, not
480
+ revision: it asks *is what you are publishing still what I approved?*, and a state report is not a change of
481
+ work.
455
482
 
456
483
  **FILE YOUR SESSION ARTIFACTS:** copy your final `plan.md` and `tldr.md` into
457
484
  `docs/research/{topic}/sessions/{slug}/`. **Two files — that is the whole filing step.** Questions ride the
@@ -524,5 +551,8 @@ plane. The publish receipt is written at your turn's STOP and is structurally un
524
551
  with a clean rerun builds trust, an undisclosed one destroys it.
525
552
  - **A rule recalled from context needs re-reading at the source before it is used to refuse work.** A
526
553
  rule already corrected on disk is the commonest thing a seat blocks itself on.
554
+ - **A control-plane verb that fails with a transport error** (`ECONNRESET`, a socket closed under the call)
555
+ is the transport, not a refusal: call it once more. If it fails again, say so in your next TLDR and carry
556
+ on with work that does not need it. A refusal has a name; a dropped connection does not.
527
557
  - Your job skill is next: `spec-build` (build/fixit) · `spec-review` (the outside look) ·
528
558
  `docs-process` (compile the living docs). Counterpart: `spec-coordinator` (the seat that arms you).