5-phase-workflow 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -101
- package/bin/install.js +168 -176
- package/bin/sync-agents.js +59 -54
- package/docs/workflow-guide.md +35 -79
- package/package.json +2 -2
- package/src/agents/step-executor-agent.md +5 -4
- package/src/agents/step-orchestrator-agent.md +12 -12
- package/src/agents/verification-agent.md +7 -8
- package/src/codex-commands/plan.md +65 -0
- package/src/commands/5/address-review-findings.md +43 -52
- package/src/commands/5/analyze-feature.md +1 -1
- package/src/commands/5/configure.md +75 -179
- package/src/commands/5/eject.md +0 -1
- package/src/commands/5/implement.md +78 -68
- package/src/commands/5/lean-check.md +75 -0
- package/src/commands/5/reconfigure.md +48 -129
- package/src/commands/5/review.md +42 -14
- package/src/commands/5/synchronize-agents.md +1 -1
- package/src/commands/5/update.md +2 -2
- package/src/hooks/dependency-guard.js +150 -0
- package/src/hooks/statusline.js +5 -3
- package/src/references/configure-tables.md +0 -33
- package/src/settings.json +3 -4
- package/src/skills/baseline-conventions/SKILL.md +37 -0
- package/src/skills/cognitive-load/SKILL.md +52 -0
- package/src/skills/configure-agents-md/SKILL.md +72 -0
- package/src/skills/configure-rules/SKILL.md +146 -0
- package/src/skills/documentation-style/SKILL.md +32 -0
- package/src/skills/fowler-code-smells/SKILL.md +25 -0
- package/src/skills/review-style/SKILL.md +142 -0
- package/src/templates/AGENTS.md +31 -31
- package/src/templates/workflow/REVIEW-FINDINGS.md +2 -9
- package/src/workflows/5-implement.js +67 -92
- package/docs/findings.md +0 -361
- package/docs/progress.md +0 -102
- package/src/commands/5/apply-review-findings.md +0 -66
- package/src/commands/5/commit.md +0 -43
- package/src/commands/5/plan.md +0 -189
- package/src/commands/5/reply-pr-comments.md +0 -48
- package/src/commands/5/split.md +0 -86
- package/src/commands/5/triage-pr-comments.md +0 -70
- package/src/hooks/config-guard.js +0 -30
- package/src/skills/configure-docs-index/SKILL.md +0 -243
- package/src/skills/configure-skills/SKILL.md +0 -311
- package/src/skills/generate-readme/EXAMPLES.md +0 -168
- package/src/skills/generate-readme/SKILL.md +0 -122
- package/src/skills/generate-readme/TEMPLATE.md +0 -141
- package/src/skills/use-index/SKILL.md +0 -49
- package/src/templates/ARCHITECTURE.md +0 -33
- package/src/templates/CONCERNS.md +0 -33
- package/src/templates/CONVENTIONS.md +0 -74
- package/src/templates/TESTING.md +0 -24
- package/src/templates/workflow/FIX-PLAN.md +0 -55
- package/src/templates/workflow/PLAN-COMPACT.md +0 -42
- package/src/templates/workflow/PLAN.md +0 -76
- package/src/templates/workflow/REVIEW-SUMMARY.md +0 -53
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export const meta = {
|
|
2
2
|
name: '5-implement',
|
|
3
|
-
description: 'Execute a
|
|
4
|
-
whenToUse: 'Invoked by the /5:implement command when the Workflow tool is available.
|
|
3
|
+
description: 'Execute a full plan: derive steps, run executors in parallel waves, verify. Claude Code only; Codex uses the prose loop.',
|
|
4
|
+
whenToUse: 'Invoked by the /5:implement command for full plans when the Workflow tool is available. Compact plans never reach it; the command implements them inline.',
|
|
5
5
|
phases: [
|
|
6
|
-
{ title: 'Orchestrate', detail: '
|
|
6
|
+
{ title: 'Orchestrate', detail: 'one orchestrator agent derives steps + components' },
|
|
7
7
|
{ title: 'Execute', detail: 'one executor agent per component; parallel components fire together per step' },
|
|
8
8
|
{ title: 'Verify', detail: 'inline when all passed + mechanical, else one verification agent' }
|
|
9
9
|
]
|
|
@@ -15,24 +15,24 @@ export const meta = {
|
|
|
15
15
|
// the canonical human-readable contract; keep these prompts in sync with them.
|
|
16
16
|
//
|
|
17
17
|
// This script has NO filesystem access. It orchestrates agents and RETURNS the
|
|
18
|
-
// final result. The /5:implement command
|
|
19
|
-
//
|
|
18
|
+
// final result. The /5:implement command runs the fast-path build/test check and
|
|
19
|
+
// auto-commit after this returns. Nothing is persisted: a rerun starts over.
|
|
20
20
|
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
// the plan's Component Checklist, so re-derivation is stable by name.
|
|
21
|
+
// Models: haiku for mechanical work, sonnet for ordinary logic, opus at effort
|
|
22
|
+
// medium for complex work and for every retry.
|
|
24
23
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
25
24
|
|
|
26
25
|
const RESULT_SCHEMA = {
|
|
27
26
|
type: 'object',
|
|
28
27
|
additionalProperties: false,
|
|
29
|
-
required: ['status', 'verify'],
|
|
28
|
+
required: ['status', 'verify', 'skipped'],
|
|
30
29
|
properties: {
|
|
31
30
|
status: { enum: ['success', 'failed'] },
|
|
32
31
|
filesCreated: { type: 'array', items: { type: 'string' } },
|
|
33
32
|
filesModified: { type: 'array', items: { type: 'string' } },
|
|
34
33
|
verify: { enum: ['passed', 'failed', 'skipped'] },
|
|
35
34
|
deviations: { type: 'string', description: 'none or a brief list' },
|
|
35
|
+
skipped: { type: 'string', description: 'none, or what was deliberately not built and when to add it' },
|
|
36
36
|
error: { type: 'string', description: 'none or an error description' }
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -40,13 +40,13 @@ const RESULT_SCHEMA = {
|
|
|
40
40
|
const VERIFICATION_SCHEMA = {
|
|
41
41
|
type: 'object',
|
|
42
42
|
additionalProperties: false,
|
|
43
|
-
required: ['status', 'completeness', 'infrastructure', '
|
|
43
|
+
required: ['status', 'completeness', 'infrastructure', 'scope'],
|
|
44
44
|
properties: {
|
|
45
45
|
status: { enum: ['passed', 'partial', 'failed'] },
|
|
46
46
|
completeness: { enum: ['passed', 'partial', 'failed'] },
|
|
47
47
|
infrastructure: { enum: ['passed', 'failed'] },
|
|
48
48
|
acceptanceCriteria: { type: 'string', description: 'satisfied/total, e.g. "3/3"' },
|
|
49
|
-
|
|
49
|
+
scope: { enum: ['passed', 'drift'] },
|
|
50
50
|
commands: {
|
|
51
51
|
type: 'array',
|
|
52
52
|
items: {
|
|
@@ -79,7 +79,7 @@ const STATE_SCHEMA = {
|
|
|
79
79
|
number: { type: 'integer' },
|
|
80
80
|
name: { type: 'string' },
|
|
81
81
|
mode: { enum: ['parallel', 'sequential'] },
|
|
82
|
-
model: { enum: ['haiku', 'sonnet'] },
|
|
82
|
+
model: { enum: ['haiku', 'sonnet', 'opus'] },
|
|
83
83
|
components: { type: 'array', items: { type: 'string' } }
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -95,7 +95,7 @@ const STATE_SCHEMA = {
|
|
|
95
95
|
action: { enum: ['create', 'modify', 'delete', 'rename'] },
|
|
96
96
|
step: { type: 'integer' },
|
|
97
97
|
mode: { enum: ['parallel', 'sequential'] },
|
|
98
|
-
model: { enum: ['haiku', 'sonnet'] },
|
|
98
|
+
model: { enum: ['haiku', 'sonnet', 'opus'] },
|
|
99
99
|
file: { type: 'string' },
|
|
100
100
|
sourceFile: { type: ['string', 'null'] },
|
|
101
101
|
description: { type: 'string' },
|
|
@@ -128,17 +128,15 @@ function orchestratorPrompt(a) {
|
|
|
128
128
|
|
|
129
129
|
Feature: ${a.feature}
|
|
130
130
|
Plan: ${a.paths.plan}
|
|
131
|
-
Codebase scan: ${a.paths.scan} (read only if present)
|
|
132
131
|
Config: ${a.paths.config} (read only if present)
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
- COPY EVERY COMPONENT NAME VERBATIM from the plan's Component Checklist. Resume matches components by name across runs, so a renamed or reformatted name makes completed work run twice.
|
|
133
|
+
The plan has no fixed shape. Derive the components from whatever it describes: one component per target file (or tightly coupled file group), named in short kebab-case, then turn them into steps + components:
|
|
136
134
|
- Group independent components into one step with mode "parallel"; use "sequential" only when components touch the same file, one imports another, or there is an explicit dependency.
|
|
137
135
|
- Prefer fewer steps. Tests run after the components they validate.
|
|
138
|
-
- model "haiku"
|
|
139
|
-
- 1-2 high-signal patternRefs per component with line ranges or symbols so executors avoid whole-file reads.
|
|
136
|
+
- model "haiku" for mechanical work (UI tweaks, tests, docs, config, simple single-file edits); "sonnet" for ordinary logic; "opus" only for complex logic, cross-module behavior, security/auth, data migrations, or public API contracts.
|
|
137
|
+
- 1-2 high-signal patternRefs per component with line ranges or symbols so executors avoid whole-file reads. Take them from the plan where it names patterns, else find them with Glob/Grep.
|
|
140
138
|
- verifyCommands: narrowest relevant check first, then project build/test.
|
|
141
|
-
- Exclude
|
|
139
|
+
- Exclude work the plan defers or rules out. Represent every other component once. dependsOn must reference existing component names.
|
|
142
140
|
|
|
143
141
|
Return an object matching the schema (steps[] and pendingComponents[]).`
|
|
144
142
|
}
|
|
@@ -146,6 +144,9 @@ Return an object matching the schema (steps[] and pendingComponents[]).`
|
|
|
146
144
|
function executorPrompt(c, a, opts) {
|
|
147
145
|
const refs = (c.patternRefs || []).map(r => ` - ${r.file}${r.read ? ` (${r.read})` : ''}${r.reason ? ` — ${r.reason}` : ''}`).join('\n') || ' - none'
|
|
148
146
|
const verify = (c.verifyCommands || []).join(' && ') || 'infer the narrowest relevant test/build command'
|
|
147
|
+
const standards = a.standards || {}
|
|
148
|
+
const repoStandards = (standards.repo || []).map(f => ` - ${f}`).join('\n') || ' - none found'
|
|
149
|
+
const styles = (standards.styles || []).map(f => ` - ${f}`).join('\n') || ' - none found'
|
|
149
150
|
const retry = opts && opts.retry
|
|
150
151
|
? '\nThis is a RETRY of a failed attempt — read the error context carefully and correct the root cause.'
|
|
151
152
|
: ''
|
|
@@ -156,23 +157,27 @@ Action: ${c.action}
|
|
|
156
157
|
Target file: ${c.file}${c.sourceFile ? `\nSource file (rename from): ${c.sourceFile}` : ''}
|
|
157
158
|
Intent: ${c.description || ''}
|
|
158
159
|
|
|
159
|
-
Read only these pattern references (ranges/symbols), plus the target file for modify/rename:
|
|
160
|
+
Read only these pattern references (ranges/symbols), plus the target file for modify/rename — and for create when it already exists:
|
|
160
161
|
${refs}
|
|
161
162
|
|
|
162
|
-
|
|
163
|
+
Standards — read the parts that apply to your target and follow them. Repository standards win over the style skills.
|
|
164
|
+
Repository standards:
|
|
165
|
+
${repoStandards}
|
|
166
|
+
Style skills:
|
|
167
|
+
${styles}
|
|
168
|
+
|
|
169
|
+
Rules: Nothing is persisted between runs, so your work may already be done: for \`create\`, read the target first when it exists — if it already fulfils the intent, report success with no change; otherwise complete it, never overwrite it blindly. Smallest coherent change; reuse what already exists in this codebase before writing new code, then prefer stdlib, native platform features, and installed dependencies; touch only the target file and what it strictly needs (its test, an import site); mirror existing naming/exports/layout/tests; follow any local skill or rule; add no new dependency; no abstraction or error handling beyond what the intent requires. Fix mechanical issues you cause (imports, types, lint). STOP and report failed for missing dependencies, unplanned auth/schema/API/contract changes, or unclear product decisions. If verify fails only from pre-existing unrelated issues, report that as a deviation with the exact evidence — do not mark the component failed. Do not make more than three attempts on the same failing issue.
|
|
163
170
|
|
|
164
171
|
Then run: ${verify}
|
|
165
172
|
|
|
166
|
-
Return structured output: status, filesCreated, filesModified, verify, deviations (none or brief), error (none or description). Keep it concise — no logs or diffs unless failed.`
|
|
173
|
+
Return structured output: status, filesCreated, filesModified, verify, deviations (none or brief), skipped (none, or what you deliberately did not build — add when ...), error (none or description). Keep it concise — no logs or diffs unless failed.`
|
|
167
174
|
}
|
|
168
175
|
|
|
169
|
-
function verifyPrompt(a, results
|
|
176
|
+
function verifyPrompt(a, results) {
|
|
170
177
|
const ran = results.flatMap(s => s.items.map(i =>
|
|
171
|
-
` step ${s.step} ${i.component.name}: ${i.result ? i.result.status : 'no result'} (verify ${i.result ? i.result.verify : '?'})${i.escalated ? ' [escalated to
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
.map(n => ` ${n}: completed in a prior session`)
|
|
175
|
-
const summary = [...ran, ...prior].join('\n') || ' (no components ran this invocation)'
|
|
178
|
+
` step ${s.step} ${i.component.name}: ${i.result ? i.result.status : 'no result'} (verify ${i.result ? i.result.verify : '?'})${i.escalated ? ' [escalated to opus]' : ''}`))
|
|
179
|
+
const summary = ran.join('\n') || ' (no components ran)'
|
|
180
|
+
const repoStandards = ((a.standards && a.standards.repo) || []).join(', ') || 'none found'
|
|
176
181
|
const baseline = (a.baseline && a.baseline.length)
|
|
177
182
|
? a.baseline.map(b => ` ${b.command || b.details && b.details.command || '?'}: ${b.status}${b.summary ? ` — ${b.summary}` : ''}`).join('\n')
|
|
178
183
|
: ' (none recorded)'
|
|
@@ -181,6 +186,7 @@ function verifyPrompt(a, results, resumeDone) {
|
|
|
181
186
|
Feature: ${a.feature}
|
|
182
187
|
Plan: ${a.paths.plan}
|
|
183
188
|
Config: ${a.paths.config} (read only if present)
|
|
189
|
+
Repository standards: ${repoStandards}
|
|
184
190
|
|
|
185
191
|
Baseline (pre-change) command results — treat any failure listed here as PRE-EXISTING, not caused by this change:
|
|
186
192
|
${baseline}
|
|
@@ -188,40 +194,16 @@ ${baseline}
|
|
|
188
194
|
Component results:
|
|
189
195
|
${summary}
|
|
190
196
|
|
|
191
|
-
Checks: completeness (every planned component done, acceptance criteria addressed); files exist for create/modify, rename moved correctly, delete removed; build + test pass (reuse the baseline and component results above when they already prove status — rerun only the commands whose inputs changed, not identical passing ones);
|
|
197
|
+
Checks — only whether the plan was built and works; code quality and correctness belong to /5:review, which runs next: completeness (every planned component done, and any acceptance criteria the plan states addressed); files exist for create/modify, rename moved correctly, delete removed; build + test pass (reuse the baseline and component results above when they already prove status — rerun only the commands whose inputs changed, not identical passing ones); scope (run \`git status --short\` and \`git diff HEAD --stat\`, and read untracked files directly since \`git diff HEAD\` omits them: every changed file traces to a planned component — its target, its test, an import site, or a file a repository standard requires (such as a changelog entry); flag files outside the plan, work the plan rules out or defers, new dependencies no component requires, and unrequested abstractions — report drift only in scope; it does not change status). Prefer changed files over broad scanning.
|
|
192
198
|
|
|
193
|
-
Return structured output: status, completeness, infrastructure, acceptanceCriteria ("satisfied/total"),
|
|
199
|
+
Return structured output: status, completeness, infrastructure, acceptanceCriteria ("satisfied/total"), scope, commands[], failures[] (one "drift: {path} — {why}" entry per drift finding).`
|
|
194
200
|
}
|
|
195
201
|
|
|
196
202
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
197
203
|
|
|
198
|
-
//
|
|
199
|
-
function
|
|
200
|
-
|
|
201
|
-
name: c.name,
|
|
202
|
-
action: c.action || 'modify',
|
|
203
|
-
step: 1,
|
|
204
|
-
mode: 'parallel',
|
|
205
|
-
model: c.model || 'haiku',
|
|
206
|
-
file: c.file,
|
|
207
|
-
sourceFile: c.sourceFile || null,
|
|
208
|
-
description: c.description || '',
|
|
209
|
-
dependsOn: c.dependsOn || [],
|
|
210
|
-
patternRefs: c.patternRefs || [],
|
|
211
|
-
verifyCommands: c.verifyCommands || [],
|
|
212
|
-
notes: []
|
|
213
|
-
}))
|
|
214
|
-
// If two components write the same file or one depends on another, fall back to sequential.
|
|
215
|
-
const files = norm.map(c => c.file)
|
|
216
|
-
const fileConflict = new Set(files).size !== files.length
|
|
217
|
-
const hasDep = norm.some(c => c.dependsOn && c.dependsOn.length)
|
|
218
|
-
const mode = (fileConflict || hasDep) ? 'sequential' : 'parallel'
|
|
219
|
-
norm.forEach(c => { c.mode = mode })
|
|
220
|
-
const stepModel = norm.some(c => c.model === 'sonnet') ? 'sonnet' : 'haiku'
|
|
221
|
-
return {
|
|
222
|
-
steps: [{ number: 1, name: 'implement', mode, model: stepModel, components: norm.map(c => c.name) }],
|
|
223
|
-
pendingComponents: norm
|
|
224
|
-
}
|
|
204
|
+
// Model options per tier. Opus runs at medium effort: deep enough for complex work, cheaper than high.
|
|
205
|
+
function modelOpts(model) {
|
|
206
|
+
return model === 'opus' ? { model: 'opus', effort: 'medium' } : { model: model || 'haiku' }
|
|
225
207
|
}
|
|
226
208
|
|
|
227
209
|
async function runStep(step, components, a) {
|
|
@@ -232,28 +214,28 @@ async function runStep(step, components, a) {
|
|
|
232
214
|
if (step.mode === 'parallel' && stepComps.length > 1) {
|
|
233
215
|
// Fire every parallel component together — true concurrency, one wave.
|
|
234
216
|
waveResults = await parallel(stepComps.map(c => () =>
|
|
235
|
-
agent(executorPrompt(c, a), { label: `exec:${c.name}`, phase: 'Execute',
|
|
217
|
+
agent(executorPrompt(c, a), { label: `exec:${c.name}`, phase: 'Execute', ...modelOpts(c.model), schema: RESULT_SCHEMA })
|
|
236
218
|
.then(result => ({ component: c, result }))
|
|
237
219
|
.catch(() => ({ component: c, result: null }))))
|
|
238
220
|
} else {
|
|
239
221
|
waveResults = []
|
|
240
222
|
for (const c of stepComps) {
|
|
241
|
-
const result = await agent(executorPrompt(c, a), { label: `exec:${c.name}`, phase: 'Execute',
|
|
223
|
+
const result = await agent(executorPrompt(c, a), { label: `exec:${c.name}`, phase: 'Execute', ...modelOpts(c.model), schema: RESULT_SCHEMA })
|
|
242
224
|
.catch(() => null)
|
|
243
225
|
waveResults.push({ component: c, result })
|
|
244
226
|
}
|
|
245
227
|
}
|
|
246
228
|
|
|
247
|
-
// Retry failed OR crashed (null) components up to twice, escalating to
|
|
229
|
+
// Retry failed OR crashed (null) components up to twice, escalating to opus.
|
|
248
230
|
// A crashed agent (null result) is retried too — an infra error should not be
|
|
249
231
|
// treated more harshly than a clean structured failure.
|
|
250
232
|
for (const wr of waveResults) {
|
|
251
233
|
let attempt = 1
|
|
252
234
|
while ((wr.result == null || wr.result.status === 'failed') && attempt <= 2) {
|
|
253
|
-
log(`retry ${wr.component.name} (attempt ${attempt + 1}) on
|
|
235
|
+
log(`retry ${wr.component.name} (attempt ${attempt + 1}) on opus`)
|
|
254
236
|
wr.escalated = true
|
|
255
237
|
const retried = await agent(executorPrompt(wr.component, a, { retry: true }),
|
|
256
|
-
{ label: `retry:${wr.component.name}`, phase: 'Execute',
|
|
238
|
+
{ label: `retry:${wr.component.name}`, phase: 'Execute', ...modelOpts('opus'), schema: RESULT_SCHEMA }).catch(() => null)
|
|
257
239
|
if (retried) wr.result = retried
|
|
258
240
|
attempt++
|
|
259
241
|
}
|
|
@@ -265,58 +247,50 @@ async function runStep(step, components, a) {
|
|
|
265
247
|
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
266
248
|
|
|
267
249
|
const a = args || {}
|
|
268
|
-
const resumeDone = new Set(a.completedComponents || [])
|
|
269
250
|
|
|
270
251
|
phase('Orchestrate')
|
|
271
|
-
|
|
272
|
-
let plan
|
|
273
|
-
if (a.isCompact && Array.isArray(a.components) && a.components.length) {
|
|
274
|
-
log(`compact plan: ${a.components.length} component(s), no orchestrator agent`)
|
|
275
|
-
plan = compactSteps(a.components)
|
|
276
|
-
} else {
|
|
277
|
-
plan = await agent(orchestratorPrompt(a), { label: 'orchestrate', phase: 'Orchestrate', schema: STATE_SCHEMA })
|
|
278
|
-
}
|
|
252
|
+
const plan = await agent(orchestratorPrompt(a), { label: 'orchestrate', phase: 'Orchestrate', schema: STATE_SCHEMA })
|
|
279
253
|
const steps = plan.steps
|
|
280
254
|
const components = plan.pendingComponents
|
|
281
255
|
|
|
282
256
|
phase('Execute')
|
|
283
257
|
const results = []
|
|
284
258
|
for (const step of steps) {
|
|
285
|
-
|
|
286
|
-
if (!remaining.length) { log(`step ${step.number} already complete (resume), skipping`); continue }
|
|
287
|
-
const stepResult = await runStep({ ...step, components: remaining.map(c => c.name) }, components, a)
|
|
288
|
-
results.push(stepResult)
|
|
259
|
+
results.push(await runStep(step, components, a))
|
|
289
260
|
}
|
|
290
261
|
|
|
291
262
|
phase('Verify')
|
|
292
263
|
const allItems = results.flatMap(s => s.items)
|
|
293
264
|
const allPassed = allItems.length > 0 && allItems.every(i => i.result && i.result.status === 'success' && i.result.verify !== 'failed')
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
const
|
|
265
|
+
// Mechanical when every component was planned as haiku and none needed a retry; anything
|
|
266
|
+
// else signals reasoning-level work that warrants the full verification agent.
|
|
267
|
+
const usedOpus = components.some(c => c.model === 'opus') || allItems.some(i => i.escalated)
|
|
268
|
+
const mechanical = !usedOpus && components.every(c => (c.model || 'haiku') === 'haiku')
|
|
269
|
+
// Deterministic scope signal: files an executor touched beyond its planned target(s).
|
|
270
|
+
// Tests and import sites are legitimate, so this is reported for the user, never a failure.
|
|
271
|
+
const extraFiles = allItems.flatMap(i => {
|
|
272
|
+
const stripDot = f => f.replace(/^\.\//, '')
|
|
273
|
+
const planned = new Set([i.component.file, i.component.sourceFile].filter(Boolean).map(stripDot))
|
|
274
|
+
const touched = i.result ? [...(i.result.filesCreated || []), ...(i.result.filesModified || [])].map(stripDot) : []
|
|
275
|
+
return touched.filter(f => !planned.has(f)).map(f => ({ component: i.component.name, file: f }))
|
|
276
|
+
})
|
|
277
|
+
if (extraFiles.length) log(`scope: ${extraFiles.length} file(s) touched outside planned targets`)
|
|
297
278
|
let verification
|
|
298
|
-
if (allPassed &&
|
|
299
|
-
log('all components passed + mechanical
|
|
300
|
-
|
|
279
|
+
if (allPassed && mechanical) {
|
|
280
|
+
log('all components passed + mechanical — no verification agent; /5:implement runs build/test and the scope check')
|
|
281
|
+
// No shell access here: /5:implement runs build/test and the scope check itself when it sees inline: true.
|
|
282
|
+
verification = { status: 'passed', completeness: 'passed', infrastructure: 'passed', acceptanceCriteria: 'n/a', commands: [], failures: [], inline: true }
|
|
301
283
|
} else {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
label: 'verify', phase: 'Verify', model: usedSonnet ? 'sonnet' : 'haiku', schema: VERIFICATION_SCHEMA
|
|
284
|
+
verification = await agent(verifyPrompt(a, results), {
|
|
285
|
+
label: 'verify', phase: 'Verify', ...modelOpts(usedOpus ? 'opus' : 'sonnet'), schema: VERIFICATION_SCHEMA
|
|
305
286
|
})
|
|
306
287
|
}
|
|
307
288
|
|
|
308
|
-
//
|
|
309
|
-
const newlyCompleted = allItems.filter(i => i.result && i.result.status === 'success').map(i => i.component.name)
|
|
310
|
-
const completedComponents = [...new Set([...resumeDone, ...newlyCompleted])]
|
|
311
|
-
|
|
312
|
-
// Returned to the /5:implement command, which persists the slim state.json and auto-commits.
|
|
313
|
-
// steps + components are returned for reporting and per-step staging only — they are NOT persisted;
|
|
314
|
-
// the next run re-derives them, and resume matches on completedComponents by name.
|
|
289
|
+
// Returned to the /5:implement command for the fast-path check, per-step staging, and the report.
|
|
315
290
|
return {
|
|
316
291
|
feature: a.feature,
|
|
317
292
|
steps,
|
|
318
293
|
components,
|
|
319
|
-
completedComponents,
|
|
320
294
|
results: results.map(s => ({
|
|
321
295
|
step: s.step,
|
|
322
296
|
name: s.name,
|
|
@@ -324,5 +298,6 @@ return {
|
|
|
324
298
|
items: s.items.map(i => ({ component: i.component.name, file: i.component.file, sourceFile: i.component.sourceFile, escalated: !!i.escalated, result: i.result }))
|
|
325
299
|
})),
|
|
326
300
|
verification,
|
|
301
|
+
extraFiles,
|
|
327
302
|
status: verification.status === 'passed' ? 'completed' : 'failed'
|
|
328
303
|
}
|