@iceinvein/agent-skills 0.23.7 → 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.
- package/package.json +1 -1
- package/skills/index.json +3 -3
- package/skills/magpie/README.md +1 -1
- package/skills/magpie/SKILL.md +2 -2
- package/skills/magpie/evals/README.md +22 -8
- package/skills/magpie/evals/bare-invocation-goes-local/case.yaml +4 -0
- package/skills/magpie/evals/bare-invocation-goes-local/fixture.sh +159 -0
- package/skills/magpie/evals/bare-invocation-goes-local/graders/branch-was-reachable.md +5 -0
- package/skills/magpie/evals/bare-invocation-goes-local/graders/never-went-to-a-pr.md +7 -0
- package/skills/magpie/evals/bare-invocation-goes-local/graders/setup-ran-local.md +6 -0
- package/skills/magpie/evals/bare-invocation-goes-local/prompt.md +11 -0
- package/skills/magpie/evals/baselines/claude-opus-5-5.json +27 -3
- package/skills/magpie/evals/rules.json +8 -0
- package/skills/magpie/scripts/__tests__/skill-lint.test.ts +8 -0
- package/skills/magpie/skill.json +1 -1
- package/skills/sluice/SKILL.md +21 -0
- package/skills/sluice/evals/README.md +6 -2
- package/skills/sluice/evals/deep-plan-checks-the-intent/case.yaml +4 -0
- package/skills/sluice/evals/deep-plan-checks-the-intent/fixture.sh +124 -0
- package/skills/sluice/evals/deep-plan-checks-the-intent/graders/announces-deep-channel.md +12 -0
- package/skills/sluice/evals/deep-plan-checks-the-intent/graders/checker-dispatched.md +12 -0
- package/skills/sluice/evals/deep-plan-checks-the-intent/graders/no-source-edit.md +11 -0
- package/skills/sluice/evals/deep-plan-checks-the-intent/graders/no-source-write.md +11 -0
- package/skills/sluice/evals/deep-plan-checks-the-intent/graders/signoff-reports-the-check.md +16 -0
- package/skills/sluice/evals/deep-plan-checks-the-intent/graders/sluice-fired.md +5 -0
- package/skills/sluice/evals/deep-plan-checks-the-intent/graders/spec-has-an-intent-map.md +19 -0
- package/skills/sluice/evals/deep-plan-checks-the-intent/graders/stopped-for-signoff.md +8 -0
- package/skills/sluice/evals/deep-plan-checks-the-intent/prompt.md +20 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/case.yaml +4 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/fixture.sh +178 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/graders/announces-deep-channel.md +12 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/graders/every-decision-built.md +15 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/graders/no-source-edit.md +11 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/graders/no-source-write.md +11 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/graders/plan-checker-dispatched.md +11 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/graders/plan-cites-the-spec.md +7 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/graders/sluice-fired.md +5 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/graders/stops-at-preflight.md +14 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/graders/validate-ran.md +9 -0
- package/skills/sluice/evals/deep-plan-checks-the-plan/prompt.md +12 -0
- package/skills/sluice/evals/deep-plan-reads-the-prior-spec/case.yaml +4 -0
- package/skills/sluice/evals/deep-plan-reads-the-prior-spec/fixture.sh +125 -0
- package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/announces-deep-channel.md +12 -0
- package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/context-cites-the-prior-spec.md +6 -0
- package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/design-follows-the-prior-spec.md +21 -0
- package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/design-written-to-docs.md +5 -0
- package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/no-source-edit.md +11 -0
- package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/no-source-write.md +11 -0
- package/skills/sluice/evals/deep-plan-reads-the-prior-spec/graders/sluice-fired.md +5 -0
- package/skills/sluice/evals/deep-plan-reads-the-prior-spec/prompt.md +17 -0
- package/skills/sluice/evals/deep-plan-traces-the-example/case.yaml +4 -0
- package/skills/sluice/evals/deep-plan-traces-the-example/fixture.sh +151 -0
- package/skills/sluice/evals/deep-plan-traces-the-example/graders/announces-deep-channel.md +12 -0
- package/skills/sluice/evals/deep-plan-traces-the-example/graders/design-written-to-docs.md +5 -0
- package/skills/sluice/evals/deep-plan-traces-the-example/graders/no-source-edit.md +11 -0
- package/skills/sluice/evals/deep-plan-traces-the-example/graders/no-source-write.md +11 -0
- package/skills/sluice/evals/deep-plan-traces-the-example/graders/sluice-fired.md +5 -0
- package/skills/sluice/evals/deep-plan-traces-the-example/graders/spec-has-a-trace.md +7 -0
- package/skills/sluice/evals/deep-plan-traces-the-example/graders/stopped-for-signoff.md +8 -0
- package/skills/sluice/evals/deep-plan-traces-the-example/graders/trace-finds-no-accepted-slide.md +27 -0
- package/skills/sluice/evals/deep-plan-traces-the-example/prompt.md +17 -0
- package/skills/sluice/evals/rules.json +36 -2
- package/skills/sluice/references/deep-channel.md +180 -5
- package/skills/sluice/scripts/plan.sh +115 -4
- package/skills/sluice/skill.json +2 -2
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# A deck app where the ticket's own example fails the obvious design. The deck
|
|
3
|
+
# store looks slides up by the title of their accepted version, and only the
|
|
4
|
+
# canvas ever accepts one, so the smoke deck, built in chat, has no accepted
|
|
5
|
+
# slides and the lookup the ticket says to reuse finds nothing. Nothing in the
|
|
6
|
+
# code is wrong; the trap is in designing from which functions exist instead of
|
|
7
|
+
# walking the example through the data. The README fixes where the design goes, so the graders can read it,
|
|
8
|
+
# what an unaccepted slide is called, and what writes slide text, because
|
|
9
|
+
# without them each is a fork the run rightly asks before writing any design.
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
mkdir -p src/chat src/deck src/canvas data/decks tests docs/specs
|
|
13
|
+
|
|
14
|
+
cat > package.json <<'JSON'
|
|
15
|
+
{
|
|
16
|
+
"name": "decks",
|
|
17
|
+
"version": "0.9.0",
|
|
18
|
+
"type": "module",
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "node --test \"tests/*.test.js\""
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
JSON
|
|
24
|
+
|
|
25
|
+
cat > src/deck/store.js <<'JS'
|
|
26
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
27
|
+
|
|
28
|
+
const path = (id) => new URL(`../../data/decks/${id}.json`, import.meta.url);
|
|
29
|
+
|
|
30
|
+
export function loadDeck(id) {
|
|
31
|
+
return JSON.parse(readFileSync(path(id), "utf8"));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function saveDeck(deck) {
|
|
35
|
+
writeFileSync(path(deck.id), JSON.stringify(deck, null, 2) + "\n");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// A slide's title is the title of the version the user accepted on the canvas.
|
|
39
|
+
export function findSlideByTitle(deck, title) {
|
|
40
|
+
const wanted = title.trim().toLowerCase();
|
|
41
|
+
return deck.slides.find((slide) => {
|
|
42
|
+
const accepted = slide.versions[slide.accepted];
|
|
43
|
+
return accepted !== undefined && accepted.title.toLowerCase() === wanted;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
JS
|
|
47
|
+
|
|
48
|
+
cat > src/deck/redraft.js <<'JS'
|
|
49
|
+
// Appends a new version; the canvas decides whether the user accepts it.
|
|
50
|
+
export function redraftSlide(slide, draft) {
|
|
51
|
+
slide.versions.push({ title: draft.title, body: draft.body });
|
|
52
|
+
return slide.versions.length - 1;
|
|
53
|
+
}
|
|
54
|
+
JS
|
|
55
|
+
|
|
56
|
+
cat > src/deck/draft.js <<'JS'
|
|
57
|
+
// Slide text comes from the model; callers pass the completion so tests can stub it.
|
|
58
|
+
export async function draftSlide(version, instruction, complete) {
|
|
59
|
+
const text = await complete(
|
|
60
|
+
`Rewrite this slide. ${instruction}\nTitle: ${version.title}\nBody: ${version.body}`,
|
|
61
|
+
);
|
|
62
|
+
const [title, ...body] = text.trim().split("\n");
|
|
63
|
+
return { title: title.replace(/^Title:\s*/, ""), body: body.join("\n").replace(/^Body:\s*/, "") };
|
|
64
|
+
}
|
|
65
|
+
JS
|
|
66
|
+
|
|
67
|
+
cat > src/canvas/accept.js <<'JS'
|
|
68
|
+
export function accept(slide, versionIndex) {
|
|
69
|
+
if (slide.versions[versionIndex] === undefined) {
|
|
70
|
+
throw new Error(`no version ${versionIndex} on slide ${slide.id}`);
|
|
71
|
+
}
|
|
72
|
+
slide.accepted = versionIndex;
|
|
73
|
+
}
|
|
74
|
+
JS
|
|
75
|
+
|
|
76
|
+
cat > src/chat/build-deck.js <<'JS'
|
|
77
|
+
export function buildDeck(id, outline) {
|
|
78
|
+
return {
|
|
79
|
+
id,
|
|
80
|
+
slides: outline.map((item, index) => ({
|
|
81
|
+
id: `s${index + 1}`,
|
|
82
|
+
accepted: null,
|
|
83
|
+
versions: [{ title: item.title, body: item.body }],
|
|
84
|
+
})),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
JS
|
|
88
|
+
|
|
89
|
+
cat > src/chat/router.js <<'JS'
|
|
90
|
+
export function route(message) {
|
|
91
|
+
if (/^(prepare|build|make)\b.*\bslides?\b/i.test(message)) {
|
|
92
|
+
return { intent: "build-deck" };
|
|
93
|
+
}
|
|
94
|
+
return { intent: "chat" };
|
|
95
|
+
}
|
|
96
|
+
JS
|
|
97
|
+
|
|
98
|
+
cat > data/decks/smoke.json <<'JSON'
|
|
99
|
+
{
|
|
100
|
+
"id": "smoke",
|
|
101
|
+
"slides": [
|
|
102
|
+
{
|
|
103
|
+
"id": "s1",
|
|
104
|
+
"accepted": null,
|
|
105
|
+
"versions": [{ "title": "Why a pilot", "body": "Three sites asked for it." }]
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"id": "s2",
|
|
109
|
+
"accepted": null,
|
|
110
|
+
"versions": [{ "title": "Pilot Stages", "body": "Recruit, run for six weeks, review." }]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"id": "s3",
|
|
114
|
+
"accepted": null,
|
|
115
|
+
"versions": [{ "title": "Next steps", "body": "Pick the sites by May." }]
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
JSON
|
|
120
|
+
|
|
121
|
+
cat > tests/store.test.js <<'JS'
|
|
122
|
+
import assert from "node:assert/strict";
|
|
123
|
+
import { test } from "node:test";
|
|
124
|
+
import { findSlideByTitle } from "../src/deck/store.js";
|
|
125
|
+
|
|
126
|
+
test("a slide is found by the title of its accepted version", () => {
|
|
127
|
+
const deck = { slides: [{ id: "s1", accepted: 0, versions: [{ title: "Pilot Stages" }] }] };
|
|
128
|
+
assert.equal(findSlideByTitle(deck, "pilot stages").id, "s1");
|
|
129
|
+
});
|
|
130
|
+
JS
|
|
131
|
+
|
|
132
|
+
cat > README.md <<'MD'
|
|
133
|
+
# decks
|
|
134
|
+
|
|
135
|
+
Chat builds a deck (`src/chat/build-deck.js`), the canvas lets a user accept a
|
|
136
|
+
slide version (`src/canvas/accept.js`), and the deck store reads and writes
|
|
137
|
+
`data/decks/<id>.json`. Until a user accepts a version, a slide shows, and is
|
|
138
|
+
called by, its latest one. Slide text, for a new deck or a redraft, comes from
|
|
139
|
+
`draftSlide` in `src/deck/draft.js`. `data/decks/smoke.json` is the smoke deck QA runs
|
|
140
|
+
tickets against. `npm test` runs the suite.
|
|
141
|
+
|
|
142
|
+
## Designs
|
|
143
|
+
|
|
144
|
+
A ticket's design goes in `docs/specs/<ticket>.md`, for example
|
|
145
|
+
`docs/specs/DEK-12.md`, and its plan in `docs/plans/<ticket>.md`.
|
|
146
|
+
MD
|
|
147
|
+
touch docs/specs/.gitkeep
|
|
148
|
+
|
|
149
|
+
git init --quiet
|
|
150
|
+
git add -A
|
|
151
|
+
git -c user.email=fixture@example.com -c user.name=fixture commit --quiet -m "decks 0.9.0"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: regex
|
|
3
|
+
# The trace carries SKILL.md's own routing table, which names every channel, so
|
|
4
|
+
# `contains` passes whenever the skill loads. Anchor on the two ways the agent
|
|
5
|
+
# routes instead, the same ones run-stats.sh meters: a Bash call that invokes
|
|
6
|
+
# `status.sh route deep` (a quoted mention, as in a grep, does not count), or the
|
|
7
|
+
# words opening an assistant message or following a label such as "Sluice:".
|
|
8
|
+
pattern: '"command":"(?:(?:[^"\\]|\\.)*?(?:[\s/]|\\[nt]))?status\.sh(?:\\"|'')?(?:\s|\\[nt])+route(?:\s|\\[nt])+deep(?:\s|\\[nt]|[;&|]|")|"text":"(?:[^.!?\n"]{0,100}[:=]\s*)?[\s*_#>\\]*deep channel'
|
|
9
|
+
flags: i
|
|
10
|
+
target: trace
|
|
11
|
+
weight: 2
|
|
12
|
+
---
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: tool_used
|
|
3
|
+
# The run plans and stops for sign-off, so no Edit may land under src/ or
|
|
4
|
+
# tests/. Bash is left out of the case's tools, so these two are the only ways
|
|
5
|
+
# code could change.
|
|
6
|
+
tool: Edit
|
|
7
|
+
input_match: '"file_path"\s*:\s*"(?:[^"]*/)?(?:src|tests)/'
|
|
8
|
+
min: 0
|
|
9
|
+
max: 0
|
|
10
|
+
weight: 2
|
|
11
|
+
---
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: tool_used
|
|
3
|
+
# The run plans and stops for sign-off, so no Write may land under src/ or
|
|
4
|
+
# tests/. Bash is left out of the case's tools, so these two are the only ways
|
|
5
|
+
# code could change.
|
|
6
|
+
tool: Write
|
|
7
|
+
input_match: '"file_path"\s*:\s*"(?:[^"]*/)?(?:src|tests)/'
|
|
8
|
+
min: 0
|
|
9
|
+
max: 0
|
|
10
|
+
weight: 2
|
|
11
|
+
---
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: llm
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
The agent was asked to plan a ticket that spans the chat router, the deck store and the redraft path.
|
|
6
|
+
|
|
7
|
+
PASS if the final message hands a written design back for sign-off: it says where the design was written and asks for approval, or otherwise makes clear it is waiting before any code is written.
|
|
8
|
+
FAIL if the final message reports implementation work as done, moves straight into building, or presents the design as settled with no sign-off asked for.
|
package/skills/sluice/evals/deep-plan-traces-the-example/graders/trace-finds-no-accepted-slide.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: llm
|
|
3
|
+
focus: { source: file, path: docs/specs/DEK-41.md }
|
|
4
|
+
weight: 3
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
This is the design for a ticket whose example is "Redraft the Pilot Stages
|
|
8
|
+
slide to lead with the pilot outcome" on the smoke deck. In the repo,
|
|
9
|
+
`findSlideByTitle` matches only on the title of a slide's accepted version,
|
|
10
|
+
only the canvas ever sets `accepted`, and every slide in
|
|
11
|
+
`data/decks/smoke.json` has `"accepted": null`. So the lookup the ticket says
|
|
12
|
+
to reuse finds no slide for the ticket's own example.
|
|
13
|
+
|
|
14
|
+
Judge the section under the design's Trace heading for the walk, and the rest
|
|
15
|
+
of the design only for the lookup it settles on.
|
|
16
|
+
|
|
17
|
+
PASS if the Trace section quotes the ticket's example, walks it through the
|
|
18
|
+
smoke deck's actual data, and records there that its slides have no accepted
|
|
19
|
+
version (so the existing lookup returns nothing), and the design builds the
|
|
20
|
+
slide lookup around that, consistent with the
|
|
21
|
+
README (an unaccepted slide is called by its latest version): for example,
|
|
22
|
+
matching the accepted version's title when there is one and the latest
|
|
23
|
+
version's otherwise.
|
|
24
|
+
FAIL if there is no Trace section, if the accepted-slide finding appears only
|
|
25
|
+
outside it, if the design reuses `findSlideByTitle` or the accepted version as
|
|
26
|
+
it stands, or if the accepted state is mentioned only as a general risk
|
|
27
|
+
without saying what the smoke deck holds.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deep-plan-traces-the-example
|
|
3
|
+
description: A deep plan whose ticket example contradicts the obvious design. Pins a Trace section in the spec that walks the example through the fixture's real data before sign-off.
|
|
4
|
+
tags: [routing, deep, write]
|
|
5
|
+
max_turns: 25
|
|
6
|
+
timeout_seconds: 900
|
|
7
|
+
allowed_tools: [Read, Glob, Grep, Skill, Write]
|
|
8
|
+
expected_outcome: Announces the deep channel, writes docs/specs/DEK-41.md with a Trace section that runs the ticket's sentence against the smoke deck, finds its slides have no accepted version, designs around that, and stops for sign-off without code.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Plan DEK-41 for us.
|
|
12
|
+
|
|
13
|
+
> Let people revise a single slide from chat. Example, on the smoke deck:
|
|
14
|
+
> "Redraft the Pilot Stages slide to lead with the pilot outcome" should redraft
|
|
15
|
+
> that slide and leave the rest alone. The chat router needs a revise intent;
|
|
16
|
+
> finding the slide and saving the redraft should reuse what the deck store and
|
|
17
|
+
> the redraft path already do.
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"origin": "docs/specs/2026-08-07-sluice-design.md",
|
|
32
32
|
"cases": [
|
|
33
33
|
"deep-plan-across-subsystems",
|
|
34
|
-
"deep-plan-asks-the-fork"
|
|
34
|
+
"deep-plan-asks-the-fork",
|
|
35
|
+
"deep-plan-traces-the-example"
|
|
35
36
|
]
|
|
36
37
|
},
|
|
37
38
|
{
|
|
@@ -63,7 +64,8 @@
|
|
|
63
64
|
"origin": "docs/specs/2026-08-07-sluice-design.md",
|
|
64
65
|
"cases": [
|
|
65
66
|
"deep-plan-across-subsystems",
|
|
66
|
-
"deep-plan-asks-the-fork"
|
|
67
|
+
"deep-plan-asks-the-fork",
|
|
68
|
+
"deep-plan-traces-the-example"
|
|
67
69
|
]
|
|
68
70
|
},
|
|
69
71
|
{
|
|
@@ -74,6 +76,38 @@
|
|
|
74
76
|
"deep-plan-asks-the-fork"
|
|
75
77
|
]
|
|
76
78
|
},
|
|
79
|
+
{
|
|
80
|
+
"id": "deep-design-traces-the-example",
|
|
81
|
+
"states": "SKILL.md: Deep channel",
|
|
82
|
+
"origin": "commit:7decf60",
|
|
83
|
+
"cases": [
|
|
84
|
+
"deep-plan-traces-the-example"
|
|
85
|
+
]
|
|
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
|
+
},
|
|
77
111
|
{
|
|
78
112
|
"id": "deep-stops-for-plan-and-pre-flight",
|
|
79
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.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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,154 @@ 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
|
+
|
|
263
|
+
## The trace
|
|
264
|
+
|
|
265
|
+
A design written from the shape of the code (which functions exist, which slots
|
|
266
|
+
they fill) can be sound on paper and still fail the request's own example,
|
|
267
|
+
and nothing after it notices: per-task reviews read each task against its own
|
|
268
|
+
text, and the hunt runs once every task is done. So walk the request through
|
|
269
|
+
the system as it stands before you draft, and give the design a `## Trace`
|
|
270
|
+
section that records the walk (in the plan file under plan mode, carried into
|
|
271
|
+
the spec on approval):
|
|
272
|
+
|
|
273
|
+
1. **The example, verbatim.** Quote the sentence, command or scenario the
|
|
274
|
+
request gives. Where it gives none, write the acceptance criterion as one
|
|
275
|
+
concrete case and say that you did.
|
|
276
|
+
2. **Through the real data.** Follow that case step by step through the rows,
|
|
277
|
+
files or fixtures it would touch, as they exist now rather than as the
|
|
278
|
+
design assumes them: read the record, run the query, open the fixture, and
|
|
279
|
+
cite what each step read. "A deck built in chat has no accepted slides" is
|
|
280
|
+
a fact one read away, and a design keyed on accepted slides is wrong without
|
|
281
|
+
it.
|
|
282
|
+
3. **What each reused path requires and writes.** For every function the
|
|
283
|
+
design reuses, its preconditions and its side effects, read from the code
|
|
284
|
+
and not from its name. Run the example a second time where the request
|
|
285
|
+
implies a repeat (a redraft of a redraft, an edit after an edit), because
|
|
286
|
+
what the first pass wrote is what the second pass reads.
|
|
287
|
+
4. **What the walk ruled out.** Each place it contradicted the obvious design
|
|
288
|
+
(the reuse the request suggests, the first shape that came to mind), and
|
|
289
|
+
what the design does instead. An empty list is a result, not a skipped
|
|
290
|
+
step.
|
|
291
|
+
|
|
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.
|
|
297
|
+
|
|
298
|
+
The trace is what the sign-off is signing: a design without the section is
|
|
299
|
+
incomplete, however good the rest reads. It is not a prototype and not a hunt;
|
|
300
|
+
anything that only shows at runtime (routing, timing, a renderer) stays with
|
|
301
|
+
the hunt after the flip.
|
|
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
|
+
|
|
186
359
|
## Pre-flight
|
|
187
360
|
|
|
188
361
|
Design signed off, plan written, nothing built yet. Before Task 1, stop once
|
|
@@ -492,7 +665,9 @@ who will carry it out, only your partner's read of work you will do yourself.
|
|
|
492
665
|
Task isolation is gone, so
|
|
493
666
|
`run.json` and the record now carry all of the state that outlives compaction
|
|
494
667
|
and matter more, not less. And fresh context is unavailable, which was the entire thing
|
|
495
|
-
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.
|
|
496
671
|
|
|
497
672
|
One thing does not change: the work still owes a review. Reading your own diff
|
|
498
673
|
is not one, and the table below still names which tasks needed the stronger
|