@skitterbyte/skitterspec 21.0.0 → 22.0.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/MIGRATION.md +218 -0
- package/README.md +113 -6
- package/assets/claude-md-section.md +29 -18
- package/assets/commands/spec-remote-review.md +22 -0
- package/assets/core/env.config.json.example +4 -2
- package/assets/core/env.config.md +100 -23
- package/assets/review/page.html +1044 -101
- package/assets/rules/spec-planning.md +35 -3
- package/assets/rules/spec-reports.md +131 -20
- package/assets/skills/spec/SKILL.md +129 -1
- package/assets/skills/spec-bug/SKILL.md +29 -24
- package/assets/skills/spec-diff/SKILL.md +131 -36
- package/assets/skills/spec-hotfix/SKILL.md +22 -19
- package/assets/skills/spec-next/SKILL.md +119 -56
- package/assets/skills/spec-review/SKILL.md +87 -0
- package/assets/skills/spec-reviewed/SKILL.md +31 -5
- package/assets/skills/spec-start/SKILL.md +19 -0
- package/package.json +1 -1
- package/src/cli.js +913 -116
- package/src/env/classify.js +87 -2
- package/src/env/config.js +214 -17
- package/src/env/live.js +94 -0
- package/src/env/resolve.js +36 -2
- package/src/env/review.js +542 -21
- package/src/env/serve.js +298 -19
- package/src/env/supervise.js +8 -1
- package/src/init.js +60 -9
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: spec-diff
|
|
3
|
-
description: See what a spec's worktree changed — render its diff as a page you can mark up, take that review pass back, and act on it. Answers at any point, including half-way through a phase. Use when the user says "/spec-diff", "show me the diff", "what did this phase change", "review this spec's work", wants to read a worktree's changes away from the terminal, or hands back what the review page produced — a six-digit claim code, or the pasted JSON.
|
|
3
|
+
description: See what a spec's worktree changed — render its diff as a page you can mark up, take that review pass back, and act on it. Answers at any point, including half-way through a phase. Use when the user says "/spec-diff", "show me the diff", "what did this phase change", "review this spec's work", wants to read a worktree's changes away from the terminal, or hands back what the review page produced — a six-digit claim code, a bare verdict word, or the pasted JSON.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# /spec-diff — see the phase before you commit it
|
|
@@ -41,13 +41,19 @@ sole candidate or prints the candidates.
|
|
|
41
41
|
The page has marks on it — `✓ accept` per file, notes against a line or a whole
|
|
42
42
|
file, answers to the checks a written review asked — and it
|
|
43
43
|
**ends in a decision**: `✓ Approve`, `↺ Request changes` or `… Discuss first`.
|
|
44
|
-
A review comes back to you one of
|
|
44
|
+
A review comes back to you one of three ways, and **all three are ordinary**:
|
|
45
45
|
|
|
46
46
|
- **A six-digit code** — `418207`, on its own. A *served* page hands its pass
|
|
47
47
|
straight to the engine, which holds it until someone reads the code out. This
|
|
48
48
|
is the usual way on a phone.
|
|
49
|
-
- **A
|
|
50
|
-
|
|
49
|
+
- **A verdict word** — `commit`, `commit-continue`, `continue`, `changes` or
|
|
50
|
+
`discuss`. A `file://` page cannot send anything, so where the reader marked
|
|
51
|
+
nothing it copies a command carrying the conclusion on its own. Send it with
|
|
52
|
+
`skitterspec spec-env review <spec> --verdict <word>`; it joins the same merge
|
|
53
|
+
a claimed pass does, so everything below is unchanged.
|
|
54
|
+
- **A pasted JSON blob** — the same `file://` page, once the reader has marked
|
|
55
|
+
something up: accepts and notes do not fit on a command line, so the whole
|
|
56
|
+
pass travels. Not legacy: it is the whole story for a local reader.
|
|
51
57
|
|
|
52
58
|
Either way, **this is not a request to render anything**: it is a review coming
|
|
53
59
|
back, and these steps replace §3–§5 below.
|
|
@@ -151,6 +157,10 @@ back, and these steps replace §3–§5 below.
|
|
|
151
157
|
absent verdict has always meant "report it and wait" — which is why that is
|
|
152
158
|
what it still means.
|
|
153
159
|
|
|
160
|
+
**A pass may carry an `action:` instead**, and then none of the above
|
|
161
|
+
applies: it concluded nothing. Go to **§2b**. The engine refuses a pass
|
|
162
|
+
carrying both, so there is never a choice to make between them.
|
|
163
|
+
|
|
154
164
|
3. **Say what you read, then ask what's up.** Report the accepted count, then
|
|
155
165
|
each open comment as `file:line — note`, then the files you would touch.
|
|
156
166
|
**Wait — unless the verdict already said otherwise.** Pasting on its own is
|
|
@@ -335,6 +345,69 @@ it — and the paste was the one place it broke. The *work* either authorises is
|
|
|
335
345
|
ordinary phase-sized cost, and step 3 is where the operator decides whether to
|
|
336
346
|
spend it.
|
|
337
347
|
|
|
348
|
+
## 2b. An action changes something, then hands the page back
|
|
349
|
+
|
|
350
|
+
Only on a pass carrying an **action**. Three of them, and
|
|
351
|
+
**all three end the same way**:
|
|
352
|
+
do the thing, re-render (§4), and wait again (§4b) — because the reader
|
|
353
|
+
still has a decision in front of them and has not made it.
|
|
354
|
+
|
|
355
|
+
| Action | Do | Then |
|
|
356
|
+
|--------|----|------|
|
|
357
|
+
| `live-on` | commit first (below), then `skitterspec spec-env live take <spec>` | re-render `--branch`, wait again |
|
|
358
|
+
| `allow-network` | `skitterspec spec-env review allow network` | re-render, wait again |
|
|
359
|
+
| `allow-remote` | `skitterspec spec-env review allow remote --set on` | re-render, wait again |
|
|
360
|
+
|
|
361
|
+
**THERE IS NO `live-off`, and its absence is deliberate.** Putting *this* change
|
|
362
|
+
live is about the diff on screen; handing the whole instance back to `main` is a
|
|
363
|
+
workspace decision with nothing to do with this review. The `live:` line names
|
|
364
|
+
`/spec-live main` for it — a command the operator types, which is also why no
|
|
365
|
+
skill runs it.
|
|
366
|
+
|
|
367
|
+
**AN ACTION IS NOT A VERDICT, and the gate is untouched by all three.** A phase
|
|
368
|
+
that ended armed the gate, and it is discharged by a committing verdict or a
|
|
369
|
+
recorded skip and by nothing else — so after any of these the phase still owes
|
|
370
|
+
an answer, and that is exactly why the run waits again rather than finishing.
|
|
371
|
+
The engine enforces this rather than trusting it: `ACTIONS` is disjoint from
|
|
372
|
+
`VERDICTS` and `COMMITTING`, so nothing that routes on a verdict can see one.
|
|
373
|
+
|
|
374
|
+
**`live-on` commits first, and the commit is a precondition rather than an answer.**
|
|
375
|
+
`live take` refuses a dirty worktree, and even without that guard
|
|
376
|
+
uncommitted work stays behind in the worktree — so what went live would be the
|
|
377
|
+
*previous* commit while the page claimed to be showing this one. Hand off to
|
|
378
|
+
`review.commitWith` exactly as §2a does (do not restate its rules here), then
|
|
379
|
+
take the instance. The reader has not approved anything by pressing it.
|
|
380
|
+
|
|
381
|
+
**It re-renders `--branch` afterwards**, because the working view is now empty:
|
|
382
|
+
the commit just happened, and the branch range is what still answers "what am I
|
|
383
|
+
looking at". §4's clean-tree fallback reaches the same place on its own, so
|
|
384
|
+
passing `--branch` is belt and braces rather than a separate rule.
|
|
385
|
+
|
|
386
|
+
**A mid-phase page does not get the commit.** Where the render took
|
|
387
|
+
`--buttons midrun`, the work is half a phase — committing it to look at it
|
|
388
|
+
running splits one phase across two commits and leaves a mess nobody asked for.
|
|
389
|
+
Say the phase needs to land first, re-render, and wait.
|
|
390
|
+
|
|
391
|
+
**Relay every refusal, and work around none of them.** `live take` refuses a
|
|
392
|
+
workbench another spec holds, a hotfix, a stateful spec, a branch touching
|
|
393
|
+
migrations, and a tree with no dev server listening. Each refusal names its own
|
|
394
|
+
way out. **Never park another spec's live session** to make room — that is
|
|
395
|
+
someone else's work, and freeing it is their decision. Re-render and wait, so
|
|
396
|
+
the reader can choose something else.
|
|
397
|
+
|
|
398
|
+
**`allow` writes a committed file, and that is worth a sentence.** It edits
|
|
399
|
+
`specs/.core/env.config.json` in the **primary checkout**, so it changes
|
|
400
|
+
behaviour for everyone who pulls and leaves that tree dirty — unlike the live
|
|
401
|
+
actions, which move a branch and write a gitignored receipt. The engine prints
|
|
402
|
+
the absolute path and whose tree it dirtied; relay that rather than letting a
|
|
403
|
+
shared setting change land silently. And it is **enable-only**: there is no
|
|
404
|
+
action that turns a tier off, because turning `network` off from a page reached
|
|
405
|
+
over the network kills the page doing the turning.
|
|
406
|
+
|
|
407
|
+
**`allow remote` permits publishing. It does not publish.** Publishing stays an
|
|
408
|
+
explicit ask in every case (§6), because the page it leaves behind is one this
|
|
409
|
+
tooling cannot remove.
|
|
410
|
+
|
|
338
411
|
## 3. Gate it on nothing
|
|
339
412
|
|
|
340
413
|
**This skill has no preconditions and must never grow one.** Not tests passing,
|
|
@@ -367,15 +440,17 @@ skitterspec spec-env review <spec> --branch # everything since the base bran
|
|
|
367
440
|
skitterspec spec-env review serve # every spec, on localhost
|
|
368
441
|
```
|
|
369
442
|
|
|
370
|
-
**
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
443
|
+
**There is no switch left to handle.** The engine serves and prints a **stack** —
|
|
444
|
+
one line per tier, `local`, `network`, `remote`, each either a URL or the one
|
|
445
|
+
command that turns it on. All of it is free and none of it publishes anything.
|
|
446
|
+
You are not choosing between them; you are relaying every line it printed.
|
|
447
|
+
`.claude/rules/spec-reports.md` carries the shape and why.
|
|
375
448
|
|
|
376
|
-
The operator who does not want a LAN listener
|
|
377
|
-
|
|
378
|
-
|
|
449
|
+
The operator who does not want a LAN listener sets `review.allowNetwork: false`
|
|
450
|
+
in `env.config.json` — `network` then reads `off` with the command that turns it
|
|
451
|
+
back on, and `local` is the loopback page. Turning serving off entirely is
|
|
452
|
+
`review.serve: "never"`, and then `local` is the `file://` page, which says on
|
|
453
|
+
its own line that it cannot send a verdict.
|
|
379
454
|
|
|
380
455
|
`serve` renders **per request**, so nothing it shows can be stale, and it lists
|
|
381
456
|
every spec with a worktree rather than one. `--host 0.0.0.0` binds beyond
|
|
@@ -428,21 +503,29 @@ committing verdict or a recorded skip.
|
|
|
428
503
|
|
|
429
504
|
**On `--page-only`, stop here** and report the path.
|
|
430
505
|
|
|
431
|
-
## 4a.
|
|
432
|
-
|
|
433
|
-
`spec-env review`
|
|
434
|
-
**
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
| `local` | the `file://`
|
|
440
|
-
| `
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
`
|
|
444
|
-
|
|
445
|
-
|
|
506
|
+
## 4a. Relay the stack — every tier, never one you picked
|
|
507
|
+
|
|
508
|
+
`spec-env review` prints one line per tier and `--json` carries the same thing
|
|
509
|
+
as `tiers`. **Relay all of them, in that order**, whatever the `reader:` line
|
|
510
|
+
says:
|
|
511
|
+
|
|
512
|
+
| Tier | Carries |
|
|
513
|
+
|------|---------|
|
|
514
|
+
| `local` | the loopback URL — or the `file://` page when nothing served, which says it cannot send a verdict |
|
|
515
|
+
| `network` | the LAN URL and any `also:` alternates under it — or `off` with the command that turns it on |
|
|
516
|
+
| `remote` | the published URL and *a verdict here needs `/spec-reviewed`* — or `off` with the command that turns it on |
|
|
517
|
+
|
|
518
|
+
**The `reader:` line decides nothing here any more.** It is still printed and
|
|
519
|
+
still the only place that question is answered, but the offer no longer branches
|
|
520
|
+
on it — because branching on it is what produced a `file://` page for a session
|
|
521
|
+
detected `unknown`, a LAN URL for a phone off the network, and an address that
|
|
522
|
+
changed underneath a reader mid-session. A tier that is off keeps its line, so a
|
|
523
|
+
reader who has left the house can see the surface exists and ask for it.
|
|
524
|
+
|
|
525
|
+
**Never read an environment variable to decide anything about the offer.** Not
|
|
526
|
+
`SSH_CONNECTION`, not `CLAUDE_CODE_*`, not a tty check — there is nothing left
|
|
527
|
+
for a detection to decide, and a second implementation of one could not be
|
|
528
|
+
tested and would drift. The ranking and the traps
|
|
446
529
|
(`CLAUDE_CODE_ENTRYPOINT` describes the *process*, not the reader; stdin is never
|
|
447
530
|
a tty under Claude Code) live in `review.js` beside the code, which is where they
|
|
448
531
|
belong.
|
|
@@ -461,8 +544,9 @@ answer left.
|
|
|
461
544
|
|
|
462
545
|
`review.reader` in `env.config.json` (`local` · `remote` · `detect`) lets the
|
|
463
546
|
operator settle where they are reading, and an explicit value is believed without
|
|
464
|
-
sniffing.
|
|
465
|
-
|
|
547
|
+
sniffing. What the engine may *do* is settled by the tier settings instead —
|
|
548
|
+
`review.serve` (`always` · `never`), `review.allowNetwork` and
|
|
549
|
+
`review.allowRemote` — not by the reader.
|
|
466
550
|
|
|
467
551
|
## 4b. Wait for the verdict — because asking for one means waiting for it
|
|
468
552
|
|
|
@@ -483,8 +567,8 @@ said the page was **ready** rather than that it was **waiting**.
|
|
|
483
567
|
**So the question is not "can I watch a file".** It is "am I asking?" — and the
|
|
484
568
|
transport only decides what carries the answer back:
|
|
485
569
|
|
|
486
|
-
- **A served page** posts to the local store;
|
|
487
|
-
steps below
|
|
570
|
+
- **A served page** posts to the local store; the engine's own wait returns when
|
|
571
|
+
it lands, and the steps below run it.
|
|
488
572
|
- **A `file://` page** has nothing to post to, so the reader pastes the pass and
|
|
489
573
|
their next message carries it. Say you are holding and **end the turn** — that
|
|
490
574
|
is the same wait, carried by the conversation. It is not a lesser one.
|
|
@@ -498,9 +582,20 @@ already arrived. Those get the `Review` row, no question, and nothing is owed.
|
|
|
498
582
|
|
|
499
583
|
1. **Note the moment**, as an ISO timestamp, before you start. That instant is
|
|
500
584
|
the entire scope of what may be claimed without a person naming it.
|
|
501
|
-
2. **
|
|
502
|
-
|
|
503
|
-
|
|
585
|
+
2. **Run the engine's wait in the background, and end the turn:**
|
|
586
|
+
|
|
587
|
+
```
|
|
588
|
+
skitterspec spec-env review wait <spec> --since <timestamp>
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
**Never a watcher of your own, and never a timeout.** It lasts as long as the
|
|
592
|
+
session, because a reader who walks away from a diff is the normal case — and
|
|
593
|
+
a loop composed here would be proven by nothing, which is how one written as
|
|
594
|
+
`[ "$x" \> "$y" ]` spun for five minutes in zsh and looked exactly like
|
|
595
|
+
patience. `/spec-next` §5 carries the full account.
|
|
596
|
+
|
|
597
|
+
Not a poll and not a held-open turn: the operator has their terminal back,
|
|
598
|
+
and the session costs nothing while they read.
|
|
504
599
|
3. **On waking, let the engine choose:**
|
|
505
600
|
|
|
506
601
|
```
|
|
@@ -711,8 +806,8 @@ consumed rather than stored.
|
|
|
711
806
|
purpose; someone who forgot they had voted has not, and only the report tells
|
|
712
807
|
them apart.
|
|
713
808
|
|
|
714
|
-
`Review` carries the files and `+`/`−`, the
|
|
715
|
-
|
|
809
|
+
`Review` carries the files and `+`/`−`, and the tier stack — labelled, run
|
|
810
|
+
together with `·` because a row is one cell. Where the page holds a review pass, it also
|
|
716
811
|
carries the three totals — files accepted, comments open, comments answered.
|
|
717
812
|
`--json` reports those under `notes.totals`; read that, never the diff.
|
|
718
813
|
|
|
@@ -274,30 +274,33 @@ this worktree is refused until a verdict is sent or
|
|
|
274
274
|
`skitterspec spec-env review skip "<reason>"` records the decision to move on.
|
|
275
275
|
The exit is always one command, and one of them is *"I am moving on"*.
|
|
276
276
|
|
|
277
|
-
Relay the
|
|
278
|
-
|
|
277
|
+
Relay the engine's **stack** — the `local:`, `network:` and `remote:` lines, all
|
|
278
|
+
three — rather than the bare `page:` path: a path is not clickable in any
|
|
279
|
+
terminal, and a page nobody can open is a page nobody reads. Relay the `live:`
|
|
280
|
+
line with it where the engine printed one, and nothing where it did not.
|
|
281
|
+
`.claude/rules/spec-reports.md` carries the shape, including why a rigid
|
|
282
|
+
contract took that line.
|
|
279
283
|
|
|
280
284
|
- **Never write the review unasked**, and **never publish**. Publishing leaves
|
|
281
285
|
something behind that this tooling cannot remove, so it is always an ask. A
|
|
282
286
|
`file://` link is no use on a phone, and saying so **is** the ask —
|
|
283
287
|
`/spec-diff` §6 owns how.
|
|
284
|
-
**
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
drift.
|
|
288
|
+
**The `reader:` line no longer decides anything here, and that is the point.**
|
|
289
|
+
It is still printed, and it is still the only place that question is answered —
|
|
290
|
+
but the offer does not change with it, because the stack lists every tier
|
|
291
|
+
whatever it says. Three reader states used to mean three different offers, and
|
|
292
|
+
that branching is exactly what produced a `file://` page on a session detected
|
|
293
|
+
`unknown`, a LAN URL for a phone off the network, and an address that changed
|
|
294
|
+
underneath a reader when detection flipped mid-session.
|
|
295
|
+
|
|
296
|
+
So: **relay all three tier lines, every time.** Any `also:` lines sit under
|
|
297
|
+
`network` — the other addresses this machine has, offered because the
|
|
298
|
+
best-guess one can be wrong — so pass them on rather than editing them out.
|
|
299
|
+
|
|
300
|
+
**Never read an environment variable to decide anything about the offer** — not
|
|
301
|
+
`SSH_CONNECTION`, not `CLAUDE_CODE_*`, not a tty check. There is nothing left
|
|
302
|
+
here for a detection to decide, and a second implementation of one could not be
|
|
303
|
+
tested and would drift.
|
|
301
304
|
|
|
302
305
|
**Serving is the engine's to do; publishing is never.** A `remote` reader
|
|
303
306
|
authorises a local server — one process, ended by one flag, leaving nothing
|
|
@@ -154,6 +154,24 @@ Before writing any code for this phase, get the workspace clean:
|
|
|
154
154
|
being *"I am moving on"*, which is what keeps this a push rather than a wall —
|
|
155
155
|
a gate with no exit gets switched off wholesale instead of answered.
|
|
156
156
|
|
|
157
|
+
- **Report anything left waiting, and claim none of it.** Ask the engine once:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
skitterspec spec-env review waiting
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
A pass listed here arrived when nothing was watching — a wait that never ran,
|
|
164
|
+
a session cleared, a terminal closed overnight — and no watcher can recover
|
|
165
|
+
those, however good. Relay what it prints: the spec, the code, the verdict and
|
|
166
|
+
the age, with `/spec-reviewed <code>` to pick one up and `--drop <code>` to
|
|
167
|
+
disown it.
|
|
168
|
+
|
|
169
|
+
**It is information, not a gate.** It never refuses, nothing here is
|
|
170
|
+
conditional on it, and **you never claim one** — `/spec-diff` §0 stands
|
|
171
|
+
unchanged, and a pass sitting there when you arrived was not sent to you.
|
|
172
|
+
Silent when nothing is waiting, which is the usual case: say nothing rather
|
|
173
|
+
than reporting that there was nothing to report.
|
|
174
|
+
|
|
157
175
|
## 3. Implement the phase
|
|
158
176
|
|
|
159
177
|
Identify the **first unfinished phase** from the `00-overview.md` phase index,
|
|
@@ -299,30 +317,51 @@ so one subject lives in one place:
|
|
|
299
317
|
|
|
300
318
|
## ⏸ Review ready — <N> files, +<a> −<d>
|
|
301
319
|
|
|
302
|
-
**
|
|
320
|
+
- **local** — <the `local:` URL>
|
|
321
|
+
- **network** — <the `network:` URL, or off with the command that turns it on>
|
|
322
|
+
- **remote** — <the `remote:` URL, or off with the command that turns it on>
|
|
303
323
|
|
|
304
|
-
|
|
324
|
+
<the `live:` line, exactly as the engine printed it — omitted when it printed none>
|
|
305
325
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
exists for, and then the banner carries the published URL with what is true of
|
|
310
|
-
it: *press a verdict, then type `/spec-reviewed`*, because nothing pushes from
|
|
311
|
-
the artifact store into this conversation.
|
|
326
|
+
I'm holding here until you send a verdict — the wait covers local and network.
|
|
327
|
+
|
|
328
|
+
`/spec-reviewed` picks it up · `spec-env review skip "<reason>"` moves on
|
|
312
329
|
|
|
313
|
-
**
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
330
|
+
**The stack is the engine's — copy it, do not compose it.** `spec-env review`
|
|
331
|
+
printed one line per tier, in that order, and `--json` carries the same thing as
|
|
332
|
+
`tiers`. Relay all three whatever they say, including the ones that are off with
|
|
333
|
+
the command that turns them on: a reader who has left the house cannot ask for a
|
|
334
|
+
remote page they were never shown.
|
|
335
|
+
|
|
336
|
+
**Do not pick one for them.** The engine used to, and the guess failed three
|
|
337
|
+
separate ways in one day. `.claude/rules/spec-reports.md` carries the shape and
|
|
338
|
+
why the *one link, never two* rule now reads
|
|
339
|
+
**one link per reachable store, each labelled** —
|
|
340
|
+
local and network are two doors into one room, so the wait
|
|
341
|
+
covers both. `remote` is a second store, so its own line carries its own caveat:
|
|
342
|
+
*a verdict here needs `/spec-reviewed`*, because nothing pushes from
|
|
343
|
+
the artifact store into this conversation. Never let the holding line promise a
|
|
344
|
+
wait over that tier.
|
|
345
|
+
|
|
346
|
+
**Publishing is still never yours to do.** `remote` reading `off` is the normal
|
|
347
|
+
state, and the fix for it is the command on that line — typed by the operator,
|
|
348
|
+
or pressed on the page, because a published page is one this tooling cannot
|
|
349
|
+
remove and permitting it is not publishing it.
|
|
350
|
+
|
|
351
|
+
**The `live:` line goes with the stack**, and it is the engine's line too —
|
|
352
|
+
copied as printed, and **absent when the engine printed none**. The page can act
|
|
353
|
+
on it: a press puts the spec live or takes it down and hands the reader back the
|
|
354
|
+
same page, which is `/spec-diff` §2b's routing and not this skill's. What
|
|
355
|
+
matters here is that an action **never clears the gate this phase armed** — the
|
|
356
|
+
reader has looked at it running and concluded nothing, so the phase still owes a
|
|
357
|
+
verdict.
|
|
319
358
|
|
|
320
359
|
---
|
|
321
360
|
|
|
322
361
|
**Where you are not waiting, it stays the `Review` row** — the counts and the
|
|
323
362
|
page link, and **no question**:
|
|
324
363
|
|
|
325
|
-
| **Review** | <N> files, +<a> −<d> ·
|
|
364
|
+
| **Review** | <N> files, +<a> −<d> · **local** <URL> · **network** <URL> · **remote** off |
|
|
326
365
|
|
|
327
366
|
At the end of a phase you are always waiting, so this shape belongs to the
|
|
328
367
|
renders that are not this step: a mid-phase `/spec-diff`, a page produced
|
|
@@ -352,16 +391,25 @@ whether the two places are adjacent rows or a row and a banner.
|
|
|
352
391
|
|
|
353
392
|
The row is findable, but a row cannot make the continuation follow from the
|
|
354
393
|
reading — and that is the gap the whole gate exists to close.
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
reader pressed is what carries the work on.
|
|
394
|
+
|
|
395
|
+
**The wait is a command. Do not write one.**
|
|
358
396
|
|
|
359
397
|
1. **Note the moment you start waiting**, as an ISO timestamp. That instant is
|
|
360
398
|
the whole scope of what you may claim.
|
|
361
|
-
2. **
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
399
|
+
2. **Run the engine's wait in the background, and end your turn:**
|
|
400
|
+
|
|
401
|
+
```
|
|
402
|
+
skitterspec spec-env review wait <spec> --since <the timestamp>
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
It returns when a pass arrives inside that window, and
|
|
406
|
+
**it takes no timeout unless you pass one** —
|
|
407
|
+
the wait lasts as long as your session, because a reader who walks away from
|
|
408
|
+
a diff is the normal case rather than the edge one. Do not give it a duration
|
|
409
|
+
of your own.
|
|
410
|
+
|
|
411
|
+
Then **end your turn**. Do not poll it and do not hold the turn open: the
|
|
412
|
+
point is that the reader has the terminal back while they read.
|
|
365
413
|
3. **On waking, let the engine pick**:
|
|
366
414
|
|
|
367
415
|
```
|
|
@@ -369,11 +417,26 @@ reader pressed is what carries the work on.
|
|
|
369
417
|
```
|
|
370
418
|
|
|
371
419
|
It claims the one pass that arrived inside the window, and acts on nothing
|
|
372
|
-
at all when none did (the
|
|
373
|
-
when two did (two sittings, or two people — the operator has the
|
|
420
|
+
at all when none did (the wait can be woken by something that was not a
|
|
421
|
+
pass) or when two did (two sittings, or two people — the operator has the
|
|
422
|
+
codes).
|
|
374
423
|
4. **Route on the verdict** exactly as `/spec-diff` §2 and §4 describe. Do not
|
|
375
424
|
restate that routing here.
|
|
376
425
|
|
|
426
|
+
**WHY A COMMAND RATHER THAN A LOOP YOU COMPOSE.** This step used to say "watch
|
|
427
|
+
the pending store" and stop, so every run invented its own watcher in shell —
|
|
428
|
+
and three failed in two days, each reaching the operator as *"I pressed the
|
|
429
|
+
button and nothing happened"*. The worst wrote
|
|
430
|
+
`until [ -f "$P" ] && [ "$x" \> "$y" ]`: valid bash, a syntax error in zsh, a
|
|
431
|
+
predicate that could never be true. It spun for five minutes writing to a
|
|
432
|
+
stderr nobody reads.
|
|
433
|
+
|
|
434
|
+
**The bug is not the lesson — the silence is.** A watcher that can never fire
|
|
435
|
+
and one patiently working are indistinguishable from outside, so nothing about
|
|
436
|
+
that run looked wrong until the operator asked. `review wait` says it has
|
|
437
|
+
started, is written once, and is tested against a store that gains a pass
|
|
438
|
+
mid-flight. A predicate composed fresh each time is proven by nothing.
|
|
439
|
+
|
|
377
440
|
**WHY THIS IS SAFE, AND WHAT IT COSTS.** It was once true that a device
|
|
378
441
|
reaching your page could not reach your conversation, and that fact was the
|
|
379
442
|
whole guard: a pass sat in the holding area until a person typed
|
|
@@ -387,11 +450,11 @@ is that the page can now act, so the token has become a credential rather than
|
|
|
387
450
|
a convenience — and `--claim-since` refusing to choose between two passes is
|
|
388
451
|
what stops a race becoming a wrong commit.
|
|
389
452
|
|
|
390
|
-
**Where the harness cannot
|
|
391
|
-
the wait. Say the page is rendered and that you are holding
|
|
392
|
-
your turn; the reader's next message is what carries it,
|
|
393
|
-
it up from the paste exactly as it always has. The gate
|
|
394
|
-
the engine's, not the
|
|
453
|
+
**Where the harness cannot run something in the background, you still wait** —
|
|
454
|
+
the turn ending is the wait. Say the page is rendered and that you are holding
|
|
455
|
+
for the pass, then end your turn; the reader's next message is what carries it,
|
|
456
|
+
and `/spec-diff` picks it up from the paste exactly as it always has. The gate
|
|
457
|
+
holds either way: it is the engine's, not the wait's.
|
|
395
458
|
|
|
396
459
|
This once read *"change nothing"* — keep the row, keep the question, do not
|
|
397
460
|
wait — and that exemption is the hatch a whole class of unanswerable questions
|
|
@@ -401,38 +464,38 @@ came through. Every harness can end a turn.
|
|
|
401
464
|
one line; by the time this step is reached the chain has finished, so waiting
|
|
402
465
|
here stops nothing that was still going to happen.
|
|
403
466
|
|
|
404
|
-
Relay the
|
|
405
|
-
|
|
467
|
+
Relay the engine's **stack** — the `local:`, `network:` and `remote:` lines —
|
|
468
|
+
never the bare `page:` path: a path is not clickable in any terminal, and a page
|
|
469
|
+
nobody can open is a page nobody reads.
|
|
406
470
|
|
|
407
471
|
- **Never write the review unasked**, and **never publish**. Publishing leaves
|
|
408
472
|
something behind that this tooling cannot remove, so it is always something
|
|
409
473
|
someone asks for. A `file://` link is no use on a phone, and saying so **is**
|
|
410
474
|
the ask — publishing is the answer to it, and `/spec-diff` §6 owns how.
|
|
411
|
-
**
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
`file://` URL with its marker, and that is when publishing is worth naming.
|
|
475
|
+
**The `reader:` line no longer decides anything here, and that is the point.**
|
|
476
|
+
It is still printed, and it is still the only place that question is answered —
|
|
477
|
+
but the offer does not change with it, because the stack lists every tier
|
|
478
|
+
whatever it says. Three reader states used to mean three different offers, and
|
|
479
|
+
that branching is exactly what produced a `file://` page on a session detected
|
|
480
|
+
`unknown`, a LAN URL for a phone off the network, and an address that changed
|
|
481
|
+
underneath a reader when detection flipped mid-session.
|
|
482
|
+
|
|
483
|
+
So: **relay all three tier lines, every time.** Any `also:` lines sit under
|
|
484
|
+
`network` — the other addresses this machine has, offered because the
|
|
485
|
+
best-guess one can be wrong — so pass them on rather than editing them out.
|
|
486
|
+
|
|
487
|
+
**Never read an environment variable to decide anything about the offer** — not
|
|
488
|
+
`SSH_CONNECTION`, not `CLAUDE_CODE_*`, not a tty check. There is nothing left
|
|
489
|
+
here for a detection to decide, and a second implementation of one could not be
|
|
490
|
+
tested and would drift.
|
|
491
|
+
|
|
492
|
+
**Serving is the engine's to do; publishing is never.** The server is one
|
|
493
|
+
process, ended by one flag, leaving nothing behind, and `review.allowNetwork`
|
|
494
|
+
decides whether it binds wide — no detection involved. A reader the server CAN
|
|
495
|
+
reach is not a reason to publish as well: the tiers they can already open are on
|
|
496
|
+
the stack. Publishing leaves a page this tooling cannot remove, so it stays an
|
|
497
|
+
ask in every case, always — which is why `remote` shows the command rather than
|
|
498
|
+
a URL until someone types it.
|
|
436
499
|
|
|
437
500
|
- **Never fatal.** A failed render — no worktree, a git error — is one line and
|
|
438
501
|
the phase is still done. The page is a convenience; the repo is the record.
|