@iceinvein/agent-skills 0.18.2 → 0.19.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.
Files changed (71) hide show
  1. package/dist/cli/index.js +14 -6
  2. package/package.json +1 -1
  3. package/skills/index.json +1 -1
  4. package/skills/sluice/SKILL.md +6 -0
  5. package/skills/sluice/evals/README.md +79 -0
  6. package/skills/sluice/evals/bypass-question-stays-silent/graders/answers-the-question.md +9 -0
  7. package/skills/sluice/evals/bypass-question-stays-silent/graders/no-channel-announcement.md +7 -0
  8. package/skills/sluice/evals/bypass-question-stays-silent/graders/writes-nothing.md +5 -0
  9. package/skills/sluice/evals/bypass-question-stays-silent/prompt.md +10 -0
  10. package/skills/sluice/evals/deep-plan-across-subsystems/graders/announces-deep-channel.md +7 -0
  11. package/skills/sluice/evals/deep-plan-across-subsystems/graders/design-written-to-docs.md +5 -0
  12. package/skills/sluice/evals/deep-plan-across-subsystems/graders/no-implementation-yet.md +6 -0
  13. package/skills/sluice/evals/deep-plan-across-subsystems/graders/sluice-fired.md +5 -0
  14. package/skills/sluice/evals/deep-plan-across-subsystems/graders/stopped-for-signoff.md +8 -0
  15. package/skills/sluice/evals/deep-plan-across-subsystems/prompt.md +11 -0
  16. package/skills/sluice/evals/deep-run-blocks-on-a-real-decision/case.yaml +4 -0
  17. package/skills/sluice/evals/deep-run-blocks-on-a-real-decision/fixture.sh +332 -0
  18. package/skills/sluice/evals/deep-run-blocks-on-a-real-decision/graders/contract-not-rewritten.md +6 -0
  19. package/skills/sluice/evals/deep-run-blocks-on-a-real-decision/graders/ends-on-one-decision.md +15 -0
  20. package/skills/sluice/evals/deep-run-blocks-on-a-real-decision/graders/quiet-flag-parsed.md +5 -0
  21. package/skills/sluice/evals/deep-run-blocks-on-a-real-decision/graders/sluice-fired.md +5 -0
  22. package/skills/sluice/evals/deep-run-blocks-on-a-real-decision/graders/task-4-blocked.md +7 -0
  23. package/skills/sluice/evals/deep-run-blocks-on-a-real-decision/graders/three-tasks-landed.md +7 -0
  24. package/skills/sluice/evals/deep-run-blocks-on-a-real-decision/prompt.md +11 -0
  25. package/skills/sluice/evals/deep-run-finishes-every-task/case.yaml +4 -0
  26. package/skills/sluice/evals/deep-run-finishes-every-task/fixture.sh +304 -0
  27. package/skills/sluice/evals/deep-run-finishes-every-task/graders/did-not-check-in-between-tasks.md +13 -0
  28. package/skills/sluice/evals/deep-run-finishes-every-task/graders/every-task-done.md +7 -0
  29. package/skills/sluice/evals/deep-run-finishes-every-task/graders/no-task-left-todo.md +7 -0
  30. package/skills/sluice/evals/deep-run-finishes-every-task/graders/quiet-flag-landed.md +5 -0
  31. package/skills/sluice/evals/deep-run-finishes-every-task/graders/sluice-fired.md +5 -0
  32. package/skills/sluice/evals/deep-run-finishes-every-task/graders/suite-was-run.md +6 -0
  33. package/skills/sluice/evals/deep-run-finishes-every-task/prompt.md +11 -0
  34. package/skills/sluice/evals/explicit-instruction-collapses-to-fast/case.yaml +4 -0
  35. package/skills/sluice/evals/explicit-instruction-collapses-to-fast/fixture.sh +73 -0
  36. package/skills/sluice/evals/explicit-instruction-collapses-to-fast/graders/announces-fast-channel.md +7 -0
  37. package/skills/sluice/evals/explicit-instruction-collapses-to-fast/graders/collapsed-not-negotiated.md +10 -0
  38. package/skills/sluice/evals/explicit-instruction-collapses-to-fast/graders/no-design-or-plan-file.md +6 -0
  39. package/skills/sluice/evals/explicit-instruction-collapses-to-fast/graders/seam-implemented.md +9 -0
  40. package/skills/sluice/evals/explicit-instruction-collapses-to-fast/graders/sluice-fired.md +5 -0
  41. package/skills/sluice/evals/explicit-instruction-collapses-to-fast/prompt.md +11 -0
  42. package/skills/sluice/evals/fast-flag-on-existing-command/case.yaml +4 -0
  43. package/skills/sluice/evals/fast-flag-on-existing-command/fixture.sh +73 -0
  44. package/skills/sluice/evals/fast-flag-on-existing-command/graders/announces-fast-channel.md +7 -0
  45. package/skills/sluice/evals/fast-flag-on-existing-command/graders/quiet-flag-implemented.md +5 -0
  46. package/skills/sluice/evals/fast-flag-on-existing-command/graders/sluice-fired.md +5 -0
  47. package/skills/sluice/evals/fast-flag-on-existing-command/graders/stayed-in-fast.md +9 -0
  48. package/skills/sluice/evals/fast-flag-on-existing-command/graders/suite-was-run.md +6 -0
  49. package/skills/sluice/evals/fast-flag-on-existing-command/graders/test-edited-before-source.md +6 -0
  50. package/skills/sluice/evals/fast-flag-on-existing-command/prompt.md +11 -0
  51. package/skills/sluice/evals/main-new-interface/case.yaml +4 -0
  52. package/skills/sluice/evals/main-new-interface/fixture.sh +73 -0
  53. package/skills/sluice/evals/main-new-interface/graders/announces-main-channel.md +7 -0
  54. package/skills/sluice/evals/main-new-interface/graders/behaviour-preserved.md +9 -0
  55. package/skills/sluice/evals/main-new-interface/graders/shape-agreed-before-building.md +10 -0
  56. package/skills/sluice/evals/main-new-interface/graders/sluice-fired.md +5 -0
  57. package/skills/sluice/evals/main-new-interface/graders/suite-was-run.md +6 -0
  58. package/skills/sluice/evals/main-new-interface/prompt.md +11 -0
  59. package/skills/sluice/evals/results/2026-09-20T01-51-28-540Z/aggregate-result.json +105 -0
  60. package/skills/sluice/evals/results/2026-09-20T01-51-28-540Z/report.html +300 -0
  61. package/skills/sluice/evals/results/2026-09-20T01-52-06-287Z/aggregate-result.json +122 -0
  62. package/skills/sluice/evals/results/2026-09-20T01-52-06-287Z/report.html +324 -0
  63. package/skills/sluice/evals/superpowers-conflict-stands-down/case.yaml +4 -0
  64. package/skills/sluice/evals/superpowers-conflict-stands-down/fixture.sh +39 -0
  65. package/skills/sluice/evals/superpowers-conflict-stands-down/graders/names-no-channel.md +8 -0
  66. package/skills/sluice/evals/superpowers-conflict-stands-down/graders/stands-down-once.md +9 -0
  67. package/skills/sluice/evals/superpowers-conflict-stands-down/prompt.md +10 -0
  68. package/skills/sluice/scripts/session-start.sh +51 -0
  69. package/skills/sluice/scripts/stop-guard.sh +101 -0
  70. package/skills/sluice/scripts/tree-snapshot.sh +75 -0
  71. package/skills/sluice/skill.json +1 -1
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: deep-run-blocks-on-a-real-decision
3
+ description: The one reason a deep run may end a turn mid-plan. A task that collides with a public contract the plan never saw is blocked and handed back as a decision, not guessed at.
4
+ tags: [routing, deep, execution, blocked, scaffold, write]
5
+ max_turns: 80
6
+ timeout_seconds: 2400
7
+ allowed_tools: [Read, Glob, Grep, Skill, Write, Edit, Bash]
8
+ expected_outcome: Task 2 lands, Task 3 is marked blocked, and the turn ends on one question with options. The public-API test is left exactly as it was.
9
+ ---
10
+
11
+ Plan's signed off and pre-flight's answered, it's all in `.sluice/run.json`. Go.
@@ -0,0 +1,4 @@
1
+ schema_version: "1.1"
2
+ name: deep-run-finishes-every-task
3
+ context:
4
+ scaffold_script: fixture.sh
@@ -0,0 +1,304 @@
1
+ #!/usr/bin/env bash
2
+ # A deep run stopped exactly where the two stops are already behind it: design
3
+ # signed off, plan validated, pre-flight answered, Task 1 landed. What is left
4
+ # is execution, which is the part that must not stop.
5
+ set -euo pipefail
6
+
7
+ mkdir -p src/cli tests docs/plans .sluice
8
+
9
+ cat > package.json <<'JSON'
10
+ {
11
+ "name": "shipit",
12
+ "version": "1.2.0",
13
+ "type": "module",
14
+ "scripts": {
15
+ "test": "node --test \"tests/*.test.js\""
16
+ }
17
+ }
18
+ JSON
19
+
20
+ # Task 1, already landed: the sink exists and nothing routes through it yet.
21
+ cat > src/cli/progress.js <<'JS'
22
+ export function createSink({ quiet }) {
23
+ return {
24
+ write(line) {
25
+ if (!quiet) {
26
+ console.log(line);
27
+ }
28
+ },
29
+ };
30
+ }
31
+ JS
32
+
33
+ cat > tests/progress.test.js <<'JS'
34
+ import assert from "node:assert/strict";
35
+ import { test } from "node:test";
36
+ import { createSink } from "../src/cli/progress.js";
37
+
38
+ test("a loud sink prints the line", () => {
39
+ const lines = [];
40
+ const sink = createSink({ quiet: false });
41
+ const restore = console.log;
42
+ console.log = (line) => lines.push(line);
43
+ try {
44
+ sink.write("-> build");
45
+ } finally {
46
+ console.log = restore;
47
+ }
48
+ assert.deepEqual(lines, ["-> build"]);
49
+ });
50
+
51
+ test("a quiet sink swallows the line", () => {
52
+ const lines = [];
53
+ const sink = createSink({ quiet: true });
54
+ const restore = console.log;
55
+ console.log = (line) => lines.push(line);
56
+ try {
57
+ sink.write("-> build");
58
+ } finally {
59
+ console.log = restore;
60
+ }
61
+ assert.deepEqual(lines, []);
62
+ });
63
+ JS
64
+
65
+ cat > src/cli/args.js <<'JS'
66
+ export function parseArgs(argv) {
67
+ return { dryRun: argv.includes("--dry-run") };
68
+ }
69
+ JS
70
+
71
+ cat > tests/args.test.js <<'JS'
72
+ import assert from "node:assert/strict";
73
+ import { test } from "node:test";
74
+ import { parseArgs } from "../src/cli/args.js";
75
+
76
+ test("dry run is read off the argv", () => {
77
+ assert.deepEqual(parseArgs(["--dry-run"]), { dryRun: true });
78
+ });
79
+ JS
80
+
81
+ cat > src/cli/deploy.js <<'JS'
82
+ const STEPS = ["build", "upload", "activate"];
83
+
84
+ export function deploy(args) {
85
+ const { dryRun } = args;
86
+
87
+ for (const step of STEPS) {
88
+ console.log(`-> ${step}`);
89
+ }
90
+
91
+ console.log(dryRun ? "dry run: 3 steps skipped" : "deployed: 3 steps");
92
+ }
93
+ JS
94
+
95
+ cat > tests/deploy.test.js <<'JS'
96
+ import assert from "node:assert/strict";
97
+ import { test } from "node:test";
98
+ import { deploy } from "../src/cli/deploy.js";
99
+
100
+ function capture(run) {
101
+ const lines = [];
102
+ const restore = console.log;
103
+ console.log = (line) => lines.push(line);
104
+ try {
105
+ run();
106
+ } finally {
107
+ console.log = restore;
108
+ }
109
+ return lines;
110
+ }
111
+
112
+ test("dry run skips the steps and says so", () => {
113
+ const lines = capture(() => deploy({ dryRun: true }));
114
+ assert.equal(lines.at(-1), "dry run: 3 steps skipped");
115
+ });
116
+
117
+ test("a full deploy reports every step", () => {
118
+ const lines = capture(() => deploy({ dryRun: false }));
119
+ assert.deepEqual(lines, ["-> build", "-> upload", "-> activate", "deployed: 3 steps"]);
120
+ });
121
+ JS
122
+
123
+ cat > docs/plans/2026-09-20-quiet-flag.md <<'MD'
124
+ # Plan: quiet-flag
125
+
126
+ ## Goal
127
+
128
+ `deploy --quiet` suppresses the three per-step progress lines and leaves the
129
+ final summary line untouched. Every other deploy output is unchanged.
130
+
131
+ ## Architecture
132
+
133
+ Progress lines go through a sink instead of `console.log`. The sink is built
134
+ from the parsed flags and handed to `deploy`, so `deploy` never asks whether it
135
+ is quiet. The summary line does not go through the sink.
136
+
137
+ ## Ground Rules
138
+
139
+ - Commit message convention: `<type>(<scope>): <subject>`, subject lower case,
140
+ no trailing full stop, no attribution trailers and no tool footers.
141
+ - Test runner: `npm test`, which runs `node --test "tests/*.test.js"`.
142
+ - Node built-ins only. No dependency may be added to package.json.
143
+ - Every test asserts on captured output lines, never on internal state.
144
+
145
+ ### Task 1: progress sink
146
+
147
+ **Contract:** Needs: none | Offers: `createSink({ quiet: boolean }) -> { write(line: string): void }`
148
+ **Touches:** src/cli/progress.js (new) | tests/progress.test.js (new)
149
+
150
+ - [x] Add a test asserting a sink built with `quiet: false` prints the line it is given -> the test fails because src/cli/progress.js does not exist
151
+ - [x] Add a test asserting a sink built with `quiet: true` prints nothing -> it fails for the same reason
152
+ - [x] Write `createSink` returning an object with a `write` method that prints unless `quiet` -> `npm test` green
153
+
154
+ ### Task 2: quiet flag parsing
155
+
156
+ **Contract:** Needs: none | Offers: `parseArgs(argv: string[]) -> { dryRun: boolean, quiet: boolean }`
157
+ **Touches:** src/cli/args.js (edit) | tests/args.test.js (test)
158
+
159
+ - [ ] Add a test asserting `parseArgs(["--quiet"])` returns `{ dryRun: false, quiet: true }` -> the new test fails, the existing dry-run test still passes
160
+ - [ ] Add a test asserting `parseArgs([])` returns `{ dryRun: false, quiet: false }` -> it fails on the missing key
161
+ - [ ] Read `--quiet` off argv alongside `--dry-run` -> `npm test` green
162
+
163
+ ### Task 3: route progress through the sink
164
+
165
+ **Contract:** Needs: `createSink({ quiet: boolean }) -> { write(line: string): void }` | Offers: `deploy(args: { dryRun: boolean }, sink: { write(line: string): void }) -> void`
166
+ **Touches:** src/cli/deploy.js (edit) | tests/deploy.test.js (test)
167
+
168
+ - [ ] Add a test passing a recording sink to `deploy` and asserting the three `-> <step>` lines arrive on the sink -> the new test fails because deploy takes no sink
169
+ - [ ] Give `deploy` a second parameter `sink` and send each `-> <step>` line to `sink.write` -> the new test passes
170
+ - [ ] Keep the summary line on `console.log` and keep both original deploy tests asserting the exact lines `dry run: 3 steps skipped` and `deployed: 3 steps` -> `npm test` green
171
+
172
+ ### Task 4: turn quiet on
173
+
174
+ **Contract:** Needs: `parseArgs(argv: string[]) -> { dryRun: boolean, quiet: boolean }`, `createSink({ quiet: boolean }) -> { write(line: string): void }`, `deploy(args: { dryRun: boolean }, sink: { write(line: string): void }) -> void` | Offers: `main(argv: string[]) -> void`
175
+ **Touches:** src/cli/main.js (new) | tests/main.test.js (test)
176
+ **Flips:** the three progress lines become suppressible; before this task `--quiet` parses and changes nothing
177
+
178
+ - [ ] Add a test asserting `main(["--quiet"])` prints only `deployed: 3 steps` -> it fails because src/cli/main.js does not exist
179
+ - [ ] Add a test asserting `main([])` prints the three step lines and then `deployed: 3 steps` -> it fails for the same reason
180
+ - [ ] Write `main` to call `parseArgs`, build the sink from `quiet`, and call `deploy` with both -> `npm test` green
181
+ MD
182
+
183
+ cat > docs/plans/2026-09-20-quiet-flag-record.md <<'MD'
184
+ # Run record: quiet-flag
185
+
186
+ ## Pre-flight
187
+
188
+ - Review: tier 3 only. Task 4 is the flip and is the one task that gets a reviewer.
189
+ - Model: default for every task. None of the three remaining tasks was marked mechanical.
190
+ - Workspace: shared tree. Tasks run serially, so no worktree was cut.
191
+
192
+ ## Log
193
+
194
+ - Task 1, progress sink: landed. `createSink` written with both tests green.
195
+ Inert by design, nothing routes through it yet.
196
+ MD
197
+
198
+ cat > .sluice/run.json <<'JSON'
199
+ {
200
+ "schema": 1,
201
+ "topic": "quiet-flag",
202
+ "channel": "deep",
203
+ "started": "2026-09-20T09:05:00Z",
204
+ "plan": "docs/plans/2026-09-20-quiet-flag.md",
205
+ "record": "docs/plans/2026-09-20-quiet-flag-record.md",
206
+ "tasks": [
207
+ {
208
+ "id": 1,
209
+ "status": "done",
210
+ "name": "progress sink",
211
+ "tier": 2,
212
+ "touches": [
213
+ "src/cli/progress.js",
214
+ "tests/progress.test.js"
215
+ ],
216
+ "offers": [
217
+ "boolean",
218
+ "createSink",
219
+ "line",
220
+ "quiet",
221
+ "string",
222
+ "void",
223
+ "write"
224
+ ]
225
+ },
226
+ {
227
+ "id": 2,
228
+ "status": "todo",
229
+ "name": "quiet flag parsing",
230
+ "tier": 1,
231
+ "touches": [
232
+ "src/cli/args.js",
233
+ "tests/args.test.js"
234
+ ],
235
+ "offers": [
236
+ "argv",
237
+ "boolean",
238
+ "dryRun",
239
+ "parseArgs",
240
+ "quiet",
241
+ "string"
242
+ ]
243
+ },
244
+ {
245
+ "id": 3,
246
+ "status": "todo",
247
+ "name": "route progress through the sink",
248
+ "tier": 1,
249
+ "touches": [
250
+ "src/cli/deploy.js",
251
+ "tests/deploy.test.js"
252
+ ],
253
+ "needs": [
254
+ "createSink",
255
+ "write"
256
+ ],
257
+ "offers": [
258
+ "args",
259
+ "boolean",
260
+ "deploy",
261
+ "dryRun",
262
+ "line",
263
+ "sink",
264
+ "string",
265
+ "void",
266
+ "write"
267
+ ]
268
+ },
269
+ {
270
+ "id": 4,
271
+ "status": "todo",
272
+ "name": "turn quiet on",
273
+ "tier": 3,
274
+ "flips": true,
275
+ "touches": [
276
+ "src/cli/main.js",
277
+ "tests/main.test.js"
278
+ ],
279
+ "needs": [
280
+ "createSink",
281
+ "deploy",
282
+ "parseArgs",
283
+ "write"
284
+ ],
285
+ "offers": [
286
+ "argv",
287
+ "main",
288
+ "string",
289
+ "void"
290
+ ]
291
+ }
292
+ ],
293
+ "updated": "2026-09-20T09:05:00Z",
294
+ "preflight": {
295
+ "review": "tier3",
296
+ "model": "default",
297
+ "workspace": "shared"
298
+ }
299
+ }
300
+ JSON
301
+
302
+ git init --quiet
303
+ git add -A
304
+ git -c user.email=fixture@example.com -c user.name=fixture commit --quiet -m "feat(progress): add the quiet-aware progress sink"
@@ -0,0 +1,13 @@
1
+ ---
2
+ type: llm
3
+ weight: 3
4
+ ---
5
+
6
+ The agent was handed a signed-off plan with three tasks left and told to go.
7
+
8
+ PASS if the final message is a handback for finished work: it reports the
9
+ remaining tasks as complete and the suite as green.
10
+ FAIL if the final message stops partway and asks whether to carry on to the
11
+ next task, reports one task done and waits, asks for approval to proceed, or
12
+ otherwise hands back with tasks still outstanding for any reason other than a
13
+ decision only the user can make.
@@ -0,0 +1,7 @@
1
+ ---
2
+ type: regex
3
+ pattern: '"status":\s*"done"'
4
+ match: 'count:4'
5
+ target: { source: file, path: .sluice/run.json }
6
+ weight: 2
7
+ ---
@@ -0,0 +1,7 @@
1
+ ---
2
+ type: regex
3
+ pattern: '"status":\s*"(todo|active|blocked)"'
4
+ match: not_contains
5
+ target: { source: file, path: .sluice/run.json }
6
+ weight: 3
7
+ ---
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: regex
3
+ pattern: '--quiet'
4
+ target: { source: file, path: src/cli/args.js }
5
+ ---
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: tool_used
3
+ tool: Skill
4
+ input_match: '"skill"\s*:\s*"(?:[\w-]+:)?sluice"'
5
+ ---
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: tool_used
3
+ tool: Bash
4
+ input_match: '(npm test|node --test)'
5
+ min: 3
6
+ ---
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: deep-run-finishes-every-task
3
+ description: A deep run past pre-flight with three tasks left must carry them all to done in one turn, not check in after each.
4
+ tags: [routing, deep, execution, scaffold, write]
5
+ max_turns: 80
6
+ timeout_seconds: 2400
7
+ allowed_tools: [Read, Glob, Grep, Skill, Write, Edit, Bash]
8
+ expected_outcome: Tasks 2, 3 and 4 all reach done, the suite is green, and the turn ends once at the handback rather than after each task.
9
+ ---
10
+
11
+ Plan's signed off and pre-flight's answered, it's all in `.sluice/run.json`. Go.
@@ -0,0 +1,4 @@
1
+ schema_version: "1.1"
2
+ name: explicit-instruction-collapses-to-fast
3
+ context:
4
+ scaffold_script: fixture.sh
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env bash
2
+ # Smallest repo that makes --quiet a real one-subsystem change: an existing
3
+ # command with an existing flag, an existing test, and a runnable suite.
4
+ set -euo pipefail
5
+
6
+ mkdir -p src/cli tests
7
+
8
+ cat > package.json <<'JSON'
9
+ {
10
+ "name": "shipit",
11
+ "version": "1.2.0",
12
+ "type": "module",
13
+ "scripts": {
14
+ "test": "node --test \"tests/*.test.js\""
15
+ }
16
+ }
17
+ JSON
18
+
19
+ cat > src/cli/deploy.js <<'JS'
20
+ const STEPS = ["build", "upload", "activate"];
21
+
22
+ export function deploy(args, log = console.log) {
23
+ const dryRun = args.includes("--dry-run");
24
+
25
+ for (const step of STEPS) {
26
+ log(`-> ${step}`);
27
+ if (!dryRun) {
28
+ run(step);
29
+ }
30
+ }
31
+
32
+ log(dryRun ? "dry run: 3 steps skipped" : "deployed: 3 steps");
33
+ }
34
+
35
+ function run(step) {
36
+ if (!STEPS.includes(step)) {
37
+ throw new Error(`unknown step: ${step}`);
38
+ }
39
+ }
40
+ JS
41
+
42
+ cat > tests/deploy.test.js <<'JS'
43
+ import assert from "node:assert/strict";
44
+ import { test } from "node:test";
45
+ import { deploy } from "../src/cli/deploy.js";
46
+
47
+ function capture() {
48
+ const lines = [];
49
+ return { lines, log: (line) => lines.push(line) };
50
+ }
51
+
52
+ test("dry run skips the steps and says so", () => {
53
+ const { lines, log } = capture();
54
+ deploy(["--dry-run"], log);
55
+ assert.equal(lines.at(-1), "dry run: 3 steps skipped");
56
+ });
57
+
58
+ test("a full deploy reports every step", () => {
59
+ const { lines, log } = capture();
60
+ deploy([], log);
61
+ assert.deepEqual(lines, ["-> build", "-> upload", "-> activate", "deployed: 3 steps"]);
62
+ });
63
+ JS
64
+
65
+ cat > README.md <<'MD'
66
+ # shipit
67
+
68
+ Deploy CLI. `npm test` runs the suite.
69
+ MD
70
+
71
+ git init --quiet
72
+ git add -A
73
+ git -c user.email=fixture@example.com -c user.name=fixture commit --quiet -m "shipit 1.2.0"
@@ -0,0 +1,7 @@
1
+ ---
2
+ type: regex
3
+ pattern: 'fast channel'
4
+ flags: i
5
+ target: trace
6
+ weight: 2
7
+ ---
@@ -0,0 +1,10 @@
1
+ ---
2
+ type: llm
3
+ focus: trace
4
+ weight: 2
5
+ ---
6
+
7
+ The request adds a new seam, which would normally route to the main channel, but the user said "don't propose anything, don't write it up, just do it".
8
+
9
+ PASS if the agent honoured that: it named the fast channel and went straight to the work.
10
+ FAIL if it named the main or deep channel, laid out approaches for the user to pick from, or ended its turn waiting for the shape to be agreed.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: file_exists
3
+ path: 'docs/**'
4
+ exists: false
5
+ weight: 2
6
+ ---
@@ -0,0 +1,9 @@
1
+ ---
2
+ type: llm
3
+ focus: { source: file, path: src/cli/deploy.js }
4
+ ---
5
+
6
+ This is the deploy module after the change.
7
+
8
+ PASS if deploy takes its target from outside itself (an argument, an injected object, or a registry lookup) and runs the three steps through it, so a second target could be supplied without editing this function's body.
9
+ FAIL if the steps still run against a hardcoded local path with no seam, or if the target is chosen by a conditional inside deploy that a new target would have to be added to.
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: tool_used
3
+ tool: Skill
4
+ input_match: '"skill"\s*:\s*"(?:[\w-]+:)?sluice"'
5
+ ---
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: explicit-instruction-collapses-to-fast
3
+ description: The same new-interface work as the main case, with an explicit instruction to skip the ceremony. Explicit instruction wins, so it collapses to fast.
4
+ tags: [routing, fast, override, scaffold, write]
5
+ max_turns: 30
6
+ timeout_seconds: 900
7
+ allowed_tools: [Read, Glob, Grep, Skill, Write, Edit, Bash]
8
+ expected_outcome: Announces the fast channel rather than main, and implements the target seam without proposing shapes or writing a design.
9
+ ---
10
+
11
+ Deploy only knows how to push to our own boxes. Put the three steps behind a pluggable target so S3 can slot in later. Don't propose anything, don't write it up, just do it.
@@ -0,0 +1,4 @@
1
+ schema_version: "1.1"
2
+ name: fast-flag-on-existing-command
3
+ context:
4
+ scaffold_script: fixture.sh
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env bash
2
+ # Smallest repo that makes --quiet a real one-subsystem change: an existing
3
+ # command with an existing flag, an existing test, and a runnable suite.
4
+ set -euo pipefail
5
+
6
+ mkdir -p src/cli tests
7
+
8
+ cat > package.json <<'JSON'
9
+ {
10
+ "name": "shipit",
11
+ "version": "1.2.0",
12
+ "type": "module",
13
+ "scripts": {
14
+ "test": "node --test \"tests/*.test.js\""
15
+ }
16
+ }
17
+ JSON
18
+
19
+ cat > src/cli/deploy.js <<'JS'
20
+ const STEPS = ["build", "upload", "activate"];
21
+
22
+ export function deploy(args, log = console.log) {
23
+ const dryRun = args.includes("--dry-run");
24
+
25
+ for (const step of STEPS) {
26
+ log(`-> ${step}`);
27
+ if (!dryRun) {
28
+ run(step);
29
+ }
30
+ }
31
+
32
+ log(dryRun ? "dry run: 3 steps skipped" : "deployed: 3 steps");
33
+ }
34
+
35
+ function run(step) {
36
+ if (!STEPS.includes(step)) {
37
+ throw new Error(`unknown step: ${step}`);
38
+ }
39
+ }
40
+ JS
41
+
42
+ cat > tests/deploy.test.js <<'JS'
43
+ import assert from "node:assert/strict";
44
+ import { test } from "node:test";
45
+ import { deploy } from "../src/cli/deploy.js";
46
+
47
+ function capture() {
48
+ const lines = [];
49
+ return { lines, log: (line) => lines.push(line) };
50
+ }
51
+
52
+ test("dry run skips the steps and says so", () => {
53
+ const { lines, log } = capture();
54
+ deploy(["--dry-run"], log);
55
+ assert.equal(lines.at(-1), "dry run: 3 steps skipped");
56
+ });
57
+
58
+ test("a full deploy reports every step", () => {
59
+ const { lines, log } = capture();
60
+ deploy([], log);
61
+ assert.deepEqual(lines, ["-> build", "-> upload", "-> activate", "deployed: 3 steps"]);
62
+ });
63
+ JS
64
+
65
+ cat > README.md <<'MD'
66
+ # shipit
67
+
68
+ Deploy CLI. `npm test` runs the suite.
69
+ MD
70
+
71
+ git init --quiet
72
+ git add -A
73
+ git -c user.email=fixture@example.com -c user.name=fixture commit --quiet -m "shipit 1.2.0"
@@ -0,0 +1,7 @@
1
+ ---
2
+ type: regex
3
+ pattern: 'fast channel'
4
+ flags: i
5
+ target: trace
6
+ weight: 2
7
+ ---
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: regex
3
+ pattern: '--quiet'
4
+ target: { source: file, path: src/cli/deploy.js }
5
+ ---
@@ -0,0 +1,5 @@
1
+ ---
2
+ type: tool_used
3
+ tool: Skill
4
+ input_match: '"skill"\s*:\s*"(?:[\w-]+:)?sluice"'
5
+ ---
@@ -0,0 +1,9 @@
1
+ ---
2
+ type: llm
3
+ focus: trace
4
+ ---
5
+
6
+ The agent was asked to add a `--quiet` flag to an existing deploy command.
7
+
8
+ PASS if the agent named the fast channel and stayed there: it did not write a design document, did not write a plan file, and did not stop to ask which approach to take before starting.
9
+ FAIL if it announced the main or deep channel, escalated to either of them, wrote a file under docs/specs or docs/plans, or ended its turn waiting for sign-off before implementing.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: tool_used
3
+ tool: Bash
4
+ input_match: '(npm test|node --test)'
5
+ min: 2
6
+ ---
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: tool_order
3
+ before: { tool: Edit, input_match: 'tests/deploy\.test\.js' }
4
+ after: { tool: Edit, input_match: 'src/cli/deploy\.js' }
5
+ weight: 2
6
+ ---
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: fast-flag-on-existing-command
3
+ description: A new flag on an existing command is the fast channel's own example. Pins the announcement and test-first.
4
+ tags: [routing, fast, scaffold, write]
5
+ max_turns: 30
6
+ timeout_seconds: 900
7
+ allowed_tools: [Read, Glob, Grep, Skill, Write, Edit, Bash]
8
+ expected_outcome: Announces the fast channel, edits tests/deploy.test.js and runs the suite before touching src/cli/deploy.js, then ships --quiet with the suite green.
9
+ ---
10
+
11
+ Add a `--quiet` flag to the deploy command. It suppresses the per-step progress lines and leaves the final summary line alone. Everything else about the command stays as it is.
@@ -0,0 +1,4 @@
1
+ schema_version: "1.1"
2
+ name: main-new-interface
3
+ context:
4
+ scaffold_script: fixture.sh