@trailstep/create-flows 0.3.1 → 0.3.3

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/dist/index.js CHANGED
@@ -1,527 +1,365 @@
1
- // src/grill-it-away/workflow.ts
2
- import { defineWorkflow, shape as shape2 } from "@trailstep/authoring";
1
+ import {
2
+ MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS,
3
+ STORY_BOUNDARY,
4
+ STORY_CONTEXT_END,
5
+ STORY_CONTEXT_START,
6
+ STORY_STATE_KEYS,
7
+ normalizeTakeItAwayWorkflowOptions,
8
+ resetStoryLocalState,
9
+ reviewOutput,
10
+ reviewPasses,
11
+ storyRouterStep,
12
+ takeItAwayInput,
13
+ takeItAwayOutput
14
+ } from "./chunk-RF3PMHPX.js";
3
15
 
4
- // src/feature-implementation/shared/output-schema.ts
5
- import { jsonSchema } from "@trailstep/authoring";
6
- var takeItAwayOutput = jsonSchema({
7
- type: "object",
8
- properties: {
9
- status: { type: "string", const: "implemented" },
10
- featureDocPath: { type: "string" },
11
- implementationDocPath: { type: "string" },
12
- storyCount: { type: "number" },
13
- completedStories: { type: "array", items: { type: "string" } },
14
- summary: { type: "string" }
16
+ // src/grill-it-away/workflow.ts
17
+ import { defineWorkflow, shape } from "@trailstep/authoring";
18
+
19
+ // src/feature-implementation/shared/agent-roles.ts
20
+ var featureImplementationAgents = {
21
+ featureWriter: {
22
+ size: "medium",
23
+ thinking: "medium",
24
+ description: "Turns a conversation/feature request into a standalone feature doc."
15
25
  },
16
- required: [
17
- "status",
18
- "featureDocPath",
19
- "implementationDocPath",
20
- "storyCount",
21
- "completedStories",
22
- "summary"
23
- ],
24
- additionalProperties: false
25
- });
26
- function extractStoryTitle(storyContent, fallbackIndex) {
27
- const heading = /^#{1,6}\s+(.+)$/m.exec(storyContent);
28
- return heading?.[1]?.trim() || `Story ${fallbackIndex}`;
29
- }
26
+ planner: {
27
+ size: "large",
28
+ thinking: "high",
29
+ description: "Creates or improves the implementation strategy: architecture-aware, TDD/vertical-slice/tracer-bullet story design."
30
+ },
31
+ reviewer: {
32
+ size: "large",
33
+ thinking: "high",
34
+ description: "Reviews implementation strategies before story slicing."
35
+ },
36
+ slicer: {
37
+ size: "large",
38
+ thinking: "high",
39
+ description: "Slices an approved implementation strategy into coherent implementation stories."
40
+ },
41
+ storyExplorer: {
42
+ size: "small",
43
+ thinking: "low",
44
+ description: "Finds story-specific files, seams, and validation hints without editing."
45
+ },
46
+ testWriter: {
47
+ size: "medium",
48
+ thinking: "medium",
49
+ description: "Writes focused behavioral red tests for the active story."
50
+ },
51
+ storyImplementer: {
52
+ size: "medium",
53
+ thinking: "medium",
54
+ description: "Implements the smallest green slice for the active story."
55
+ },
56
+ storyDoctor: {
57
+ size: "large",
58
+ thinking: "high",
59
+ description: "Diagnoses and repairs repeated active-story validation failures before the workflow exhausts retries."
60
+ },
61
+ validator: {
62
+ size: "small",
63
+ thinking: "low",
64
+ description: "Runs focused validation commands and reports concise results."
65
+ },
66
+ storyReviewer: {
67
+ size: "large",
68
+ thinking: "high",
69
+ description: "Reviews isolated story diffs and phase evidence before commit."
70
+ }
71
+ };
30
72
 
31
73
  // src/grill-it-away/grill/step.ts
32
74
  import { step as step8 } from "@trailstep/authoring";
33
75
 
76
+ // src/feature-implementation/initialize-take-it-away/step.ts
77
+ import { state as state6, step as step7 } from "@trailstep/authoring";
78
+
34
79
  // src/feature-implementation/create-feature-doc/step.ts
35
- import { Document as Document2, state as state5, step as step7 } from "@trailstep/authoring";
36
-
37
- // src/feature-implementation/create-or-improve-implementation-doc/step.ts
38
- import { Document, state as state4, step as step6 } from "@trailstep/authoring";
39
-
40
- // src/feature-implementation/review-implementation-doc/step.ts
41
- import { fail as fail5, step as step5 } from "@trailstep/authoring";
42
-
43
- // src/feature-implementation/shared/constants.ts
44
- var STORY_BOUNDARY = "<!-- trailstep-story-boundary -->";
45
- var STORY_CONTEXT_START = "<context>";
46
- var STORY_CONTEXT_END = "</context>";
47
- var REVIEW_PASS_THRESHOLD = 4;
48
- var MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS = 5;
49
- var MAX_STORY_REVIEW_ATTEMPTS = 3;
50
-
51
- // src/feature-implementation/shared/review-schema.ts
52
- import { jsonSchema as jsonSchema2 } from "@trailstep/authoring";
53
- var RATING_SCHEMA = { type: "integer", minimum: 1, maximum: 5 };
54
- var reviewOutput = jsonSchema2({
80
+ import { Document as Document3, state as state5, step as step6 } from "@trailstep/authoring";
81
+
82
+ // src/feature-implementation/create-or-improve-implementation-strategy/step.ts
83
+ import { Document as Document2, state as state4, step as step5 } from "@trailstep/authoring";
84
+
85
+ // src/feature-implementation/review-implementation-strategy/step.ts
86
+ import { fail as fail3, step as step4 } from "@trailstep/authoring";
87
+
88
+ // src/feature-implementation/slice-implementation-stories/step.ts
89
+ import { Document, state as state3, step as step3 } from "@trailstep/authoring";
90
+
91
+ // src/feature-implementation/split-implementation-stories/step.ts
92
+ import { document, fail as fail2, state as state2, step as step2 } from "@trailstep/authoring";
93
+
94
+ // src/feature-implementation/planning-checkpoint/step.ts
95
+ import {
96
+ fail,
97
+ jsonSchema,
98
+ state,
99
+ step
100
+ } from "@trailstep/authoring";
101
+ var planningCheckpointAnswer = jsonSchema({
55
102
  type: "object",
56
103
  properties: {
57
- score: RATING_SCHEMA,
58
- summary: { type: "string" },
59
- methodologyRatings: {
60
- type: "object",
61
- properties: {
62
- tdd: RATING_SCHEMA,
63
- verticalSlicing: RATING_SCHEMA,
64
- tracerBullet: RATING_SCHEMA,
65
- dependencies: RATING_SCHEMA,
66
- architecture: RATING_SCHEMA
67
- },
68
- required: ["tdd", "verticalSlicing", "tracerBullet", "dependencies", "architecture"],
69
- additionalProperties: false
70
- },
71
- requiredImprovements: { type: "array", items: { type: "string" } }
104
+ action: { type: "string", enum: ["approve", "revise", "abort"] },
105
+ notes: { type: "string" }
72
106
  },
73
- required: ["score", "summary", "methodologyRatings", "requiredImprovements"],
107
+ required: ["action"],
74
108
  additionalProperties: false
75
109
  });
76
- function reviewPasses(review) {
77
- return review.score >= REVIEW_PASS_THRESHOLD;
78
- }
79
-
80
- // src/feature-implementation/split-implementation-stories/step.ts
81
- import { document, fail as fail4, state as state3, step as step4 } from "@trailstep/authoring";
82
-
83
- // src/feature-implementation/implement-story/step.ts
84
- import { fail as fail3, step as step3 } from "@trailstep/authoring";
85
-
86
- // src/feature-implementation/review-story-implementation/step.ts
87
- import { fail as fail2, step as step2 } from "@trailstep/authoring";
88
-
89
- // src/feature-implementation/commit-reviewed-story/step.ts
90
- import { done, fail, state as state2, step } from "@trailstep/authoring";
91
-
92
- // src/feature-implementation/shared/story-state.ts
93
- import { execFile } from "child_process";
94
- import { promisify } from "util";
95
- import { state } from "@trailstep/authoring";
96
- var execFileAsync = promisify(execFile);
97
- var STORY_STATE_KEYS = {
98
- activeStory: "activeStory",
99
- activeStoryStartCommit: "activeStoryStartCommit",
100
- completedStories: "completedStories",
101
- storyQueue: "storyQueue"
102
- };
103
- async function runGit(args, cwd) {
104
- try {
105
- const { stdout } = await execFileAsync("git", [...args], {
106
- cwd,
107
- maxBuffer: 1024 * 1024 * 10
110
+ var planningCheckpointStep = step({ id: "planning-checkpoint" }).wait(async (context) => {
111
+ const input = context.input;
112
+ const waitId = await nextPlanningCheckpointWaitId();
113
+ return {
114
+ id: waitId,
115
+ kind: "input",
116
+ message: [
117
+ "Review the accepted implementation plan and story slices before execution.",
118
+ `Implementation doc: ${input.implementationDoc.path}`,
119
+ 'Answer with {"action":"approve"} to continue, {"action":"revise","notes":"..."} to return to planning, or {"action":"abort","notes":"..."} to stop.'
120
+ ].join("\n"),
121
+ output: planningCheckpointAnswer
122
+ };
123
+ }).do(async (context, input) => {
124
+ const answer = readPlanningCheckpointAnswer(context.waits);
125
+ if (!answer) {
126
+ return fail({
127
+ code: "planning_checkpoint_missing_answer",
128
+ message: "Planning checkpoint resumed without a recorded human answer."
108
129
  });
109
- return { ok: true, stdout: stdout.trimEnd() };
110
- } catch (error) {
111
- const message = error instanceof Error ? error.message : String(error);
112
- return { ok: false, error: message };
113
130
  }
114
- }
115
- async function recordActiveStoryStartCommit() {
116
- const existing = await state.get(
117
- STORY_STATE_KEYS.activeStoryStartCommit
118
- );
119
- if (existing?.commit || existing?.warning) {
120
- return existing;
121
- }
122
- const cwd = state.cwd;
123
- if (!cwd) {
124
- const missingCwd = {
125
- warning: "Workflow cwd is unavailable; the story reviewer cannot verify committed story changes from a durable git baseline."
126
- };
127
- await state.set(STORY_STATE_KEYS.activeStoryStartCommit, missingCwd);
128
- return missingCwd;
129
- }
130
- const result = await runGit(["rev-parse", "HEAD"], cwd);
131
- if (!result.ok || result.stdout.length === 0) {
132
- const missingGitBaseline = {
133
- warning: `Unable to record story start commit with \`git rev-parse HEAD\`: ${result.ok ? "no commit was returned" : result.error}`
134
- };
135
- await state.set(STORY_STATE_KEYS.activeStoryStartCommit, missingGitBaseline);
136
- return missingGitBaseline;
131
+ if (answer.action === "abort") {
132
+ return fail({
133
+ code: "planning_checkpoint_aborted",
134
+ message: answer.notes?.trim() || "Human aborted at the planning checkpoint.",
135
+ details: { implementationDocPath: input.implementationDoc.path }
136
+ });
137
137
  }
138
- const baseline = { commit: result.stdout };
139
- await state.set(STORY_STATE_KEYS.activeStoryStartCommit, baseline);
140
- return baseline;
141
- }
142
- async function resetActiveStoryStartCommit() {
143
- await state.set(STORY_STATE_KEYS.activeStoryStartCommit, null);
144
- }
145
- async function loadStoryReviewGitContext() {
146
- const baseline = await state.get(
147
- STORY_STATE_KEYS.activeStoryStartCommit
148
- );
149
- const warnings = [];
150
- if (baseline?.warning) {
151
- warnings.push(baseline.warning);
138
+ if (answer.action === "revise") {
139
+ await state.set(STORY_STATE_KEYS.skipNextPlanningCheckpoint, true);
140
+ return createOrImproveImplementationStrategyStep({
141
+ featureDoc: input.featureDoc,
142
+ previousReview: humanRevisionReview(answer.notes),
143
+ attempt: 1
144
+ });
152
145
  }
153
- const cwd = state.cwd;
154
- if (!cwd) {
155
- warnings.push(
156
- "Workflow cwd is unavailable; reviewer must treat committed story-change context as missing."
157
- );
158
- return { storyStartCommit: baseline?.commit, warnings };
146
+ const activeStory = await state.get(STORY_STATE_KEYS.activeStory);
147
+ if (!activeStory) {
148
+ return fail({
149
+ code: "planning_checkpoint_missing_active_story",
150
+ message: "Planning checkpoint approved, but no active story was prepared for execution.",
151
+ details: { implementationDocPath: input.implementationDoc.path }
152
+ });
159
153
  }
160
- const status = await runGit(["status", "--short"], cwd);
161
- if (!status.ok) {
162
- warnings.push(`Unable to inspect \`git status --short\`: ${status.error}`);
154
+ return storyRouterStep({ reason: "start-story", currentStory: activeStory });
155
+ });
156
+ async function shouldPauseForPlanningCheckpoint() {
157
+ const options = await state.get(STORY_STATE_KEYS.workflowOptions);
158
+ if (!(options?.planningCheckpoint.enabled ?? false)) {
159
+ return false;
163
160
  }
164
- const uncommittedDiff = await runGit(["diff"], cwd);
165
- if (!uncommittedDiff.ok) {
166
- warnings.push(
167
- `Unable to inspect uncommitted changes with \`git diff\`: ${uncommittedDiff.error}`
168
- );
161
+ const skipNextCheckpoint = await state.get(
162
+ STORY_STATE_KEYS.skipNextPlanningCheckpoint
163
+ );
164
+ if (skipNextCheckpoint) {
165
+ await state.set(STORY_STATE_KEYS.skipNextPlanningCheckpoint, false);
166
+ return false;
169
167
  }
170
- const storyStartCommit = baseline?.commit;
171
- let committedDiff;
172
- if (!storyStartCommit) {
173
- warnings.push(
174
- "No durable story start commit is recorded; reviewer must not silently review only the current working tree diff."
175
- );
176
- } else {
177
- const baselineReachable = await runGit(["cat-file", "-e", `${storyStartCommit}^{commit}`], cwd);
178
- if (!baselineReachable.ok) {
179
- warnings.push(
180
- `Recorded story start commit \`${storyStartCommit}\` is invalid or unreachable: ${baselineReachable.error}`
181
- );
182
- } else {
183
- const committed = await runGit(["diff", `${storyStartCommit}..HEAD`], cwd);
184
- if (!committed.ok) {
185
- warnings.push(
186
- `Unable to inspect committed story changes with \`git diff ${storyStartCommit}..HEAD\`: ${committed.error}`
187
- );
188
- } else {
189
- committedDiff = committed.stdout;
190
- }
168
+ return true;
169
+ }
170
+ async function nextPlanningCheckpointWaitId() {
171
+ const previousCount = await state.get(STORY_STATE_KEYS.planningCheckpointCount) ?? 0;
172
+ const nextCount = previousCount + 1;
173
+ await state.set(STORY_STATE_KEYS.planningCheckpointCount, nextCount);
174
+ return nextCount === 1 ? "planning-checkpoint" : `planning-checkpoint-${nextCount}`;
175
+ }
176
+ function readPlanningCheckpointAnswer(waits) {
177
+ for (const [waitId, value] of Object.entries(waits)) {
178
+ if (!waitId.startsWith("planning-checkpoint") || !isPlanningCheckpointAnswer(value)) {
179
+ continue;
191
180
  }
181
+ return value;
192
182
  }
183
+ return void 0;
184
+ }
185
+ function isPlanningCheckpointAnswer(value) {
186
+ if (typeof value !== "object" || value === null) {
187
+ return false;
188
+ }
189
+ const action = value.action;
190
+ return action === "approve" || action === "revise" || action === "abort";
191
+ }
192
+ function humanRevisionReview(notes) {
193
+ const trimmedNotes = notes?.trim();
193
194
  return {
194
- storyStartCommit,
195
- statusShort: status.ok ? status.stdout : void 0,
196
- committedDiff,
197
- uncommittedDiff: uncommittedDiff.ok ? uncommittedDiff.stdout : void 0,
198
- warnings
195
+ score: 1,
196
+ summary: trimmedNotes ? `Human requested planning revision: ${trimmedNotes}` : "Human requested planning revision at the checkpoint.",
197
+ methodologyRatings: {
198
+ tdd: 1,
199
+ verticalSlicing: 1,
200
+ tracerBullet: 1,
201
+ dependencies: 1,
202
+ architecture: 1
203
+ },
204
+ requiredImprovements: [
205
+ trimmedNotes || "Revise the implementation plan per human checkpoint feedback."
206
+ ]
199
207
  };
200
208
  }
201
209
 
202
- // src/feature-implementation/commit-reviewed-story/run-git.ts
203
- import { execFile as execFile2 } from "child_process";
204
- import { promisify as promisify2 } from "util";
205
- var execFileAsync2 = promisify2(execFile2);
206
- async function runGit2(args, cwd) {
207
- try {
208
- const { stdout } = await execFileAsync2("git", [...args], {
209
- cwd,
210
- maxBuffer: 1024 * 1024 * 10
211
- });
212
- return { ok: true, stdout: stdout.trimEnd() };
213
- } catch (error) {
214
- const message = error instanceof Error ? error.message : String(error);
215
- return { ok: false, error: message };
210
+ // src/feature-implementation/split-implementation-stories/story-context.ts
211
+ function parseStoryContextBlocks(rawBlocks) {
212
+ return rawBlocks.flatMap((rawBlock) => {
213
+ const parsed = parseStoryContextBlock(rawBlock);
214
+ return parsed ? [parsed] : [];
215
+ });
216
+ }
217
+ function selectStoryContext({
218
+ blocks,
219
+ storyContent,
220
+ audience,
221
+ phase
222
+ }) {
223
+ return blocks.filter(
224
+ (block) => blockMatchesAudience(block, audience) && blockMatchesStory(block, storyContent) && blockMatchesPhase(block, phase)
225
+ ).map((block) => block.body).join("\n\n---\n\n");
226
+ }
227
+ function parseStoryContextBlock(rawBlock) {
228
+ const lines = rawBlock.replace(/\r\n?/g, "\n").split("\n");
229
+ const firstBlankLine = lines.findIndex((line) => line.trim().length === 0);
230
+ const metadataLines = firstBlankLine === -1 ? lines : lines.slice(0, firstBlankLine);
231
+ const bodyLines = firstBlankLine === -1 ? [] : lines.slice(firstBlankLine + 1);
232
+ const metadata = parseMetadataLines(metadataLines);
233
+ const body = bodyLines.join("\n").trim();
234
+ if (body.length === 0 || !hasRecognizedScopeMetadata(metadata)) {
235
+ return void 0;
216
236
  }
237
+ return { ...metadata, body };
217
238
  }
218
-
219
- // src/feature-implementation/commit-reviewed-story/step.ts
220
- var commitReviewedStoryStep = step({ id: "commit-reviewed-story" }).do(
221
- async (input) => {
222
- const activeStory = await state2.get(STORY_STATE_KEYS.activeStory) ?? input.currentStory;
223
- if (storyAutoCommitEnabled()) {
224
- const commitResult = await commitReviewedStoryChanges(
225
- activeStory,
226
- input.implementationSummary
227
- );
228
- if (!commitResult.ok) {
229
- return fail({
230
- code: commitResult.code,
231
- message: commitResult.message,
232
- details: commitResult.details
233
- });
239
+ function parseMetadataLines(lines) {
240
+ const metadata = {};
241
+ for (const line of lines) {
242
+ const match = /^([A-Za-z-]+):\s*(.*?)\s*$/.exec(line.trim());
243
+ if (!match) {
244
+ continue;
245
+ }
246
+ const key = match[1]?.toLowerCase();
247
+ const value = match[2] ?? "";
248
+ if (key === "audience") {
249
+ const audience = parseAudience(value);
250
+ if (audience) {
251
+ metadata.audience = audience;
234
252
  }
253
+ continue;
235
254
  }
236
- return completeReviewedStory(activeStory);
237
- }
238
- );
239
- function storyAutoCommitEnabled() {
240
- const mode = process.env.TRAILSTEP_STORY_COMMIT_MODE;
241
- return ["1", "true", "enabled", "worktree"].includes(mode?.toLowerCase() ?? "");
242
- }
243
- async function commitReviewedStoryChanges(activeStory, implementationSummary) {
244
- const cwd = state2.cwd;
245
- if (!cwd) {
246
- return {
247
- ok: false,
248
- code: "story_commit_missing_cwd",
249
- message: "Cannot commit reviewed story because the TrailStep workflow cwd is unavailable.",
250
- details: { storyPath: activeStory.path }
251
- };
252
- }
253
- const insideWorkTree = await runGit2(["rev-parse", "--is-inside-work-tree"], cwd);
254
- if (!insideWorkTree.ok || insideWorkTree.stdout !== "true") {
255
- return {
256
- ok: false,
257
- code: "story_commit_not_git_worktree",
258
- message: "Cannot commit reviewed story because the workflow is not running inside a git worktree.",
259
- details: {
260
- storyPath: activeStory.path,
261
- gitError: insideWorkTree.ok ? void 0 : insideWorkTree.error
255
+ if (key === "stories") {
256
+ const stories = parseStoryList(value);
257
+ if (stories) {
258
+ metadata.stories = stories;
262
259
  }
263
- };
264
- }
265
- const add = await runGit2(["add", "-A"], cwd);
266
- if (!add.ok) {
267
- return {
268
- ok: false,
269
- code: "story_commit_stage_failed",
270
- message: "Cannot commit reviewed story because `git add -A` failed.",
271
- details: { storyPath: activeStory.path, gitError: add.error }
272
- };
273
- }
274
- const staged = await runGit2(["diff", "--cached", "--name-only"], cwd);
275
- if (!staged.ok) {
276
- return {
277
- ok: false,
278
- code: "story_commit_status_failed",
279
- message: "Cannot commit reviewed story because staged changes could not be inspected.",
280
- details: { storyPath: activeStory.path, gitError: staged.error }
281
- };
282
- }
283
- if (staged.stdout.trim().length === 0) {
284
- const baseline = await state2.get(
285
- STORY_STATE_KEYS.activeStoryStartCommit
286
- );
287
- const head = await runGit2(["rev-parse", "HEAD"], cwd);
288
- if (baseline?.commit && head.ok && head.stdout !== baseline.commit) {
289
- return { ok: true };
260
+ continue;
290
261
  }
291
- return {
292
- ok: false,
293
- code: "story_commit_empty",
294
- message: "The story passed review, but there are no staged or already-committed changes since the story baseline to commit.",
295
- details: {
296
- storyPath: activeStory.path,
297
- storyStartCommit: baseline?.commit,
298
- head: head.ok ? head.stdout : void 0,
299
- gitError: head.ok ? void 0 : head.error
262
+ if (key === "phases") {
263
+ const phases = parsePhaseList(value);
264
+ if (phases) {
265
+ metadata.phases = phases;
300
266
  }
301
- };
267
+ }
302
268
  }
303
- const storyTitle = extractStoryTitle(activeStory.content, 1);
304
- const commit = await runGit2(
305
- [
306
- "commit",
307
- "-m",
308
- truncateCommitSubject(`trailstep: ${storyTitle}`),
309
- "-m",
310
- commitBody(activeStory, implementationSummary, staged.stdout)
311
- ],
312
- cwd
313
- );
314
- if (!commit.ok) {
315
- return {
316
- ok: false,
317
- code: "story_commit_failed",
318
- message: "The story passed review, but TrailStep could not create the story commit.",
319
- details: { storyPath: activeStory.path, gitError: commit.error }
320
- };
269
+ return metadata;
270
+ }
271
+ function parseAudience(value) {
272
+ const normalized = value.trim().toLowerCase();
273
+ if (normalized === "implementer" || normalized === "reviewer" || normalized === "all") {
274
+ return normalized;
321
275
  }
322
- return { ok: true };
276
+ return void 0;
323
277
  }
324
- async function completeReviewedStory(activeStory) {
325
- const completed = await state2.get(STORY_STATE_KEYS.completedStories) ?? [];
326
- const updatedCompleted = [
327
- ...completed,
328
- extractStoryTitle(activeStory.content, completed.length + 1)
329
- ];
330
- await state2.set(STORY_STATE_KEYS.completedStories, updatedCompleted);
331
- const storyQueue = await state2.get(STORY_STATE_KEYS.storyQueue) ?? [];
332
- const [nextStory, ...remaining] = storyQueue;
333
- if (!nextStory) {
334
- await state2.set(STORY_STATE_KEYS.activeStory, null);
335
- await resetActiveStoryStartCommit();
336
- const featureDoc = await state2.get("featureDoc");
337
- const implementationDoc = await state2.get("implementationDoc");
338
- const output = {
339
- status: "implemented",
340
- featureDocPath: featureDoc?.path ?? "",
341
- implementationDocPath: implementationDoc?.path ?? "",
342
- storyCount: updatedCompleted.length,
343
- completedStories: updatedCompleted,
344
- summary: `Implemented and reviewed ${updatedCompleted.length} ${updatedCompleted.length === 1 ? "story" : "stories"}.`
345
- };
346
- return done(output);
278
+ function parseStoryList(value) {
279
+ const items = splitMetadataList(value);
280
+ if (items.length === 0) {
281
+ return void 0;
347
282
  }
348
- await state2.set(STORY_STATE_KEYS.storyQueue, remaining);
349
- await state2.set(STORY_STATE_KEYS.activeStory, nextStory);
350
- await resetActiveStoryStartCommit();
351
- await recordActiveStoryStartCommit();
352
- return implementStoryStep({ currentStory: nextStory, attempt: 1 });
283
+ if (items.length === 1 && items[0]?.toLowerCase() === "all") {
284
+ return "all";
285
+ }
286
+ return items.filter((item) => item.toLowerCase() !== "all");
287
+ }
288
+ function parsePhaseList(value) {
289
+ const items = splitMetadataList(value);
290
+ if (items.length === 0) {
291
+ return void 0;
292
+ }
293
+ if (items.length === 1 && items[0]?.toLowerCase() === "all") {
294
+ return "all";
295
+ }
296
+ const phases = items.filter(isStoryContextPhase);
297
+ return phases.length > 0 ? phases : void 0;
353
298
  }
354
- function truncateCommitSubject(subject) {
355
- return subject.length <= 72 ? subject : `${subject.slice(0, 69)}...`;
299
+ function splitMetadataList(value) {
300
+ return value.split(",").map((item) => item.trim()).filter((item) => item.length > 0);
356
301
  }
357
- function commitBody(activeStory, implementationSummary, stagedFiles) {
302
+ function isStoryContextPhase(value) {
358
303
  return [
359
- "Committed automatically by TrailStep after a passing story review.",
360
- "",
361
- `Story artifact: ${activeStory.path}`,
362
- "",
363
- "Implementer summary:",
364
- implementationSummary?.trim() || "Not provided.",
365
- "",
366
- "Staged files:",
367
- stagedFiles.split("\n").filter((file) => file.length > 0).map((file) => `- ${file}`).join("\n")
368
- ].join("\n");
304
+ "deterministic-context-preflight",
305
+ "explore-story",
306
+ "write-red-tests",
307
+ "implement-green",
308
+ "validate-story",
309
+ "review-story-implementation"
310
+ ].includes(value);
369
311
  }
370
-
371
- // src/feature-implementation/review-story-implementation/prompt.ts
372
- import { promptSections, section } from "@trailstep/authoring";
373
-
374
- // src/feature-implementation/shared/feature-methodology.md?raw
375
- var feature_methodology_default = "# Feature workflow methodology\n\n## Planning principles\n\n- Start from observable behavior and user value.\n- Preserve uncertainty explicitly; do not invent missing product decisions.\n- Prefer the smallest vertical slice that proves a complete path through the system.\n- Use tracer bullets to de-risk integration seams early with thin end-to-end behavior.\n- Every implementation story must begin with a behavioral red test that fails for the right reason.\n- The green phase should add only enough production code to pass the focused red test.\n- Refactor only after focused tests pass, and keep refactors scoped to the feature.\n- Dependencies are hard dependencies only: story B depends on story A when B cannot pass its own tests until A is complete.\n\n## Review scoring\n\nScore on a 1-5 integer scale:\n\n- 5: excellent; strongly aligned with TDD, vertical slicing, tracer-bullet risk reduction, dependency clarity, and local architecture.\n- 4: good; acceptable with only minor non-blocking improvements.\n- 3: incomplete; useful but needs material changes before continuing.\n- 2: poor; misses core methodology or important requirements.\n- 1: unusable; unsafe, incoherent, or not traceable to the feature.\n\nScores of 4 or 5 pass. Scores below 4 must list specific `requiredImprovements` so the next attempt can address them directly.\n";
376
-
377
- // src/feature-implementation/shared/story-implementation-contract.md?raw
378
- var story_implementation_contract_default = "# Story implementation contract\n\nThe story loop works one story at a time. Each story is self-contained. You will not see non-context overview text or other stories; you will see only the active story plus any shared `<context>` content prepended by the split-stories step.\n\n## Protect pre-existing uncommitted work\n\nRun `git status --short` before making any change.\n\n- The preferred execution mode is a TrailStep-created isolated worktree/branch. In that mode, a successful review is followed by an automatic story commit, so the next story starts from a clean diff boundary.\n- Treat every pre-existing uncommitted file as protected \u2014 it may be unfinished human work, another run's in-progress work, or a sign that isolation failed.\n- Do not run destructive cleanup: no `git reset`, `git checkout --`, broad `git clean`, or equivalent revert/format-away operations.\n- Do not delete or rewrite unrelated chunks in a file that already has changes.\n- If this story requires editing a file that is already dirty, preserve the existing changes and make only the minimal additional change the story requires.\n- If dirty work makes the story ambiguous or blocked, stop and report a blocked state instead of removing it.\n- Do not create git commits yourself unless the story explicitly requires it. TrailStep creates the reviewed story commit after the reviewer passes the story when auto-commit mode is enabled.\n\n## Implementer responsibilities\n\n- Implement only the current story and required dependency-compatible adjustments. Do not start later stories.\n- Use strict behavioral-red TDD: write or update a focused failing behavioral test first, confirm it fails for the intended reason when feasible, implement the smallest change to pass it, then refactor only after it's green.\n- Run the story's validation commands before reporting completion when feasible.\n- Do not report success until implementation and focused validation are done, or until a true blocked state is reached \u2014 report the blocker clearly instead of pretending the story is complete.\n\n## Reviewer responsibilities\n\n- Review only the active story diff from the recorded story baseline through HEAD plus the current uncommitted diff. Do not try to review every dirty change in the checkout as global context.\n- Inspect with read-only commands only (`git status --short`, the exact `git diff <storyStartCommit>..HEAD` command supplied in your prompt, and `git diff`). Do not run tests or other commands \u2014 the implementer is responsible for passing focused validation; this is an AI-only review.\n- Never edit code, delete files, revert files, run cleanup, stage files, commit files, or otherwise modify the working tree to isolate your review.\n- If unrelated dirty files, missing baseline information, or overlapping changes make the active story diff unreviewable, return a failing structured review that explains the isolation problem instead of changing the repository.\n- Review against: the story's acceptance criteria, TDD evidence and test quality, vertical-slice completeness, tracer-bullet alignment where applicable, minimal scope with no gold-plating, and integration with existing architecture/project conventions.\n\n## Completion\n\nA story is complete when code is changed as needed, behavioral tests exist, focused validation passes according to the implementer, and the implementation is well integrated with the existing architecture.\n";
379
-
380
- // src/feature-implementation/review-story-implementation/prompt.ts
381
- var fragments = {
382
- methodology: feature_methodology_default.trimEnd(),
383
- storyContract: story_implementation_contract_default.trimEnd()
384
- };
385
- function reviewStoryImplementationPrompt({
386
- input
387
- }) {
388
- const committedDiffCommand = input.gitContext.storyStartCommit ? `git diff ${input.gitContext.storyStartCommit}..HEAD` : "git diff <missing-story-start-commit>..HEAD";
389
- return promptSections(
390
- fragments.methodology,
391
- fragments.storyContract,
392
- section("Story", input.currentStory.content),
393
- section("Implementer summary", input.implementationSummary ?? "Not provided."),
394
- section(
395
- "Story review git baseline",
396
- [
397
- `Recorded story start commit: ${input.gitContext.storyStartCommit ?? "MISSING"}`,
398
- `Inspect committed story changes with: \`${committedDiffCommand}\``,
399
- "Inspect uncommitted working tree changes with: `git diff`",
400
- "Inspect status with: `git status --short`",
401
- input.gitContext.warnings.length > 0 ? [
402
- "",
403
- "Warnings / blocked context:",
404
- ...input.gitContext.warnings.map((warning) => `- ${warning}`)
405
- ].join("\n") : ""
406
- ].filter((line) => line.length > 0).join("\n")
407
- ),
408
- section(
409
- "Committed story changes (`git diff <storyStartCommit>..HEAD`)",
410
- input.gitContext.committedDiff ?? "Not available."
411
- ),
412
- section(
413
- "Uncommitted working tree changes (`git diff`)",
414
- input.gitContext.uncommittedDiff ?? "Not available."
415
- ),
416
- section(
417
- "Working tree status (`git status --short`)",
418
- input.gitContext.statusShort ?? "Not available."
419
- ),
420
- section(
421
- "Task",
422
- "Review only the active story slice represented by the recorded story start commit through HEAD, plus the current uncommitted working tree changes, against the story above and the implementer summary. Prefer the supplied diff/status sections; if you need to inspect locally, use read-only commands only (`git status --short`, the listed `git diff <storyStartCommit>..HEAD`, and `git diff`) \u2014 do not run tests, edit code, revert files, clean files, or try to isolate the diff by changing the repository. If unrelated dirty files, a missing/invalid baseline, or ambiguous context prevents a trustworthy review, say so in the structured review and require the implementer/workflow to fix the isolation; never remove changes yourself. Respond only with the structured review."
423
- )
424
- );
312
+ function hasRecognizedScopeMetadata(metadata) {
313
+ return Boolean(metadata.audience || metadata.stories || metadata.phases);
425
314
  }
426
-
427
- // src/feature-implementation/review-story-implementation/step.ts
428
- var reviewStoryImplementationStep = step2({ id: "review-story-implementation" }).prompt(reviewStoryImplementationPrompt, {
429
- agent: "reviewer",
430
- output: reviewOutput
431
- }).do(async (review, input) => {
432
- if (!reviewPasses(review)) {
433
- if (input.attempt >= MAX_STORY_REVIEW_ATTEMPTS) {
434
- return fail2({
435
- code: "story_review_exhausted",
436
- message: `Story failed review ${MAX_STORY_REVIEW_ATTEMPTS} times in a row (last score ${review.score}/5).`,
437
- details: { review, storyPath: input.currentStory.path }
438
- });
439
- }
440
- return implementStoryStep({
441
- currentStory: input.currentStory,
442
- previousStoryReview: review,
443
- attempt: input.attempt + 1
444
- });
315
+ function blockMatchesAudience(block, audience) {
316
+ return block.audience === audience || block.audience === "all";
317
+ }
318
+ function blockMatchesStory(block, storyContent) {
319
+ if (block.stories === "all") {
320
+ return true;
445
321
  }
446
- return commitReviewedStoryStep({
447
- currentStory: input.currentStory,
448
- implementationSummary: input.implementationSummary
449
- });
450
- });
451
-
452
- // src/feature-implementation/implement-story/prompt.ts
453
- import { jsonSchema as jsonSchema3, list, promptSections as promptSections2, section as section2 } from "@trailstep/authoring";
454
-
455
- // src/feature-implementation/shared/project-architecture-guidance.md?raw
456
- var project_architecture_guidance_default = "# Architecture awareness\n\nBefore naming files, choosing package/module boundaries, picking integration seams, or ordering stories/dependencies, ground the decision in the target project's own conventions rather than inventing structure:\n\n- Look for and read the project's own architecture/convention docs at its root and nearby \u2014 `CLAUDE.md`, `AGENTS.md`, `.pi/rules/`, ADRs, architecture docs, or an equivalent README section.\n- Prefer existing module/package boundaries, naming conventions, and file layout patterns already established in the project over introducing new ones.\n- When the project's own guidance is silent or ambiguous on a non-trivial integration choice, say so explicitly as an assumption rather than silently picking one convention over another.\n";
457
-
458
- // src/feature-implementation/implement-story/prompt.ts
459
- var fragments2 = {
460
- methodology: feature_methodology_default.trimEnd(),
461
- architectureGuidance: project_architecture_guidance_default.trimEnd(),
462
- storyContract: story_implementation_contract_default.trimEnd()
463
- };
464
- var implementStoryOutput = jsonSchema3({
465
- type: "object",
466
- properties: {
467
- blocked: { type: "boolean" },
468
- blockedReason: { type: "string" },
469
- summary: {
470
- type: "string",
471
- description: "Files changed, commands run, and their results."
472
- }
473
- },
474
- required: ["blocked", "summary"],
475
- additionalProperties: false
476
- });
477
- function implementStoryPrompt({ input }) {
478
- const taskBody = input.previousStoryReview === void 0 ? "Implement this story." : promptSections2(
479
- `This is retry attempt ${input.attempt} for this story. A previous review scored the implementation ${input.previousStoryReview.score}/5: ${input.previousStoryReview.summary}`,
480
- `Required improvements to address, without discarding what already works:
481
-
482
- ${list(input.previousStoryReview.requiredImprovements)}`
483
- );
484
- return promptSections2(
485
- fragments2.methodology,
486
- fragments2.architectureGuidance,
487
- fragments2.storyContract,
488
- section2("Story", input.currentStory.content),
489
- section2(
490
- "Task",
491
- `${taskBody}
492
-
493
- Set \`blocked: true\` with a clear \`blockedReason\` instead of guessing or pretending the story is complete. Otherwise set \`blocked: false\` and use \`summary\` to report files changed, commands run, and their results.`
494
- )
495
- );
322
+ if (!block.stories) {
323
+ return false;
324
+ }
325
+ const storyLabels = extractStoryLabels(storyContent).map(normalizeStoryLabel);
326
+ return block.stories.some((story) => storyLabels.includes(normalizeStoryLabel(story)));
496
327
  }
497
-
498
- // src/feature-implementation/implement-story/step.ts
499
- var implementStoryStep = step3({ id: "implement-story" }).prompt(implementStoryPrompt, {
500
- agent: "implementer",
501
- output: implementStoryOutput
502
- }).do(async (promptOutput, input) => {
503
- if (promptOutput.blocked) {
504
- return fail3({
505
- code: "story_blocked",
506
- message: promptOutput.blockedReason ?? "Story implementation reported a blocked state.",
507
- details: { storyPath: input.currentStory.path }
508
- });
328
+ function blockMatchesPhase(block, phase) {
329
+ if (!block.phases || block.phases === "all" || block.phases.includes(phase)) {
330
+ return true;
509
331
  }
510
- return reviewStoryImplementationStep({
511
- currentStory: input.currentStory,
512
- attempt: input.attempt,
513
- implementationSummary: promptOutput.summary,
514
- gitContext: await loadStoryReviewGitContext()
515
- });
516
- });
332
+ return phase === "deterministic-context-preflight" && block.phases.includes("explore-story");
333
+ }
334
+ function extractStoryLabels(storyContent) {
335
+ const match = /^#{1,6}\s+(Story\s+\d+)(?::\s*(.+))?\s*$/im.exec(storyContent);
336
+ const ordinal = match?.[1]?.trim();
337
+ if (!ordinal) {
338
+ return [];
339
+ }
340
+ const title = match?.[2]?.trim();
341
+ return title ? [ordinal, `${ordinal}: ${title}`] : [ordinal];
342
+ }
343
+ function normalizeStoryLabel(value) {
344
+ return value.trim().replace(/\s+/g, " ").toLowerCase();
345
+ }
517
346
 
518
347
  // src/feature-implementation/split-implementation-stories/step.ts
519
- var splitImplementationStoriesStep = step4({ id: "split-implementation-stories" }).do(
520
- async ({ implementationDoc }) => {
521
- const contextStartCount = countOccurrences(implementationDoc.content, STORY_CONTEXT_START);
522
- const contextEndCount = countOccurrences(implementationDoc.content, STORY_CONTEXT_END);
348
+ var splitImplementationStoriesStep = step2({ id: "split-implementation-stories" }).do(
349
+ async ({
350
+ featureDoc,
351
+ implementationDoc
352
+ }) => {
353
+ const contextStartCount = countStandaloneMarkerLines(
354
+ implementationDoc.content,
355
+ STORY_CONTEXT_START
356
+ );
357
+ const contextEndCount = countStandaloneMarkerLines(
358
+ implementationDoc.content,
359
+ STORY_CONTEXT_END
360
+ );
523
361
  if (contextStartCount !== contextEndCount) {
524
- return fail4({
362
+ return fail2({
525
363
  code: "unbalanced_story_context",
526
364
  message: `Found ${contextStartCount} \`${STORY_CONTEXT_START}\` marker(s) and ${contextEndCount} \`${STORY_CONTEXT_END}\` marker(s) in implementation-doc.md. Context blocks must be balanced.`,
527
365
  details: { implementationDocPath: implementationDoc.path }
@@ -529,21 +367,25 @@ var splitImplementationStoriesStep = step4({ id: "split-implementation-stories"
529
367
  }
530
368
  const contextMatches = Array.from(implementationDoc.content.matchAll(storyContextPattern()));
531
369
  if (contextMatches.length !== contextStartCount) {
532
- return fail4({
370
+ return fail2({
533
371
  code: "invalid_story_context_order",
534
372
  message: `Every \`${STORY_CONTEXT_START}\` marker in implementation-doc.md must be followed by a matching \`${STORY_CONTEXT_END}\` marker.`,
535
373
  details: { implementationDocPath: implementationDoc.path }
536
374
  });
537
375
  }
538
- const contextBlocks = contextMatches.map(([, context]) => context?.trim() ?? "").filter((context) => context.length > 0);
539
- const sharedContext = contextBlocks.join("\n\n---\n\n");
376
+ const parsedContextBlocks = parseStoryContextBlocks(
377
+ contextMatches.map(([, context]) => context ?? "")
378
+ );
540
379
  const contentWithoutContextBlocks = implementationDoc.content.replace(
541
380
  storyContextPattern(),
542
381
  ""
543
382
  );
544
- const chunks = contentWithoutContextBlocks.split(STORY_BOUNDARY).slice(1).map((chunk) => chunk.trim()).filter((chunk) => chunk.length > 0).map((chunk) => prependSharedContext(chunk, sharedContext));
383
+ const chunks = contentWithoutContextBlocks.split(STORY_BOUNDARY).slice(1).map((chunk) => chunk.trim()).filter((chunk) => chunk.length > 0);
384
+ const storyContexts = chunks.map(
385
+ (chunk) => buildStoryPhaseContexts(parsedContextBlocks, chunk)
386
+ );
545
387
  if (chunks.length === 0) {
546
- return fail4({
388
+ return fail2({
547
389
  code: "no_stories_found",
548
390
  message: `No \`${STORY_BOUNDARY}\`-delimited stories found in implementation-doc.md.`,
549
391
  details: { implementationDocPath: implementationDoc.path }
@@ -554,201 +396,280 @@ var splitImplementationStoriesStep = step4({ id: "split-implementation-stories"
554
396
  storyDocs.push(await document(chunk));
555
397
  }
556
398
  const [firstStory, ...remaining] = storyDocs;
557
- await state3.set(STORY_STATE_KEYS.storyQueue, remaining);
558
- await state3.set(STORY_STATE_KEYS.completedStories, []);
559
- await state3.set(STORY_STATE_KEYS.activeStory, firstStory);
560
- await recordActiveStoryStartCommit();
561
- return implementStoryStep({ currentStory: firstStory, attempt: 1 });
562
- }
563
- );
564
- function countOccurrences(value, needle) {
565
- if (needle.length === 0) {
566
- return 0;
567
- }
568
- let count = 0;
569
- let offset = 0;
570
- while (true) {
571
- const index = value.indexOf(needle, offset);
572
- if (index === -1) {
573
- return count;
399
+ const [firstStoryContext = {}, ...remainingStoryContexts] = storyContexts;
400
+ await state2.set(STORY_STATE_KEYS.storyQueue, remaining);
401
+ await state2.set(STORY_STATE_KEYS.storyContextQueue, remainingStoryContexts);
402
+ await state2.set(STORY_STATE_KEYS.completedStories, []);
403
+ await state2.set(STORY_STATE_KEYS.activeStory, firstStory);
404
+ await state2.set(STORY_STATE_KEYS.activeStoryContext, firstStoryContext);
405
+ await state2.set(STORY_STATE_KEYS.activePhase, "story-router");
406
+ await resetStoryLocalState({
407
+ preserveActiveStoryStartCommit: true,
408
+ preserveLatestStoryRouterState: true,
409
+ preserveStoryBaseline: true
410
+ });
411
+ if (featureDoc && await shouldPauseForPlanningCheckpoint()) {
412
+ return planningCheckpointStep({ featureDoc, implementationDoc });
574
413
  }
575
- count += 1;
576
- offset = index + needle.length;
414
+ return storyRouterStep({ reason: "start-story", currentStory: firstStory });
577
415
  }
416
+ );
417
+ function buildStoryPhaseContexts(blocks, storyContent) {
418
+ const phases = [
419
+ "deterministic-context-preflight",
420
+ "explore-story",
421
+ "write-red-tests",
422
+ "implement-green",
423
+ "validate-story",
424
+ "review-story-implementation"
425
+ ];
426
+ return Object.fromEntries(
427
+ phases.map((phase) => [
428
+ phase,
429
+ selectStoryContext({
430
+ blocks,
431
+ storyContent,
432
+ audience: phase === "review-story-implementation" ? "reviewer" : "implementer",
433
+ phase
434
+ })
435
+ ])
436
+ );
437
+ }
438
+ function countStandaloneMarkerLines(value, marker) {
439
+ return Array.from(value.matchAll(standaloneMarkerLinePattern(marker))).length;
440
+ }
441
+ function standaloneMarkerLinePattern(marker) {
442
+ return new RegExp(`^[ \\t]*${escapeRegExp(marker)}[ \\t]*$`, "gm");
578
443
  }
579
444
  function storyContextPattern() {
580
445
  return new RegExp(
581
- `${escapeRegExp(STORY_CONTEXT_START)}([\\s\\S]*?)${escapeRegExp(STORY_CONTEXT_END)}`,
582
- "g"
446
+ `^[ \\t]*${escapeRegExp(STORY_CONTEXT_START)}[ \\t]*\\r?\\n([\\s\\S]*?)^[ \\t]*${escapeRegExp(STORY_CONTEXT_END)}[ \\t]*$`,
447
+ "gm"
583
448
  );
584
449
  }
585
- function prependSharedContext(story, sharedContext) {
586
- if (sharedContext.length === 0) {
587
- return story;
588
- }
589
- return ["## Shared implementation context", sharedContext, "## Active story", story].join("\n\n");
590
- }
591
450
  function escapeRegExp(value) {
592
451
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
593
452
  }
594
453
 
595
- // src/feature-implementation/review-implementation-doc/prompt.ts
596
- import { list as list2, promptSections as promptSections3, section as section3 } from "@trailstep/authoring";
454
+ // src/feature-implementation/slice-implementation-stories/prompt.ts
455
+ import { promptSections, section } from "@trailstep/authoring";
456
+
457
+ // src/feature-implementation/shared/feature-methodology.md?raw
458
+ var feature_methodology_default = "# Feature workflow methodology\n\n## Planning principles\n\n- Start from observable behavior and user value.\n- Preserve uncertainty explicitly; do not invent missing product decisions.\n- Prefer the smallest vertical slice that proves a complete path through the system.\n- Slice by integration-visible value and risk, not by numeric story budgets.\n- Split a story when it contains separable user-visible/integration-visible outcomes, independent risk seams, or hard dependency boundaries that can each be proven by distinct behavioral tests.\n- Bundle work into the same story when splitting would create micro-stories, docs/package-metadata-only tasks, duplicate validation-only paths, or horizontal setup/refactor/test-only tasks that are not independently observable.\n- Use tracer bullets to de-risk integration seams early with thin end-to-end behavior.\n- Every implementation story must begin with a behavioral red test that fails for the right reason.\n- The green phase should add only enough production code to pass the focused red test.\n- Refactor only after focused tests pass, and keep refactors scoped to the feature.\n- Dependencies are hard dependencies only: story B depends on story A when B cannot pass its own tests until A is complete.\n\n## Review scoring\n\nScore on a 1-5 integer scale:\n\n- 5: excellent; strongly aligned with TDD, cost-aware vertical slicing, tracer-bullet risk reduction, dependency clarity, and local architecture.\n- 4: good; acceptable with only minor non-blocking improvements.\n- 3: incomplete; useful but needs material changes before continuing.\n- 2: poor; misses core methodology or important requirements.\n- 1: unusable; unsafe, incoherent, or not traceable to the feature.\n\nScores of 4 or 5 pass. Scores below 4 must list specific `requiredImprovements` so the next attempt can address them directly.\n";
597
459
 
598
460
  // src/feature-implementation/shared/implementation-doc-format.md?raw
599
- var implementation_doc_format_default = "# Implementation doc format\n\n`implementation-doc.md` converts `feature-doc.md` into implementation-ready stories.\n\nUse this structure:\n\n```markdown\n# Implementation Plan: <Feature Name>\n\n## Overview for planner/reviewer\n\nOptional high-level planning notes that do not need to be seen by story implementers.\n\n<context>\n\n## Shared Implementation Context\n\nPut every cross-story detail that an implementer may need here: outcome and constraints, architecture/integration notes, tracer-bullet strategy, repository conventions, shared validation commands, edge cases, assumptions, and hard dependency graph notes.\n\nAnything inside this balanced `<context>` block is prepended programmatically to every story by the split-stories step.\n\n</context>\n\n<!-- trailstep-story-boundary -->\n\n### Story 001: <title>\n\nDependencies: none\n\n#### Goal\n\n#### User-Visible / Integration-Visible Slice\n\n#### Acceptance Criteria\n\n#### Red Phase\n\n- Test file:\n- Test case:\n- Expected failing assertion:\n\n#### Green Phase\n\n#### Refactor Phase\n\n#### Validation Commands\n\n#### Notes for Implementer\n\n<!-- trailstep-story-boundary -->\n\n### Story 002: <title>\n\nDependencies: Story 001\n\n...\n```\n\nRules:\n\n- Text above the first `<!-- trailstep-story-boundary -->` is never read by an implementer unless it is inside a balanced `<context>` ... `</context>` block. The split-stories step prepends all context blocks to every story.\n- If a detail applies to most/all stories, put it in `<context>`; if it applies only to selected stories, repeat it inside those stories. No story-critical detail may live only in non-context overview text.\n- Every `<context>` marker must have a matching `</context>` marker, and context blocks should be outside story bodies unless there is a deliberate reason to prepend that block to every story.\n- Every story must start right after a `<!-- trailstep-story-boundary -->` line, on its own line, with nothing else on that line. Splitting is mechanical and depends on this exact marker \u2014 do not use markdown headings alone to separate stories, and do not add or omit a boundary marker except between/before stories.\n- Stories must be topologically ordered.\n- Every story must be implementation-ready and self-contained, written as instructions to an implementer who will not see this file, only their own story's content.\n- Avoid horizontal stories unless they are embedded in an observable vertical slice.\n- Name concrete files/tests/commands when they can be inferred from the repository.\n- Preserve uncertainty as assumptions or blocked states instead of guessing.\n";
461
+ var implementation_doc_format_default = "# Implementation doc format\n\n`implementation-doc.md` converts `feature-doc.md` into implementation-ready stories.\n\nUse this structure:\n\n```markdown\n# Implementation Plan: <Feature Name>\n\n## Overview for planner/reviewer\n\nOptional high-level planning notes that do not need to be seen by story implementers.\n\n<context>\naudience: implementer\nstories: all\nphases: deterministic-context-preflight\n\n## Shared Implementer Exploration Context\n\nPut only cross-story details that an implementer needs during story exploration: outcome constraints, architecture/integration notes, repository conventions, shared validation command hints, assumptions, and hard dependency graph notes. Do not put reviewer-only instructions, full implementation-doc process guidance, or unrelated story details here.\n\nScoped context blocks are stored separately from story bodies. They are not prepended to every split story. The router passes matching implementer context only to the narrow phase that needs it.\n\n</context>\n\n<!-- trailstep-story-boundary -->\n\n### Story 001: <title>\n\nDependencies: none\n\n#### Goal\n\n#### User-Visible / Integration-Visible Slice\n\n#### Acceptance Criteria\n\n#### Red Phase\n\n- Test file:\n- Test case:\n- Expected failing assertion:\n\n#### Green Phase\n\n#### Refactor Phase\n\n#### Validation Commands\n\n#### Notes for Implementer\n\n<!-- trailstep-story-boundary -->\n\n### Story 002: <title>\n\nDependencies: Story 001\n\n...\n```\n\nRules:\n\n- Text above the first `<!-- trailstep-story-boundary -->` is never part of an active story body. Only scoped `<context>` ... `</context>` blocks with recognized metadata can be selected as separate phase context.\n- Context blocks must begin with metadata lines before the first blank line. Recognized keys are `audience: implementer|reviewer|all`, `stories: all|Story 001|Story 001: Title`, and `phases: all|deterministic-context-preflight|explore-story|write-red-tests|implement-green|validate-story|review-story-implementation`.\n- Unscoped context blocks are ignored instead of being blindly prepended to every story. If a detail applies to only one story, prefer putting it directly in that story. No story-critical detail may live only in non-context overview text.\n- Every `<context>` marker must have a matching `</context>` marker. Context markers are recognized only when the marker is the sole non-whitespace content on its line; inline mentions of `<context>` in prose are ignored by the splitter.\n- Every story must start right after a `<!-- trailstep-story-boundary -->` line, on its own line, with nothing else on that line. Splitting is mechanical and depends on this exact marker \u2014 do not use markdown headings alone to separate stories, and do not add or omit a boundary marker except between/before stories.\n- Stories must be topologically ordered.\n- Every story must be implementation-ready and self-contained, written as instructions to an implementer who will not see this file, only their own story's content.\n- Avoid horizontal stories unless they are embedded in an observable vertical slice.\n- Split versus bundle by delivery value, risk, and validation distinctness, not by numeric story budgets.\n- Split a candidate story when it contains separable integration-visible outcomes, independent high-risk seams, or true hard dependency boundaries that can each be proven by a distinct behavioral red test.\n- Bundle small changes when splitting would produce micro-stories, duplicate validation paths, docs/package-metadata-only stories, package metadata wiring without behavior, or horizontal setup/refactor/test-only work that is not independently observable.\n- Keep slicing rationale in the overview or reviewer context only; do not put split/bundle justification inside story bodies shown to implementers.\n- Name concrete files/tests/commands when they can be inferred from the repository.\n- Preserve uncertainty as assumptions or blocked states instead of guessing.\n";
600
462
 
601
- // src/feature-implementation/review-implementation-doc/prompt.ts
602
- var fragments3 = {
463
+ // src/feature-implementation/slice-implementation-stories/prompt.ts
464
+ var fragments = {
603
465
  methodology: feature_methodology_default.trimEnd(),
604
466
  implementationDocFormat: implementation_doc_format_default.trimEnd()
605
467
  };
606
- function reviewImplementationDocPrompt({
468
+ function sliceImplementationStoriesPrompt({
469
+ input
470
+ }) {
471
+ return promptSections(
472
+ fragments.methodology,
473
+ fragments.implementationDocFormat,
474
+ section("Feature doc", input.featureDoc.content),
475
+ section("Reviewed implementation strategy", input.implementationStrategy.content),
476
+ section(
477
+ "Task",
478
+ "Generate only the implementation-ready story document to be mechanically split for execution. Consume the feature doc and reviewed strategy, but do not copy broad architecture/risk planning into overview prose that implementers will not see. Use `<!-- trailstep-story-boundary -->` only to delimit story bodies. Keep each story self-contained for its implementer; put genuinely shared implementer-only phase context in balanced `<context>` blocks with metadata. Do not add a separate strategy section, reviewer notes, or non-story planning artifact content."
479
+ )
480
+ );
481
+ }
482
+
483
+ // src/feature-implementation/slice-implementation-stories/step.ts
484
+ var sliceImplementationStoriesStep = step3({ id: "slice-implementation-stories" }).prompt(sliceImplementationStoriesPrompt, {
485
+ agent: "slicer",
486
+ output: Document
487
+ }).do(async (implementationDoc, input) => {
488
+ await state3.set("implementationDoc", implementationDoc);
489
+ return splitImplementationStoriesStep({ featureDoc: input.featureDoc, implementationDoc });
490
+ });
491
+
492
+ // src/feature-implementation/review-implementation-strategy/prompt.ts
493
+ import { list, promptSections as promptSections2, section as section2 } from "@trailstep/authoring";
494
+
495
+ // src/feature-implementation/shared/implementation-strategy-format.md?raw
496
+ var implementation_strategy_format_default = "# Implementation strategy format\n\n`implementation-strategy.md` captures architecture and risk planning before executable story slicing.\n\nUse this structure:\n\n```markdown\n# Implementation Strategy: <Feature Name>\n\n## Objective and Scope\n\n## Architecture Approach\n\n## Key Files and Integration Points\n\n## Risk Plan\n\n## Testing Strategy\n\n## Slicing Guidance\n```\n\nRules:\n\n- Do not include `<!-- trailstep-story-boundary -->` or any other TrailStep story boundary markup.\n- Do not write executable story bodies in this artifact.\n- Focus on architecture, risk, sequencing rationale, tracer-bullet path, hard dependencies, and validation strategy.\n- Preserve uncertainty as assumptions or explicit risks instead of guessing.\n";
497
+
498
+ // src/feature-implementation/review-implementation-strategy/prompt.ts
499
+ var fragments2 = {
500
+ methodology: feature_methodology_default.trimEnd(),
501
+ implementationStrategyFormat: implementation_strategy_format_default.trimEnd()
502
+ };
503
+ function reviewImplementationStrategyPrompt({
607
504
  input
608
505
  }) {
609
- const reviewCriteria = list2([
506
+ const reviewCriteria = list([
610
507
  "traceability to the feature doc",
611
- "strict behavioral-red TDD story design",
612
- "vertical slices rather than horizontal tasks",
508
+ "sound architecture and integration planning",
509
+ "explicit risk and uncertainty handling",
613
510
  "a clear tracer-bullet strategy",
614
- "hard dependency labels and topological ordering",
615
- "acceptance criteria and validation commands",
616
- "story instructions self-contained enough for an implementer who will only see one story plus the prepended shared context at a time",
617
- "correct use of balanced `<context>` ... `</context>` blocks for shared implementer context",
618
- "correct use of the `<!-- trailstep-story-boundary -->` marker between stories, and that no story-critical detail lives only in non-context overview text above the first marker"
511
+ "hard dependency reasoning without over-constraining",
512
+ "testing and validation strategy",
513
+ "useful slicing guidance for a later story slicer",
514
+ "absence of `<!-- trailstep-story-boundary -->` markers or executable story bodies"
619
515
  ]);
620
- return promptSections3(
621
- fragments3.methodology,
622
- fragments3.implementationDocFormat,
623
- section3("Feature doc", input.featureDoc.content),
624
- section3("Implementation doc under review", input.implementationDoc.content),
625
- section3(
516
+ return promptSections2(
517
+ fragments2.methodology,
518
+ fragments2.implementationStrategyFormat,
519
+ section2("Feature doc", input.featureDoc.content),
520
+ section2("Implementation strategy under review", input.implementationStrategy.content),
521
+ section2(
626
522
  "Task",
627
- `Critically review the implementation doc above against the feature doc and the methodology. Review for:
523
+ `Critically review the implementation strategy above against the feature doc and the methodology. Review for:
628
524
 
629
525
  ${reviewCriteria}
630
526
 
631
- Do not edit the implementation doc. Respond only with the structured review.`
527
+ Do not edit the strategy. Respond only with the structured review.`
632
528
  )
633
529
  );
634
530
  }
635
531
 
636
- // src/feature-implementation/review-implementation-doc/step.ts
637
- var reviewImplementationDocStep = step5({ id: "review-implementation-doc" }).prompt(reviewImplementationDocPrompt, {
532
+ // src/feature-implementation/review-implementation-strategy/step.ts
533
+ var reviewImplementationStrategyStep = step4({ id: "review-implementation-strategy" }).prompt(reviewImplementationStrategyPrompt, {
638
534
  agent: "reviewer",
639
535
  output: reviewOutput
640
536
  }).do((review, input) => {
641
537
  if (reviewPasses(review)) {
642
- return splitImplementationStoriesStep({ implementationDoc: input.implementationDoc });
538
+ return sliceImplementationStoriesStep({
539
+ featureDoc: input.featureDoc,
540
+ implementationStrategy: input.implementationStrategy
541
+ });
643
542
  }
644
543
  if (input.attempt >= MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS) {
645
- return fail5({
646
- code: "implementation_doc_review_exhausted",
647
- message: `implementation-doc.md failed review ${MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS} times in a row (last score ${review.score}/5).`,
648
- details: { review, implementationDocPath: input.implementationDoc.path }
544
+ return fail3({
545
+ code: "implementation_strategy_review_exhausted",
546
+ message: `implementation-strategy.md failed review ${MAX_IMPLEMENTATION_DOC_REVIEW_ATTEMPTS} times in a row (last score ${review.score}/5).`,
547
+ details: { review, implementationStrategyPath: input.implementationStrategy.path }
649
548
  });
650
549
  }
651
- return createOrImproveImplementationDocStep({
550
+ return createOrImproveImplementationStrategyStep({
652
551
  featureDoc: input.featureDoc,
653
552
  previousReview: review,
654
553
  attempt: input.attempt + 1
655
554
  });
656
555
  });
657
556
 
658
- // src/feature-implementation/create-or-improve-implementation-doc/prompt.ts
659
- import { list as list3, promptSections as promptSections4, section as section4 } from "@trailstep/authoring";
660
- var fragments4 = {
557
+ // src/feature-implementation/create-or-improve-implementation-strategy/prompt.ts
558
+ import { list as list2, promptSections as promptSections3, section as section3 } from "@trailstep/authoring";
559
+
560
+ // src/feature-implementation/shared/project-architecture-guidance.md?raw
561
+ var project_architecture_guidance_default = "# Architecture awareness\n\nBefore naming files, choosing package/module boundaries, picking integration seams, or ordering stories/dependencies, ground the decision in the target project's own conventions rather than inventing structure:\n\n- Look for and read the project's own architecture/convention docs at its root and nearby \u2014 `CLAUDE.md`, `AGENTS.md`, `.pi/rules/`, ADRs, architecture docs, or an equivalent README section.\n- Prefer existing module/package boundaries, naming conventions, and file layout patterns already established in the project over introducing new ones.\n- When the project's own guidance is silent or ambiguous on a non-trivial integration choice, say so explicitly as an assumption rather than silently picking one convention over another.\n";
562
+
563
+ // src/feature-implementation/create-or-improve-implementation-strategy/prompt.ts
564
+ var fragments3 = {
661
565
  methodology: feature_methodology_default.trimEnd(),
662
566
  architectureGuidance: project_architecture_guidance_default.trimEnd(),
663
- implementationDocFormat: implementation_doc_format_default.trimEnd()
567
+ implementationStrategyFormat: implementation_strategy_format_default.trimEnd()
664
568
  };
665
- function createOrImproveImplementationDocPrompt({
569
+ function createOrImproveImplementationStrategyPrompt({
666
570
  input
667
571
  }) {
668
- const taskBody = input.previousReview === void 0 ? "Create `implementation-doc.md` from the feature doc above, following the format exactly enough that stories can later be split mechanically on the `<!-- trailstep-story-boundary -->` marker and shared context can be prepended from balanced `<context>` blocks." : promptSections4(
669
- `This is improvement attempt ${input.attempt}. A previous review scored this plan ${input.previousReview.score}/5: ${input.previousReview.summary}`,
572
+ const taskBody = input.previousReview === void 0 ? "Create `implementation-strategy.md` from the feature doc above. This is architecture/risk planning only, not executable story generation." : promptSections3(
573
+ `This is improvement attempt ${input.attempt}. A previous review scored this strategy ${input.previousReview.score}/5: ${input.previousReview.summary}`,
670
574
  `Required improvements to address, without discarding what already works:
671
575
 
672
- ${list3(input.previousReview.requiredImprovements)}`
576
+ ${list2(input.previousReview.requiredImprovements)}`
673
577
  );
674
- return promptSections4(
675
- fragments4.methodology,
676
- fragments4.architectureGuidance,
677
- fragments4.implementationDocFormat,
678
- section4("Feature doc", input.featureDoc.content),
679
- section4(
578
+ return promptSections3(
579
+ fragments3.methodology,
580
+ fragments3.architectureGuidance,
581
+ fragments3.implementationStrategyFormat,
582
+ section3("Feature doc", input.featureDoc.content),
583
+ section3(
680
584
  "Task",
681
585
  `${taskBody}
682
586
 
683
- Design stories around strict behavioral-red TDD, vertical slicing, tracer-bullet methodology, and hard dependency labels only. Topologically order stories by dependency. Put shared implementation details in balanced \`<context>\` ... \`</context>\` blocks so split-stories prepends them to every story; repeat details inside individual stories when only selected stories need them. Write every story as implementation-ready instructions for an implementer who will only see that story plus the prepended context.`
587
+ Plan the implementation architecture, risks, tracer-bullet path, hard dependencies, test strategy, and slicing guidance. Do not include story boundary markers and do not write full story bodies; a separate slicer will turn this strategy into implementation-ready stories.`
684
588
  )
685
589
  );
686
590
  }
687
591
 
688
- // src/feature-implementation/create-or-improve-implementation-doc/step.ts
689
- var createOrImproveImplementationDocStep = step6({
690
- id: "create-or-improve-implementation-doc"
691
- }).prompt(createOrImproveImplementationDocPrompt, {
592
+ // src/feature-implementation/create-or-improve-implementation-strategy/step.ts
593
+ var createOrImproveImplementationStrategyStep = step5({
594
+ id: "create-or-improve-implementation-strategy"
595
+ }).prompt(createOrImproveImplementationStrategyPrompt, {
692
596
  agent: "planner",
693
- output: Document
694
- }).do(async (implementationDoc, input) => {
695
- await state4.set("implementationDoc", implementationDoc);
696
- return reviewImplementationDocStep({
597
+ output: Document2
598
+ }).do(async (implementationStrategy, input) => {
599
+ await state4.set("implementationStrategy", implementationStrategy);
600
+ return reviewImplementationStrategyStep({
697
601
  featureDoc: input.featureDoc,
698
- implementationDoc,
602
+ implementationStrategy,
699
603
  attempt: input.attempt
700
604
  });
701
605
  });
702
606
 
703
607
  // src/feature-implementation/create-feature-doc/prompt.ts
704
- import { promptSections as promptSections5, section as section5 } from "@trailstep/authoring";
608
+ import { promptSections as promptSections4, section as section4 } from "@trailstep/authoring";
705
609
 
706
610
  // src/feature-implementation/shared/feature-doc-format.md?raw
707
- var feature_doc_format_default = "# Feature doc format\n\n`feature-doc.md` is an intermediate product-quality feature description used as input to implementation planning.\n\nUse this structure:\n\n```markdown\n# <Feature Name>\n\n## Outcome\n\n## Users / Jobs / Pain\n\n## Proposed Behavior\n\n## Non-Goals\n\n## Examples and Acceptance Criteria\n\n## Edge Cases and Failure Modes\n\n## Affected Areas and Integration Points\n\n## Testing Expectations\n\n## Documentation / Migration / Rollout Notes\n\n## Open Questions and Assumptions\n```\n\nThe document should be detailed enough that another agent can plan implementation without reading the original conversation, while still preserving important uncertainty.\n";
611
+ var feature_doc_format_default = "# Feature doc format\n\n`feature-doc.md` is an intermediate, scope-preserving feature description used as input to implementation planning. It should distill the real request without turning it into an idealized product specification.\n\nUse this structure:\n\n```markdown\n# <Feature Name>\n\n## Must-Have Outcome\n\nSummarize the smallest confirmed outcome the user actually requested. Prefer the user's concrete wording and avoid expanding into adjacent product vision.\n\n## Users / Jobs / Pain\n\n## In-Scope Proposed Behavior\n\nDescribe only behavior needed for the must-have outcome. If something seems useful but was not requested or is uncertain, keep it out of this section.\n\n## Explicit Non-Goals\n\nList non-goals, exclusions, and scope limits explicitly stated by the conversation. Include strongly implied exclusions only when tied to clear evidence. If none were stated, say so instead of inventing exclusions.\n\n## Examples and Acceptance Criteria\n\n## Edge Cases and Failure Modes\n\n## Affected Areas and Integration Points\n\n## Testing Expectations\n\n## Documentation / Migration / Rollout Notes\n\n## Assumptions and Open Questions\n\nCapture missing product decisions, uncertain details, and assumptions required for planning. Do not resolve uncertainty by inventing answers.\n\n## Conversation Context Worth Preserving\n\nRecord constraints, rationale, terminology, user preferences, tradeoffs, and notable uncertainty that a planner or implementer should know without rereading the full conversation.\n\n## Optional / Future Ideas\n\nList explicitly mentioned nice-to-haves, possible follow-ups, or adjacent ideas separately from the current implementation scope. These ideas are not acceptance criteria unless the conversation explicitly makes them must-haves.\n\n## Structured Scope Facts\n\nProvide a compact, planning-friendly fact list without introducing new scope. Use `- Key: value` bullets for confirmed actors, in-scope surfaces, data/entities, integrations, constraints, explicit exclusions, and open decisions that later planning should preserve mechanically.\n```\n\nThe document should be detailed enough that another agent can plan implementation without reading the original conversation, while still preserving important uncertainty and scope boundaries.\n";
708
612
 
709
613
  // src/feature-implementation/create-feature-doc/prompt.ts
710
- var fragments5 = {
614
+ var fragments4 = {
711
615
  methodology: feature_methodology_default.trimEnd(),
712
616
  featureDocFormat: feature_doc_format_default.trimEnd()
713
617
  };
714
618
  function createFeatureDocPrompt({ input }) {
715
- return promptSections5(
716
- fragments5.methodology,
717
- fragments5.featureDocFormat,
718
- section5("Conversation / feature request", input.conversation),
719
- section5(
619
+ return promptSections4(
620
+ fragments4.methodology,
621
+ fragments4.featureDocFormat,
622
+ section4("Conversation / feature request", input.conversation),
623
+ section4(
624
+ "Distillation guardrails",
625
+ [
626
+ "Treat the conversation/request as evidence, not as permission to finish a product vision.",
627
+ "Preserve the user's actual must-have outcome, explicit constraints, uncertainty, and explicit non-goals; include implied scope limits only when tied to clear evidence.",
628
+ "Do not invent scope, acceptance criteria, integrations, personas, product decisions, or polish that the user did not ask for.",
629
+ "When a broad request includes brainstorming, ambitions, or adjacent possibilities, keep the confirmed current scope in the must-have/in-scope sections and move nonessential ideas to Optional / Future Ideas.",
630
+ "Use Structured Scope Facts for compact planning metadata copied or derived directly from the conversation; do not let that fact list add new requirements.",
631
+ "If a detail is unknown, record it as an assumption or open question instead of choosing an answer.",
632
+ "A later planning agent may implement the must-have and in-scope sections; optional/future ideas are context only and must not become story scope unless explicitly promoted by the user."
633
+ ].join("\n")
634
+ ),
635
+ section4(
720
636
  "Task",
721
- 'Write `feature-doc.md` following the format above, based on the conversation/request. Make it detailed enough that another agent can plan implementation without reading the original conversation. Preserve uncertainty explicitly in "Open Questions and Assumptions" \u2014 do not invent missing product decisions.'
637
+ "Write `feature-doc.md` following the format above, based on the conversation/request. Make it detailed enough that another agent can plan implementation without reading the original conversation, while preserving scope boundaries and uncertainty. Do not inflate the request into an idealized product spec."
722
638
  )
723
639
  );
724
640
  }
725
641
 
726
642
  // src/feature-implementation/create-feature-doc/step.ts
727
- var createFeatureDocStep = step7({ id: "create-feature-doc" }).prompt(createFeatureDocPrompt, {
643
+ var createFeatureDocStep = step6({ id: "create-feature-doc" }).prompt(createFeatureDocPrompt, {
728
644
  agent: "featureWriter",
729
- output: Document2
645
+ output: Document3
730
646
  }).do(async (featureDoc) => {
731
647
  await state5.set("featureDoc", featureDoc);
732
- return createOrImproveImplementationDocStep({ featureDoc, attempt: 1 });
648
+ return createOrImproveImplementationStrategyStep({ featureDoc, attempt: 1 });
733
649
  });
734
650
 
735
- // src/feature-implementation/shared/input-schema.ts
736
- import { shape } from "@trailstep/authoring";
737
- var takeItAwayInput = shape({ conversation: "string" });
651
+ // src/feature-implementation/initialize-take-it-away/step.ts
652
+ var initializeTakeItAwayStep = step7({ id: "initialize-take-it-away" }).do(
653
+ async (input) => {
654
+ await state6.set(STORY_STATE_KEYS.workflowOptions, normalizeTakeItAwayWorkflowOptions(input));
655
+ await state6.set(STORY_STATE_KEYS.workflowWarnings, []);
656
+ return createFeatureDocStep(input);
657
+ }
658
+ );
738
659
 
739
660
  // src/grill-it-away/grill/prompt.ts
740
- import { promptSections as promptSections6, section as section6 } from "@trailstep/authoring";
661
+ import { promptSections as promptSections5, section as section5 } from "@trailstep/authoring";
741
662
  function grillPrompt() {
742
- return promptSections6(
743
- section6(
663
+ return promptSections5(
664
+ section5(
744
665
  "Role",
745
666
  "You are the interactive front door for starting a new feature in the current project. Open by asking the user what they want to build."
746
667
  ),
747
- section6(
668
+ section5(
748
669
  "Task",
749
670
  "Keep asking follow-up questions until you are confident you understand the feature request well enough to hand off to a planning agent. There is no fixed checklist of required topics \u2014 use your judgment, and do not end the conversation prematurely just to finish quickly."
750
671
  ),
751
- section6(
672
+ section5(
752
673
  "Output",
753
674
  "Your structured output's `conversation` field must contain the full raw transcript of the conversation so far, not a summary. Preserve as much of the actual back-and-forth as possible."
754
675
  )
@@ -760,13 +681,13 @@ var grillStep = step8({ id: "grill" }).prompt(grillPrompt, {
760
681
  agent: "grillingAgent",
761
682
  mode: "interactive",
762
683
  output: takeItAwayInput
763
- }).do((output) => createFeatureDocStep(output));
684
+ }).do((output) => initializeTakeItAwayStep(output));
764
685
 
765
686
  // src/grill-it-away/workflow.ts
766
687
  var grillItAway = defineWorkflow({
767
688
  id: "grill-it-away",
768
689
  description: "Interactively grills the user until it understands the requested feature.",
769
- inputShape: shape2({}),
690
+ inputShape: shape({}),
770
691
  outputShape: takeItAwayOutput,
771
692
  agents: {
772
693
  grillingAgent: {
@@ -774,26 +695,7 @@ var grillItAway = defineWorkflow({
774
695
  thinking: "medium",
775
696
  description: "Grills the user until it understands the requested feature."
776
697
  },
777
- featureWriter: {
778
- size: "medium",
779
- thinking: "medium",
780
- description: "Turns a conversation/feature request into a standalone feature doc."
781
- },
782
- planner: {
783
- size: "large",
784
- thinking: "high",
785
- description: "Creates or improves the implementation doc: architecture-aware, TDD/vertical-slice/tracer-bullet story design."
786
- },
787
- reviewer: {
788
- size: "large",
789
- thinking: "high",
790
- description: "Reviews isolated implementation docs and story diffs against methodology."
791
- },
792
- implementer: {
793
- size: "medium",
794
- thinking: "medium",
795
- description: "Implements one story at a time using strict behavioral-red TDD."
796
- }
698
+ ...featureImplementationAgents
797
699
  },
798
700
  start() {
799
701
  return grillStep();
@@ -807,30 +709,9 @@ var takeItAway = defineWorkflow2({
807
709
  description: "Turns an already-organic conversation/feature request into a reviewed implementation plan, then implements it story by story.",
808
710
  inputShape: takeItAwayInput,
809
711
  outputShape: takeItAwayOutput,
810
- agents: {
811
- featureWriter: {
812
- size: "medium",
813
- thinking: "medium",
814
- description: "Turns a conversation/feature request into a standalone feature doc."
815
- },
816
- planner: {
817
- size: "large",
818
- thinking: "high",
819
- description: "Creates or improves the implementation doc: architecture-aware, TDD/vertical-slice/tracer-bullet story design."
820
- },
821
- reviewer: {
822
- size: "large",
823
- thinking: "high",
824
- description: "Reviews isolated implementation docs and story diffs against methodology."
825
- },
826
- implementer: {
827
- size: "medium",
828
- thinking: "medium",
829
- description: "Implements one story at a time using strict behavioral-red TDD."
830
- }
831
- },
712
+ agents: featureImplementationAgents,
832
713
  start(input) {
833
- return createFeatureDocStep(input);
714
+ return initializeTakeItAwayStep(input);
834
715
  }
835
716
  });
836
717
  export {