@iceinvein/agent-skills 0.23.8 → 0.23.9

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.
Files changed (54) hide show
  1. package/package.json +1 -1
  2. package/skills/index.json +3 -3
  3. package/skills/magpie/README.md +1 -1
  4. package/skills/magpie/SKILL.md +2 -2
  5. package/skills/magpie/evals/README.md +22 -8
  6. package/skills/magpie/evals/bare-invocation-goes-local/case.yaml +4 -0
  7. package/skills/magpie/evals/bare-invocation-goes-local/fixture.sh +159 -0
  8. package/skills/magpie/evals/bare-invocation-goes-local/graders/branch-was-reachable.md +5 -0
  9. package/skills/magpie/evals/bare-invocation-goes-local/graders/never-went-to-a-pr.md +7 -0
  10. package/skills/magpie/evals/bare-invocation-goes-local/graders/setup-ran-local.md +6 -0
  11. package/skills/magpie/evals/bare-invocation-goes-local/prompt.md +11 -0
  12. package/skills/magpie/evals/baselines/claude-opus-5-5.json +27 -3
  13. package/skills/magpie/evals/rules.json +8 -0
  14. package/skills/magpie/scripts/__tests__/skill-lint.test.ts +8 -0
  15. package/skills/magpie/skill.json +1 -1
  16. package/skills/sluice/SKILL.md +13 -0
  17. package/skills/sluice/evals/README.md +5 -2
  18. package/skills/sluice/evals/deep-plan-checks-the-intent/case.yaml +4 -0
  19. package/skills/sluice/evals/deep-plan-checks-the-intent/fixture.sh +124 -0
  20. package/skills/sluice/evals/deep-plan-checks-the-intent/graders/announces-deep-channel.md +12 -0
  21. package/skills/sluice/evals/deep-plan-checks-the-intent/graders/checker-dispatched.md +12 -0
  22. package/skills/sluice/evals/deep-plan-checks-the-intent/graders/no-source-edit.md +11 -0
  23. package/skills/sluice/evals/deep-plan-checks-the-intent/graders/no-source-write.md +11 -0
  24. package/skills/sluice/evals/deep-plan-checks-the-intent/graders/signoff-reports-the-check.md +16 -0
  25. package/skills/sluice/evals/deep-plan-checks-the-intent/graders/sluice-fired.md +5 -0
  26. package/skills/sluice/evals/deep-plan-checks-the-intent/graders/spec-has-an-intent-map.md +19 -0
  27. package/skills/sluice/evals/deep-plan-checks-the-intent/graders/stopped-for-signoff.md +8 -0
  28. package/skills/sluice/evals/deep-plan-checks-the-intent/prompt.md +20 -0
  29. package/skills/sluice/evals/deep-plan-checks-the-plan/case.yaml +4 -0
  30. package/skills/sluice/evals/deep-plan-checks-the-plan/fixture.sh +178 -0
  31. package/skills/sluice/evals/deep-plan-checks-the-plan/graders/announces-deep-channel.md +12 -0
  32. package/skills/sluice/evals/deep-plan-checks-the-plan/graders/every-decision-built.md +15 -0
  33. package/skills/sluice/evals/deep-plan-checks-the-plan/graders/no-source-edit.md +11 -0
  34. package/skills/sluice/evals/deep-plan-checks-the-plan/graders/no-source-write.md +11 -0
  35. package/skills/sluice/evals/deep-plan-checks-the-plan/graders/plan-checker-dispatched.md +11 -0
  36. package/skills/sluice/evals/deep-plan-checks-the-plan/graders/plan-cites-the-spec.md +7 -0
  37. package/skills/sluice/evals/deep-plan-checks-the-plan/graders/sluice-fired.md +5 -0
  38. package/skills/sluice/evals/deep-plan-checks-the-plan/graders/stops-at-preflight.md +14 -0
  39. package/skills/sluice/evals/deep-plan-checks-the-plan/graders/validate-ran.md +9 -0
  40. package/skills/sluice/evals/deep-plan-checks-the-plan/prompt.md +12 -0
  41. package/skills/sluice/evals/deep-plan-reads-the-prior-spec/case.yaml +4 -0
  42. package/skills/sluice/evals/deep-plan-reads-the-prior-spec/fixture.sh +125 -0
  43. package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/announces-deep-channel.md +12 -0
  44. package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/context-cites-the-prior-spec.md +6 -0
  45. package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/design-follows-the-prior-spec.md +21 -0
  46. package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/design-written-to-docs.md +5 -0
  47. package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/no-source-edit.md +11 -0
  48. package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/no-source-write.md +11 -0
  49. package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/sluice-fired.md +5 -0
  50. package/skills/sluice/evals/deep-plan-reads-the-prior-spec/prompt.md +17 -0
  51. package/skills/sluice/evals/rules.json +24 -0
  52. package/skills/sluice/references/deep-channel.md +145 -8
  53. package/skills/sluice/scripts/plan.sh +115 -4
  54. package/skills/sluice/skill.json +2 -2
@@ -84,6 +84,30 @@
84
84
  "deep-plan-traces-the-example"
85
85
  ]
86
86
  },
87
+ {
88
+ "id": "deep-reads-the-ground-before-the-design",
89
+ "states": "references/deep-channel.md: The ground",
90
+ "origin": "docs/specs/2026-10-21-sluice-deep-alignment.md",
91
+ "cases": [
92
+ "deep-plan-reads-the-prior-spec"
93
+ ]
94
+ },
95
+ {
96
+ "id": "deep-checks-the-spec-against-the-intent",
97
+ "states": "references/deep-channel.md: The alignment checks",
98
+ "origin": "docs/specs/2026-10-21-sluice-deep-alignment.md",
99
+ "cases": [
100
+ "deep-plan-checks-the-intent"
101
+ ]
102
+ },
103
+ {
104
+ "id": "deep-checks-the-plan-against-the-spec",
105
+ "states": "references/deep-channel.md: The alignment checks",
106
+ "origin": "docs/specs/2026-10-21-sluice-deep-alignment.md",
107
+ "cases": [
108
+ "deep-plan-checks-the-plan"
109
+ ]
110
+ },
87
111
  {
88
112
  "id": "deep-stops-for-plan-and-pre-flight",
89
113
  "states": "SKILL.md: Deep channel",
@@ -8,6 +8,19 @@
8
8
  message convention is one of them, written out at literal value along with
9
9
  anything the repo forbids a message from carrying. An implementer arrives
10
10
  with its own defaults and will use them on anything you left unsaid.
11
+ - A **Spec** line, `**Spec:** <path>` directly under the title, names the
12
+ signed-off design the plan builds. It is a line rather than a mention in
13
+ the Goal because `plan.sh validate` reads the spec's `### D<n>.` decisions
14
+ from that path, and a path written into prose gives it nothing to parse. A
15
+ relative path is read from the plan's repo root, as `docs/specs/...`, so
16
+ validate means the same thing from any directory; outside a git repo it is
17
+ read from the plan's own directory.
18
+ - A **Builds** line on every task names the decisions it builds,
19
+ `**Builds:** D1, D3`, or `**Builds:** none, <why>` for a task such as a
20
+ version bump that builds none. Validate checks coverage from these lines
21
+ and nothing else: every decision built by some task, no task building one
22
+ the spec lacks. Step prose mentioning `D1` does not count, because a docs
23
+ task that reads its work back "against D1 to D4" builds none of them.
11
24
  - No implementer sees a neighbouring task, so each task carries a
12
25
  **Contract**, not optional: a symbol absent from it does not exist for
13
26
  whoever builds the task. `Needs` names what this task calls but another
@@ -54,18 +67,26 @@ Most of what this section asks for needs no judgement to check: a `Needs` no
54
67
  task `Offers`, a "TBD", a step deferring to a neighbour, a missing `Contract`,
55
68
  a plan with no flip or two of them, an `Effort` mark on a task the tier table
56
69
  sends to tier 3, a `Model` mark anywhere. Those come back as errors with the
57
- task number on them. The warnings are the judgement calls left to you: a step
58
- with no proof, a `Needs` satisfied only by a later task, two tasks whose
59
- `Touches` overlap. Reading the plan yourself catches these on a good day, and
60
- the point of a check is the other kind of day.
70
+ task number on them. It also checks the plan against its spec, and a miss
71
+ there is an error too: a Spec line naming a readable file whose decisions are
72
+ `### D<n>.` headings, a Builds line on every task of `D<n>` ids or
73
+ `none, <why>`, every decision built and none invented. Each message says what
74
+ to fix, and names the task where one is at fault, so read them as validate
75
+ prints them. The
76
+ warnings are the judgement calls left to you: a step with no proof, a `Needs`
77
+ satisfied only by a later task, two tasks whose `Touches` overlap. Reading
78
+ the plan yourself catches these on a good day, and the point of a check is
79
+ the other kind of day.
61
80
 
62
81
  ```
63
82
  # Plan: <topic>
83
+ **Spec:** <path to the signed-off design>
64
84
  ## Ground Rules
65
85
  - <exact value>
66
86
  ### Task N: <name>
67
87
  **Contract:** Needs: <sig> | Offers: <sig>
68
88
  **Touches:** <path> (new) | <path> (edit) | <path> (test)
89
+ **Builds:** <D<n>, ..., or none and why>
69
90
  **Flips:** <what changes, from what, or omit>
70
91
  **Review:** <reason, or omit>
71
92
  **Effort:** <low, and why the work is mechanical, or omit>
@@ -118,6 +139,10 @@ Pre-flight lands in both, which is the one deliberate overlap: `run.json` holds
118
139
  the answer, so the file can say whether the stop happened at all, and the record
119
140
  holds the reason, so a reader can tell whether it should have gone that way.
120
141
  Those rows come first and open the record for the same reason they always did.
142
+ The outcomes of the intent check and the plan check sit among those opening
143
+ rows, each with its finding count and what came of them: both ran before the
144
+ record existed, so they are held until it opens, as pre-flight's answers are,
145
+ and written in with them.
121
146
 
122
147
  Where it goes follows the repo if the repo has a convention, and
123
148
  `docs/plans/YYYY-MM-DD-<topic>-record.md` if it does not. It belongs to you
@@ -183,6 +208,58 @@ on it, and let the next instruction start the plan. Nothing else about this
183
208
  section changes, because the obligation was never the mode's, only the
184
209
  enforcement was.
185
210
 
211
+ ## The ground
212
+
213
+ Read the repo before you walk anything through it. A prior spec often settles
214
+ the very fork you were about to ask about, and "anything you can settle from
215
+ the repo is not that fork" only holds once the repo has been read: until then
216
+ every fork looks like your partner's, and every pattern looks like yours to
217
+ invent. So the ground comes first, then the trace, then the fork question,
218
+ then the draft.
219
+
220
+ Ask for three things, each cited by path:
221
+
222
+ 1. **Prior designs.** Specs and plans under the repo's spec and plan
223
+ directories that touch the same subsystems or files, and what each settled
224
+ that the new design must build on, supersede or leave alone.
225
+ 2. **The code beside the path.** How the nearest similar feature is built (its
226
+ shape, where its state lives, how it is tested), so the design matches the
227
+ repo rather than inventing a pattern beside it.
228
+ 3. **Stated constraints.** Repo instructions, CI gates, the test runner and its
229
+ conventions, and release steps that the design or its plan will answer to.
230
+
231
+ Dispatch it as a read-only Explore agent where the harness has one, for the
232
+ reason the reviewer is dispatched: the file dumps fill its context instead of
233
+ yours, and you only need what it concluded. Where there is no dispatch, it is
234
+ your own read, with the same three questions. Either way the findings go into
235
+ the spec's `## Context`, one line each with the path it came from, so the
236
+ intent check and every later reader can see what the design was built on and
237
+ go and look.
238
+
239
+ A design whose `## Context` cites no earlier spec, when one covers the same
240
+ scripts, is the gap this section exists to close. Read for it rather than
241
+ assuming there is none.
242
+
243
+ The spec has a fixed shape, because the intent check reads it by section:
244
+
245
+ ```
246
+ # Design: <topic>
247
+ ## Intent
248
+ ## Context
249
+ ## Decisions
250
+ ### D1. <title>
251
+ ## Out of scope
252
+ ## Trace
253
+ ```
254
+
255
+ `## Intent` holds the request quoted verbatim, each question asked through
256
+ `AskUserQuestion` with the answer it got, and a table mapping each ask to the
257
+ `D<n>` decisions that deliver it, or to `## Out of scope` with the reason.
258
+ Decisions are headed `### D<n>. <title>`, which is what `plan.sh validate`
259
+ reads. Out of scope is its own section and never a numbered decision, because
260
+ a decision is something a task builds, and one no task could build would fail
261
+ the plan's coverage check for being honest.
262
+
186
263
  ## The trace
187
264
 
188
265
  A design written from the shape of the code (which functions exist, which slots
@@ -212,15 +289,73 @@ the spec on approval):
212
289
  what the design does instead. An empty list is a result, not a skipped
213
290
  step.
214
291
 
215
- Walking first is also what puts a fork where **The design stop and plan mode**
216
- wants it: one the walk turns up only your partner can settle is asked before
217
- the design is written, and the trace goes into the design with the answer.
292
+ The order is the ground, the walk, the fork, the draft. Walking after the
293
+ ground and before the fork is what puts a fork where **The design stop and
294
+ plan mode** wants it: one the walk turns up only your partner can settle is
295
+ asked before the design is written, and the trace goes into the design with
296
+ the answer.
218
297
 
219
298
  The trace is what the sign-off is signing: a design without the section is
220
299
  incomplete, however good the rest reads. It is not a prototype and not a hunt;
221
300
  anything that only shows at runtime (routing, timing, a renderer) stays with
222
301
  the hunt after the flip.
223
302
 
303
+ ## The alignment checks
304
+
305
+ A spec can mention every ask and deliver none of them, and a plan can cite
306
+ every decision and build half of one. Its author reads past both, because the
307
+ author knows what was meant. So each of the two stops gets a fresh reader
308
+ first: a dispatch with fresh context on this session's model and effort,
309
+ never downshifted, as with the final review, since a cheaper reader is the one
310
+ that misses a gap in meaning.
311
+
312
+ **The intent check runs before sign-off**, once the draft and its trace are
313
+ written and before `ExitPlanMode` or the prose stop. Send it the path the
314
+ design is drafted at, the harness's plan file under plan mode and the spec
315
+ file otherwise, and ask it to read the design's `## Intent` section by that
316
+ name, then:
317
+
318
+ - Does each ask in `## Intent` reach a decision that actually delivers it, not
319
+ one that merely mentions it?
320
+ - Is any decision traceable to no ask, an addition nobody asked for?
321
+ - Is any ask dropped, or moved to Out of scope without a reason?
322
+ - Does the design honour every answer recorded in `## Intent` and every
323
+ constraint in `## Context`?
324
+
325
+ Run it before the stop rather than after because a partner who signs first
326
+ signs a spec whose gaps were not yet known. On a redraft after the stop, run
327
+ it again only if a decision changed, since the map is what it reads.
328
+
329
+ **The plan check runs after `plan.sh validate` is clean and before
330
+ pre-flight**'s questions, with the spec and plan paths both in the brief:
331
+
332
+ - Does each task citing `D<n>` actually build what `D<n>` says? Across its
333
+ tasks, is each decision covered whole rather than in part?
334
+ - Does any task build something listed in Out of scope, or a shape the Trace
335
+ ruled out?
336
+ - Do the Ground Rules contradict a constraint in the spec's Context?
337
+ - Does the `Flips` task turn on what the spec says turns on?
338
+
339
+ Validate has already settled what a parser can, so this reader is spent on
340
+ what it cannot: whether the words of a task do what the words of a decision
341
+ say.
342
+
343
+ Fix each finding you agree with, in the draft for the intent check and in the
344
+ plan for the plan check, before the stop; the spec is signed by the time the
345
+ plan check runs, so nothing it finds edits the spec.
346
+ What you dispute goes into that stop's message, one line each, so your partner
347
+ signs with the disagreements in view rather than finding them later. The
348
+ sign-off message says the intent check ran, how many findings it returned and
349
+ how many were fixed; a stop that says nothing about it reads the same as one
350
+ where it never ran. After the plan check's fixes, re-run `plan.sh validate`,
351
+ because a fix to a Builds line is exactly what validate checks and the check
352
+ it passed was on the plan before the fix. Both outcomes go into the run
353
+ record's opening rows when it opens, for the reason that section gives.
354
+
355
+ Without dispatch there is no fresh reader to send. Say so at the stop and ask
356
+ the same questions yourself, as **When dispatch is unavailable** says for
357
+ review: your own read is weaker, and your partner should know it was yours.
358
+
224
359
  ## Pre-flight
225
360
 
226
361
  Design signed off, plan written, nothing built yet. Before Task 1, stop once
@@ -530,7 +665,9 @@ who will carry it out, only your partner's read of work you will do yourself.
530
665
  Task isolation is gone, so
531
666
  `run.json` and the record now carry all of the state that outlives compaction
532
667
  and matter more, not less. And fresh context is unavailable, which was the entire thing
533
- review was buying.
668
+ review was buying. The alignment checks fall back to your own read of the
669
+ same questions, and you say so at the stop, so the sign-off is not mistaken
670
+ for one a fresh reader cleared.
534
671
 
535
672
  One thing does not change: the work still owes a review. Reading your own diff
536
673
  is not one, and the table below still names which tasks needed the stronger
@@ -13,6 +13,11 @@
13
13
  #
14
14
  # validate prints one line per finding. An error means the plan cannot be
15
15
  # dispatched as written; a warning is a judgement call left to its author.
16
+ # Besides the plan's own skeleton, it reads the spec the plan's **Spec:** line
17
+ # names (a relative path from the plan's repo root, or from the plan's own
18
+ # directory outside a repo) and checks every "### D<n>." decision
19
+ # there is built by some task's **Builds:** line, and that no task builds one the
20
+ # spec does not have.
16
21
  # import seeds the run state's task rows from the plan, so the ids, names, the
17
22
  # flip, the effort marks and the tiers come from the file rather than from a
18
23
  # dozen hand-typed commands. It is safe to re-run: a status, a review mark or a
@@ -57,10 +62,19 @@ usage() {
57
62
  # than free markdown. Fenced blocks are skipped: a plan carries signatures and
58
63
  # commands in them, and a `- [ ]` inside a fence is an example, not a step.
59
64
  PARSER='
60
- BEGIN { SEP = sprintf("%c", 31) }
65
+ BEGIN {
66
+ SEP = sprintf("%c", 31)
67
+ # The one form a Builds line takes, said once for every message that asks for it.
68
+ BUILDS_HINT = "write D<n>, or none, <why>"
69
+ }
61
70
 
62
71
  function trim(s) { gsub(/^[ \t]+|[ \t]+$/, "", s); return s }
63
72
 
73
+ # Single-quoted for system(). The quote is built rather than written because
74
+ # this program is itself a single-quoted shell string, and an embedded quote
75
+ # becomes '"'"' because a backslash in a gsub replacement differs between awks.
76
+ function shq(s, q) { q = sprintf("%c", 39); gsub(q, q "\"" q "\"" q, s); return q s q }
77
+
64
78
  # Symbols on one side of a Contract line. The two sides are read differently
65
79
  # because a mistake costs differently on each.
66
80
  #
@@ -145,6 +159,17 @@ fenced { next }
145
159
 
146
160
  /^# Plan:/ { has_title = 1 }
147
161
 
162
+ # Before the first task only: a Spec line inside a task is that task naming a
163
+ # file, not the plan naming its design.
164
+ ntasks == 0 && /^\*\*Spec:\*\*/ {
165
+ spec_path = trim(substr($0, length("**Spec:**") + 1))
166
+ # Older plans write the path as code with prose after it, so a backticked
167
+ # span is the path and the rest is commentary.
168
+ if (match(spec_path, /`[^`]+`/)) spec_path = trim(substr(spec_path, RSTART + 1, RLENGTH - 2))
169
+ has_spec = 1
170
+ next
171
+ }
172
+
148
173
  /^## / {
149
174
  in_rules = (tolower($0) ~ /^## ground rules/) ? 1 : 0
150
175
  if (in_rules) has_rules_heading = 1
@@ -249,6 +274,27 @@ in_rules && /^- / { nrules++ }
249
274
  next
250
275
  }
251
276
 
277
+ /^\*\*Builds:\*\*/ {
278
+ has_builds[cur] = 1
279
+ body = trim(substr($0, length("**Builds:**") + 1))
280
+ # Validate-only, like the spec coverage below: a plan that imported before
281
+ # Builds lines existed must not be refused over one it cannot parse.
282
+ if (body == "") {
283
+ if (check_spec) finding("error", "task " cur " has an empty Builds line; " BUILDS_HINT)
284
+ next
285
+ }
286
+ if (tolower(body) ~ /^none([ \t,.;:]|$)/) next
287
+ n = split(body, parts, /[, \t]+/)
288
+ for (i = 1; i <= n; i++) {
289
+ # Authors write the line as prose, so "D2." closing a sentence is D2.
290
+ sub(/[.;:]+$/, "", parts[i])
291
+ if (parts[i] == "") continue
292
+ if (parts[i] ~ /^D[0-9]+$/) builds[cur] = builds[cur] " " parts[i]
293
+ else if (check_spec) finding("error", "task " cur " Builds " parts[i] ", which is not a decision id; " BUILDS_HINT)
294
+ }
295
+ next
296
+ }
297
+
252
298
  /^\*\*Flips:\*\*/ { has_flips[cur] = 1; nflips++; flips_list = flips_list (flips_list == "" ? "" : ", ") cur; next }
253
299
  /^\*\*Review:\*\*/ { has_review[cur] = 1; next }
254
300
  /^\*\*Model:\*\*/ { has_model[cur] = 1; next }
@@ -323,6 +369,62 @@ END {
323
369
  }
324
370
  }
325
371
 
372
+ # Gated, because only validate reads the spec. Import is run from wherever
373
+ # the run state lives, so a relative Spec path need not resolve there, and a
374
+ # plan already validated should not be refused over it.
375
+ if (check_spec) {
376
+ spec_file = spec_path
377
+ if (spec_path != "" && spec_path !~ /^\//) spec_file = ENVIRON["SLUICE_SPEC_BASE"] "/" spec_path
378
+ if (!has_spec) {
379
+ finding("error", "no **Spec:** line, so nothing ties the plan to the design it builds")
380
+ } else if (spec_path == "") {
381
+ # Checked before the read: getline on an empty filename is a fatal
382
+ # awk error, which would surface as the plan being unreadable.
383
+ finding("error", "the **Spec:** line names no path")
384
+ } else if (system("test -e " shq(spec_file)) != 0) {
385
+ finding("error", "**Spec:** names " spec_path ", which does not exist")
386
+ } else if (system("test -d " shq(spec_file)) == 0) {
387
+ # Asked before the read: BSD awk aborts on getline from a directory,
388
+ # which would surface as the plan being unreadable.
389
+ finding("error", "**Spec:** names " spec_path ", which is a directory, not a file")
390
+ } else if ((getline line < spec_file) < 0) {
391
+ finding("error", "**Spec:** names " spec_path ", which exists but cannot be read")
392
+ } else {
393
+ # The first line is already in hand from the existence probe above.
394
+ sfenced = 0; ndec = 0
395
+ do {
396
+ if (line ~ /^[ \t]*```/) { sfenced = 1 - sfenced; continue }
397
+ if (sfenced || line !~ /^### D[0-9]+\./) continue
398
+ match(line, /D[0-9]+/)
399
+ d = substr(line, RSTART, RLENGTH)
400
+ if (!(d in decision)) { decision[d] = 1; dec_order[++ndec] = d }
401
+ } while ((getline line < spec_file) > 0)
402
+ close(spec_file)
403
+ if (ndec == 0)
404
+ finding("error", "the spec " spec_path " has no \"### D<n>.\" decisions, so no task can say which one it builds")
405
+ }
406
+ for (i = 1; i <= ntasks; i++) {
407
+ id = order[i]
408
+ if (!(id in has_builds)) {
409
+ # An error, not a warning: `none, <why>` is how a task says it
410
+ # builds nothing, so a task without the line has left it unsaid.
411
+ finding("error", "task " id " has no Builds line; " BUILDS_HINT)
412
+ continue
413
+ }
414
+ n = split(builds[id], cited, " ")
415
+ for (j = 1; j <= n; j++) {
416
+ if (cited[j] == "") continue
417
+ built[cited[j]] = 1
418
+ if (ndec > 0 && !(cited[j] in decision))
419
+ finding("error", "task " id " Builds " cited[j] ", which the spec does not have")
420
+ }
421
+ }
422
+ if (ntasks > 0)
423
+ for (i = 1; i <= ndec; i++)
424
+ if (!(dec_order[i] in built))
425
+ finding("error", "spec decision " dec_order[i] " is built by no task")
426
+ }
427
+
326
428
  for (p in owners) {
327
429
  n = split(owners[p], who, " ")
328
430
  if (n < 2) continue
@@ -389,8 +491,17 @@ END {
389
491
  # FINDINGS (newline-separated sev\tmsg), TASKROWS (newline-separated), NERR,
390
492
  # NWARN.
391
493
  parse_plan() {
392
- local plan="$1" raw
393
- raw="$(awk "$PARSER" "$plan")" || { err "could not read $plan"; exit 4; }
494
+ local plan="$1" check_spec="${2-0}" raw
495
+ # A relative Spec path names a file from the plan's repo root, the way plans
496
+ # write it (docs/specs/...), so validate means the same thing from any cwd.
497
+ # Outside a repo there is no root, and the plan's own directory stands in.
498
+ # CDPATH is cleared because a cd it steers prints where it went, which would
499
+ # land in plan_dir. The base reaches awk through the environment: a -v value
500
+ # has its backslash escapes processed, and a path may hold a backslash.
501
+ local plan_dir spec_base
502
+ plan_dir="$(CDPATH= cd -- "$(dirname -- "$plan")" && pwd)" || { err "could not read $plan"; exit 4; }
503
+ spec_base="$(git -C "$plan_dir" rev-parse --show-toplevel 2>/dev/null)" || spec_base="$plan_dir"
504
+ raw="$(SLUICE_SPEC_BASE="$spec_base" awk -v check_spec="$check_spec" "$PARSER" "$plan")" || { err "could not read $plan"; exit 4; }
394
505
 
395
506
  local summary
396
507
  summary="$(printf '%s\n' "$raw" | grep "^summary$SEP" | head -1)"
@@ -421,7 +532,7 @@ case "$SUB" in
421
532
  shift
422
533
  [ $# -eq 0 ] || { err "unknown argument: $1"; exit 4; }
423
534
 
424
- parse_plan "$PLAN"
535
+ parse_plan "$PLAN" 1
425
536
 
426
537
  head="$(basename "$PLAN"): $(plural "$NTASKS" task)"
427
538
  [ "$FLIP" != "0" ] && head="$head, flip at task $FLIP"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sluice",
3
- "version": "0.27.0",
4
- "description": "Routes work by change shape into four channels (bypass, fast, main, deep) and applies only the rules each channel needs, so a one-line fix does not pay the cost of a multi-subsystem build. Carries nine rules as one-liners in the router and the full treatment in references read only on friction. Checks the finished plan with plan.sh validate rather than trusting it to memory, seeds the run state from it, keeps a deep run's task breakdown in .sluice/run.json so a statusline segment, one status command and a SessionStart hook can answer where the run is (the hook prints a live run at every session start, compaction included), lists every live deep run and routed fast or main session on the machine in one board.sh view (drawn once, watched, or as JSON), and closes each run with a ledger read out of the session transcript: elapsed, tools, tokens, and what each dispatched agent cost where the transcript recorded it. Two Stop hooks hold a handback: one refuses a tree that moved since its last recorded check, the other refuses a changed existing test not proven to fail on the session's starting code (or recorded with a reason). Claude Code only; stands down where the superpowers pipeline governs the repo.",
3
+ "version": "0.28.0",
4
+ "description": "Routes work by change shape into four channels (bypass, fast, main, deep) and applies only the rules each channel needs, so a one-line fix does not pay the cost of a multi-subsystem build. Carries nine rules as one-liners in the router and the full treatment in references read only on friction. Deep runs read the repo before designing, map the spec's intent to its decisions, and check spec against intent and plan against spec with a fresh reader, with plan.sh validate enforcing that every decision is covered. Checks the finished plan with plan.sh validate rather than trusting it to memory, seeds the run state from it, keeps a deep run's task breakdown in .sluice/run.json so a statusline segment, one status command and a SessionStart hook can answer where the run is (the hook prints a live run at every session start, compaction included), lists every live deep run and routed fast or main session on the machine in one board.sh view (drawn once, watched, or as JSON), and closes each run with a ledger read out of the session transcript: elapsed, tools, tokens, and what each dispatched agent cost where the transcript recorded it. Two Stop hooks hold a handback: one refuses a tree that moved since its last recorded check, the other refuses a changed existing test not proven to fail on the session's starting code (or recorded with a reason). Claude Code only; stands down where the superpowers pipeline governs the repo.",
5
5
  "author": "iceinvein",
6
6
  "type": "prompt",
7
7
  "tools": [