@sandrinio/vbounce 1.9.0 → 2.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.
@@ -2,7 +2,11 @@
2
2
 
3
3
  /**
4
4
  * suggest_improvements.mjs
5
- * Generates improvement suggestions from sprint trends + lessons.
5
+ * Generates human-readable improvement suggestions from:
6
+ * 1. Improvement manifest (post_sprint_improve.mjs output)
7
+ * 2. Sprint trends
8
+ * 3. LESSONS.md
9
+ *
6
10
  * Overwrites (not appends) to prevent stale suggestion accumulation.
7
11
  *
8
12
  * Usage:
@@ -14,6 +18,7 @@
14
18
  import fs from 'fs';
15
19
  import path from 'path';
16
20
  import { fileURLToPath } from 'url';
21
+ import { spawnSync } from 'child_process';
17
22
 
18
23
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
24
  const ROOT = path.resolve(__dirname, '..');
@@ -26,41 +31,76 @@ if (!sprintId) {
26
31
 
27
32
  const today = new Date().toISOString().split('T')[0];
28
33
 
29
- // 1. Read trends if available
34
+ // ---------------------------------------------------------------------------
35
+ // 0. Run post_sprint_improve.mjs to generate fresh manifest
36
+ // ---------------------------------------------------------------------------
37
+
38
+ const analyzerScript = path.join(__dirname, 'post_sprint_improve.mjs');
39
+ if (fs.existsSync(analyzerScript)) {
40
+ console.log('Running post-sprint improvement analyzer...');
41
+ const result = spawnSync(process.execPath, [analyzerScript, sprintId], {
42
+ stdio: 'inherit',
43
+ cwd: process.cwd(),
44
+ });
45
+ if (result.status !== 0) {
46
+ console.warn('⚠ Analyzer returned non-zero — continuing with available data.');
47
+ }
48
+ console.log('');
49
+ }
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // 1. Read improvement manifest (from post_sprint_improve.mjs)
53
+ // ---------------------------------------------------------------------------
54
+
55
+ const manifestPath = path.join(ROOT, '.bounce', 'improvement-manifest.json');
56
+ let manifest = null;
57
+ if (fs.existsSync(manifestPath)) {
58
+ try {
59
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
60
+ } catch {
61
+ console.warn('⚠ Could not parse improvement-manifest.json');
62
+ }
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // 2. Read trends if available
67
+ // ---------------------------------------------------------------------------
68
+
30
69
  const trendsFile = path.join(ROOT, '.bounce', 'trends.md');
31
70
  let trendsContent = null;
32
71
  if (fs.existsSync(trendsFile)) {
33
72
  trendsContent = fs.readFileSync(trendsFile, 'utf8');
34
73
  }
35
74
 
36
- // 2. Read LESSONS.md
75
+ // ---------------------------------------------------------------------------
76
+ // 3. Read LESSONS.md
77
+ // ---------------------------------------------------------------------------
78
+
37
79
  const lessonsFile = path.join(ROOT, 'LESSONS.md');
38
80
  let lessonCount = 0;
39
81
  let oldLessons = [];
40
82
  if (fs.existsSync(lessonsFile)) {
41
83
  const lines = fs.readFileSync(lessonsFile, 'utf8').split('\n');
42
- // Count lessons by counting ### entries
43
84
  const lessonEntries = lines.filter(l => /^###\s+\[\d{4}-\d{2}-\d{2}\]/.test(l));
44
85
  lessonCount = lessonEntries.length;
45
86
 
46
- // Flag lessons older than 90 days
47
87
  const cutoff = new Date();
48
88
  cutoff.setDate(cutoff.getDate() - 90);
49
89
  oldLessons = lessonEntries.filter(entry => {
50
90
  const dateMatch = entry.match(/\[(\d{4}-\d{2}-\d{2})\]/);
51
- if (dateMatch) {
52
- return new Date(dateMatch[1]) < cutoff;
53
- }
91
+ if (dateMatch) return new Date(dateMatch[1]) < cutoff;
54
92
  return false;
55
93
  });
56
94
  }
57
95
 
58
- // 3. Read improvement-log for rejected items (to avoid re-suggesting)
96
+ // ---------------------------------------------------------------------------
97
+ // 4. Read improvement-log for rejected items
98
+ // ---------------------------------------------------------------------------
99
+
59
100
  const improvementLog = path.join(ROOT, '.bounce', 'improvement-log.md');
60
101
  let rejectedItems = [];
61
102
  if (fs.existsSync(improvementLog)) {
62
103
  const logContent = fs.readFileSync(improvementLog, 'utf8');
63
- // Simple extraction: look for table rows in "Rejected" section
64
104
  const rejectedMatch = logContent.match(/## Rejected\n[\s\S]*?(?=\n## |$)/);
65
105
  if (rejectedMatch) {
66
106
  rejectedItems = rejectedMatch[0].split('\n')
@@ -70,7 +110,10 @@ if (fs.existsSync(improvementLog)) {
70
110
  }
71
111
  }
72
112
 
73
- // 4. Parse sprint stats from trends
113
+ // ---------------------------------------------------------------------------
114
+ // 5. Parse sprint stats from trends
115
+ // ---------------------------------------------------------------------------
116
+
74
117
  let lastSprintStats = null;
75
118
  if (trendsContent) {
76
119
  const rows = trendsContent.split('\n').filter(l => l.match(/^\| S-\d{2} \|/));
@@ -86,18 +129,90 @@ if (trendsContent) {
86
129
  }
87
130
  }
88
131
 
132
+ // ---------------------------------------------------------------------------
133
+ // 6. Build suggestions
134
+ // ---------------------------------------------------------------------------
135
+
89
136
  const suggestions = [];
90
137
  let itemNum = 1;
91
138
 
92
- // 5. Generate suggestions based on data
139
+ // Impact level badge
140
+ function badge(impact) {
141
+ const badges = {
142
+ P0: '🔴 P0 Critical',
143
+ P1: '🟠 P1 High',
144
+ P2: '🟡 P2 Medium',
145
+ P3: '⚪ P3 Low',
146
+ };
147
+ return badges[impact?.level] || '⚪ Unrated';
148
+ }
149
+
150
+ // --- Manifest-driven suggestions (retro + lessons + effectiveness) ---
151
+ if (manifest && manifest.proposals) {
152
+ for (const proposal of manifest.proposals) {
153
+ // Skip previously rejected
154
+ if (rejectedItems.some(r => proposal.title.includes(r))) continue;
155
+
156
+ if (proposal.source === 'retro') {
157
+ suggestions.push({
158
+ num: itemNum++,
159
+ category: 'Retro',
160
+ impact: proposal.impact,
161
+ title: proposal.title,
162
+ detail: [
163
+ `**Area:** ${proposal.area}`,
164
+ `**Source Agent:** ${proposal.sourceAgent}`,
165
+ `**Severity:** ${proposal.severity}`,
166
+ proposal.recurring ? `**Recurring:** Yes — found in ${proposal.recurrenceSprints.join(', ')} (${proposal.recurrenceCount}x)` : null,
167
+ `**Suggested Fix:** ${proposal.suggestedFix}`,
168
+ ].filter(Boolean).join('\n'),
169
+ target: mapAreaToTarget(proposal.area),
170
+ effort: proposal.severity === 'Blocker' ? 'Medium' : 'Low',
171
+ });
172
+ } else if (proposal.source === 'lesson') {
173
+ suggestions.push({
174
+ num: itemNum++,
175
+ category: 'Lesson → Automation',
176
+ impact: proposal.impact,
177
+ title: proposal.title,
178
+ detail: [
179
+ `**Rule:** ${proposal.rule}`,
180
+ `**What happened:** ${proposal.whatHappened}`,
181
+ `**Active for:** ${proposal.ageSprints} sprint(s) (since ${proposal.lessonDate})`,
182
+ `**Automation type:** ${proposal.automationType}`,
183
+ `**Action:** ${proposal.automationDetail?.action}`,
184
+ `**Rationale:** ${proposal.automationDetail?.rationale}`,
185
+ ].filter(Boolean).join('\n'),
186
+ target: mapAutomationTypeToTarget(proposal.automationType),
187
+ effort: proposal.automationDetail?.effort || 'Low',
188
+ });
189
+ } else if (proposal.source === 'effectiveness_check') {
190
+ suggestions.push({
191
+ num: itemNum++,
192
+ category: 'Effectiveness',
193
+ impact: proposal.impact,
194
+ title: proposal.title,
195
+ detail: [
196
+ `**Status:** ${proposal.detail}`,
197
+ `**Originally applied in:** ${proposal.appliedInSprint}`,
198
+ '**Action:** Re-examine the original fix — it did not resolve the underlying issue.',
199
+ ].join('\n'),
200
+ target: 'Review original improvement in .bounce/improvement-log.md',
201
+ effort: 'Medium',
202
+ });
203
+ }
204
+ }
205
+ }
206
+
207
+ // --- Metric-driven suggestions (from trends) ---
93
208
  if (lastSprintStats) {
94
209
  if (lastSprintStats.firstPassRate < 80) {
95
210
  suggestions.push({
96
211
  num: itemNum++,
97
- category: 'Process',
212
+ category: 'Metrics',
213
+ impact: { level: 'P1', label: 'High' },
98
214
  title: `Low first-pass rate (${lastSprintStats.firstPassRate}%)`,
99
215
  detail: `First-pass rate was below 80% in ${lastSprintStats.sprintId}. This suggests spec ambiguity or insufficient context packs.`,
100
- recommendation: 'Add spec quality gate to `validate_bounce_readiness.mjs`: check Story §1 word count > 50 and §2 has ≥ 2 Gherkin scenarios.',
101
216
  target: 'scripts/validate_bounce_readiness.mjs',
102
217
  effort: 'Low',
103
218
  });
@@ -106,10 +221,10 @@ if (lastSprintStats) {
106
221
  if (lastSprintStats.avgTax > 10) {
107
222
  suggestions.push({
108
223
  num: itemNum++,
109
- category: 'Process',
224
+ category: 'Metrics',
225
+ impact: { level: 'P1', label: 'High' },
110
226
  title: `High correction tax (${lastSprintStats.avgTax}% average)`,
111
- detail: 'Average correction tax exceeded 10%, indicating significant human intervention was needed.',
112
- recommendation: 'Auto-flag stories with more than 3 files expected in Sprint Plan §2 Risk Flags. Consider splitting before bouncing.',
227
+ detail: 'Average correction tax exceeded 10%, indicating significant human intervention.',
113
228
  target: 'skills/agent-team/SKILL.md Step 1',
114
229
  effort: 'Low',
115
230
  });
@@ -118,70 +233,117 @@ if (lastSprintStats) {
118
233
  if (lastSprintStats.avgBounces > 0.5) {
119
234
  suggestions.push({
120
235
  num: itemNum++,
121
- category: 'Process',
236
+ category: 'Metrics',
237
+ impact: { level: 'P2', label: 'Medium' },
122
238
  title: `High bounce rate (${lastSprintStats.avgBounces} avg per story)`,
123
- detail: 'Run `vbounce trends` to see root cause breakdown and identify recurring patterns.',
124
- recommendation: 'Review root_cause field in archived QA/Arch FAIL reports to identify systemic issues.',
239
+ detail: 'Run `vbounce trends` to see root cause breakdown.',
125
240
  target: 'scripts/sprint_trends.mjs',
126
241
  effort: 'Low',
127
242
  });
128
243
  }
129
244
  }
130
245
 
131
- // Old lessons suggestion
246
+ // --- Lesson graduation ---
132
247
  if (oldLessons.length > 0) {
133
248
  const notRejected = oldLessons.filter(l => !rejectedItems.some(r => l.includes(r)));
134
249
  if (notRejected.length > 0) {
135
250
  suggestions.push({
136
251
  num: itemNum++,
137
- category: 'Framework',
138
- title: `${notRejected.length} lessons older than 90 days`,
252
+ category: 'Graduation',
253
+ impact: { level: 'P2', label: 'Medium' },
254
+ title: `${notRejected.length} lesson(s) older than 90 days — graduation candidates`,
139
255
  detail: notRejected.map(l => ` - ${l}`).join('\n'),
140
- recommendation: 'Review these lessons. Lessons not triggered in 3+ sprints should be archived to LESSONS_ARCHIVE.md. Lessons proven over 3+ sprints should be graduated to agent configs.',
141
- target: 'LESSONS.md',
142
- effort: 'Trivial',
256
+ target: 'LESSONS.md brains/claude-agents/',
257
+ effort: 'Low',
143
258
  });
144
259
  }
145
260
  }
146
261
 
147
- // General framework suggestions
148
- suggestions.push({
149
- num: itemNum++,
150
- category: 'Framework',
151
- title: 'Review lesson graduation candidates',
152
- detail: `You have ${lessonCount} lessons in LESSONS.md. Lessons proven over 3+ sprints should graduate to permanent agent config rules.`,
153
- recommendation: 'Run a review: which lessons have prevented recurrences for 3+ sprints? Graduate those to `.claude/agents/*.md` or `brains/claude-agents/*.md`.',
154
- target: 'LESSONS.md + brains/claude-agents/',
155
- effort: 'Low',
156
- });
157
-
262
+ // --- Health check ---
158
263
  suggestions.push({
159
264
  num: itemNum++,
160
265
  category: 'Health',
266
+ impact: { level: 'P3', label: 'Low' },
161
267
  title: 'Run vbounce doctor',
162
268
  detail: 'Verify the V-Bounce Engine installation is healthy after this sprint.',
163
- recommendation: 'Run: `vbounce doctor` — checks brain files, templates, scripts, state.json validity.',
164
269
  target: 'scripts/doctor.mjs',
165
270
  effort: 'Trivial',
166
271
  });
167
272
 
168
- // 6. Format output
273
+ // ---------------------------------------------------------------------------
274
+ // 7. Format output
275
+ // ---------------------------------------------------------------------------
276
+
277
+ function mapAreaToTarget(area) {
278
+ const map = {
279
+ 'Templates': 'templates/*.md',
280
+ 'Agent Handoffs': 'brains/claude-agents/*.md',
281
+ 'RAG Pipeline': 'scripts/prep_*.mjs',
282
+ 'Skills': 'skills/*/SKILL.md',
283
+ 'Process Flow': 'skills/agent-team/SKILL.md',
284
+ 'Tooling & Scripts': 'scripts/*',
285
+ };
286
+ return map[area] || area;
287
+ }
288
+
289
+ function mapAutomationTypeToTarget(type) {
290
+ const map = {
291
+ 'gate_check': '.bounce/gate-checks.json OR scripts/pre_gate_runner.sh',
292
+ 'script': 'scripts/',
293
+ 'template_field': 'templates/*.md',
294
+ 'agent_config': 'brains/claude-agents/*.md',
295
+ };
296
+ return map[type] || type;
297
+ }
298
+
169
299
  const suggestionBlocks = suggestions.map(s => {
170
- const rejectedNote = rejectedItems.some(r => s.title.includes(r)) ? '\n> ⚠ This was previously rejected — skipping.' : '';
171
- return `### ${s.num}. [${s.category}] ${s.title}${rejectedNote}
300
+ return `### ${s.num}. [${badge(s.impact)}] [${s.category}] ${s.title}
172
301
  ${s.detail}
173
302
 
174
- **Recommendation:** ${s.recommendation}
175
303
  **Target:** \`${s.target}\`
176
304
  **Effort:** ${s.effort}`;
177
305
  }).join('\n\n---\n\n');
178
306
 
307
+ // Impact level reference
308
+ const impactRef = `## Impact Levels
309
+
310
+ | Level | Label | Meaning | Timeline |
311
+ |-------|-------|---------|----------|
312
+ | **P0** | 🔴 Critical | Blocks agent work or causes incorrect output | Fix before next sprint |
313
+ | **P1** | 🟠 High | Causes rework — bounces, wasted tokens, repeated manual steps | Fix this improvement cycle |
314
+ | **P2** | 🟡 Medium | Friction that slows agents but does not block | Fix within 2 sprints |
315
+ | **P3** | ⚪ Low | Polish — nice-to-have, batch with other improvements | Batch when convenient |`;
316
+
317
+ // Summary stats
318
+ const summarySection = manifest ? `## Summary
319
+
320
+ | Source | Count |
321
+ |--------|-------|
322
+ | Retro (§5 findings) | ${manifest.summary.bySource.retro} |
323
+ | Lesson → Automation | ${manifest.summary.bySource.lesson} |
324
+ | Effectiveness checks | ${manifest.summary.bySource.effectiveness_check} |
325
+ | Metric-driven | ${suggestions.filter(s => s.category === 'Metrics').length} |
326
+ | **Total** | **${suggestions.length}** |
327
+
328
+ | Impact | Count |
329
+ |--------|-------|
330
+ | 🔴 P0 Critical | ${manifest.summary.byImpact.P0} |
331
+ | 🟠 P1 High | ${manifest.summary.byImpact.P1 + suggestions.filter(s => s.category === 'Metrics' && s.impact.level === 'P1').length} |
332
+ | 🟡 P2 Medium | ${manifest.summary.byImpact.P2 + suggestions.filter(s => s.category === 'Metrics' && s.impact.level === 'P2').length} |
333
+ | ⚪ P3 Low | ${manifest.summary.byImpact.P3 + suggestions.filter(s => s.category === 'Health').length} |` : '';
334
+
179
335
  const output = [
180
336
  `# Improvement Suggestions (post ${sprintId})`,
181
337
  `> Generated: ${today}. Review each item. Approved items are applied by the Lead at sprint boundary.`,
182
338
  `> Rejected items go to \`.bounce/improvement-log.md\` with reason.`,
183
339
  `> Applied items go to \`.bounce/improvement-log.md\` under Applied.`,
184
340
  '',
341
+ impactRef,
342
+ '',
343
+ summarySection,
344
+ '',
345
+ '---',
346
+ '',
185
347
  suggestionBlocks || '_No suggestions generated — all metrics look healthy!_',
186
348
  '',
187
349
  '---',
@@ -192,9 +354,10 @@ const output = [
192
354
  `- **Defer** → Record in \`.bounce/improvement-log.md\` under Deferred`,
193
355
  '',
194
356
  `> Framework changes (brains/, skills/, templates/) are applied at sprint boundaries only — never mid-sprint.`,
357
+ `> Use \`/improve\` skill to have the Team Lead apply approved changes with brain-file sync.`,
195
358
  ].join('\n');
196
359
 
197
360
  const outputFile = path.join(ROOT, '.bounce', 'improvement-suggestions.md');
198
- fs.writeFileSync(outputFile, output); // overwrite, not append
361
+ fs.writeFileSync(outputFile, output);
199
362
  console.log(`✓ Improvement suggestions written to .bounce/improvement-suggestions.md`);
200
363
  console.log(` ${suggestions.length} suggestion(s) generated`);
@@ -176,24 +176,25 @@ Examples:
176
176
  ## The Bounce Sequence
177
177
 
178
178
  ### Step 0: Sprint Setup
179
+
180
+ **Prerequisite:** Sprint Planning (Phase 2) must be complete. The Sprint Plan must be in "Confirmed" status with human approval before proceeding.
181
+
179
182
  ```
180
183
  1. Cut sprint branch from main:
181
184
  git checkout -b sprint/S-01 main
182
185
  mkdir -p .bounce/archive
183
186
 
184
- 2. Read RISK_REGISTRY.md — check for open risks relevant to this sprint's stories.
185
- If high-severity risks affect planned stories, flag to human before proceeding.
186
-
187
- 3. Read sprint-{XX}.md check §2 Sprint Open Questions.
188
- If unresolved questions block stories in this sprint, flag to human.
189
- Do NOT start bouncing stories with unresolved blocking questions.
187
+ 2. Verify Sprint Plan:
188
+ - Sprint Plan status must be "Confirmed" (human-approved in Phase 2)
189
+ - §0 Sprint Readiness Gate must be fully checked
190
+ - §3 Sprint Open Questions must have no unresolved blocking items
191
+ If any check fails, return to Phase 2 (Sprint Planning).
190
192
 
191
- 4. If vdocs/_manifest.json exists, read it.
193
+ 3. If vdocs/_manifest.json exists, read it.
192
194
  Understand what's already documented — this informs which stories
193
195
  may require doc updates after the sprint.
194
196
 
195
- 5. Triage Requests: If an incoming task is an L1 Trivial change (1-2 files),
196
- use the **Hotfix Path**:
197
+ 4. **Hotfix Path** (L1 Trivial tasks only triaged during Phase 1):
197
198
  a. Create `HOTFIX-{Date}-{Name}.md` using the template.
198
199
  b. Delegate to Developer (no worktree needed if acting on active branch).
199
200
  c. Developer runs `hotfix_manager.sh ledger "{Title}" "{Description}"` after implementation.
@@ -201,26 +202,35 @@ Examples:
201
202
  e. DevOps runs `hotfix_manager.sh sync` to update any active story worktrees.
202
203
  f. Update Delivery Plan Status to "Done".
203
204
 
204
- 6. **Gate Config Check**:
205
+ 5. **Gate Config Check**:
205
206
  - If `.bounce/gate-checks.json` does not exist, run `./scripts/init_gate_config.sh` to auto-detect the project stack and generate default gate checks.
206
207
  - If it exists, verify it's current (stack detection may have changed).
207
208
 
208
- 7. **Parallel Readiness Check** (before bouncing multiple stories simultaneously):
209
+ 6. **Parallel Readiness Check** (before bouncing multiple stories simultaneously):
209
210
  - Verify test runner config excludes `.worktrees/` (vitest, jest, pytest, etc.)
210
211
  - Verify no shared mutable state between worktrees (e.g., shared temp files, singletons writing to same path)
211
212
  - Verify `.gitignore` includes `.worktrees/`
212
213
  If any check fails, fix before spawning parallel stories. Intermittent test failures from worktree cross-contamination erode trust in the test suite fast.
213
214
 
214
- 8. **Dependency Check & Execution Mode**:
215
- - For each story, check the `Depends On:` field in its template.
216
- - If Story B depends on Story A, you MUST execute them sequentially. Do not create Story B's worktree or spawn its Developer until Story A has successfully passed the DevOps merge step.
217
- - Determine Execution Mode:
218
- - **Full Bounce (Default)**: Normal L2-L4 stories go through full Dev → QA → Architect → DevOps flow.
219
- - **Fast Track (L1/L2 Minor)**: For cosmetic UI tweaks or isolated refactors, execute Dev → DevOps only. Skip QA and Architect loops to save overhead. Validate manually during Sprint Review.
220
-
221
- 9. Update sprint-{XX}.md: Status → "Active"
215
+ 7. Update sprint-{XX}.md: Status "Active"
222
216
  ```
223
217
 
218
+ **Note:** Risk assessment, dependency checks, scope selection, and execution mode decisions all happen during Sprint Planning (Phase 2), not here. Step 0 executes the confirmed plan.
219
+
220
+ ### Step 0.5: Discovery Check (L4 / 🔴 Stories Only)
221
+
222
+ Before moving any story to Ready to Bounce:
223
+
224
+ 1. For each story with `complexity_label: L4` or `ambiguity: 🔴 High`:
225
+ - Check for linked spikes in `product_plans/backlog/EPIC-{NNN}_{name}/SPIKE-*.md`
226
+ - If no spikes exist → create them (invoke doc-manager ambiguity rubric)
227
+ - If spikes exist but are not Validated/Closed → execute discovery sub-flow
228
+ - See `skills/agent-team/references/discovery.md`
229
+
230
+ 2. Once all spikes are Validated/Closed:
231
+ - Update story `ambiguity` frontmatter (should now be 🟡 or 🟢)
232
+ - Transition: Probing/Spiking → Refinement → Ready to Bounce
233
+
224
234
  ### Step 1: Story Initialization
225
235
  For each story with V-Bounce State "Ready to Bounce":
226
236
  ```bash
@@ -253,6 +263,8 @@ mkdir -p .worktrees/STORY-{ID}-{StoryName}/.bounce/{tasks,reports}
253
263
  ./scripts/pre_gate_runner.sh qa .worktrees/STORY-{ID}-{StoryName}/ sprint/S-{XX}
254
264
  - If scan FAILS on trivial issues (debug statements, missing JSDoc, TODOs):
255
265
  Return to Developer for quick fix. Do NOT spawn QA for mechanical failures.
266
+ If pre-gate scan fails 3+ times → Escalate: present failures to human with options:
267
+ a) Human fixes manually, b) Descope the story, c) Re-assign to a different approach.
256
268
  - If scan PASSES: Include scan output path in the QA task file.
257
269
  1. Spawn qa subagent in .worktrees/STORY-{ID}-{StoryName}/ with:
258
270
  - Developer Implementation Report
@@ -277,6 +289,7 @@ mkdir -p .worktrees/STORY-{ID}-{StoryName}/.bounce/{tasks,reports}
277
289
  ./scripts/pre_gate_runner.sh arch .worktrees/STORY-{ID}-{StoryName}/ sprint/S-{XX}
278
290
  - If scan reveals new dependencies or structural violations:
279
291
  Return to Developer for resolution. Do NOT spawn Architect for mechanical failures.
292
+ If pre-gate scan fails 3+ times → Escalate to human (same options as pre-QA escalation).
280
293
  - If scan PASSES: Include scan output path in the Architect task file.
281
294
  1. Spawn architect subagent in .worktrees/STORY-{ID}-{StoryName}/ with:
282
295
  - All reports for this story
@@ -309,10 +322,27 @@ mkdir -p .worktrees/STORY-{ID}-{StoryName}/.bounce/{tasks,reports}
309
322
  4. If merge conflicts:
310
323
  - Simple (imports, whitespace): DevOps resolves directly
311
324
  - Complex (logic): DevOps writes Conflict Report, Lead creates fix story
312
- 5. If post-merge tests fail: DevOps reverts merge, reports failure to Lead
325
+ 5. If post-merge tests fail:
326
+ - DevOps reverts the merge and writes a Post-Merge Failure Report (what failed, which tests, suspected cause)
327
+ - Lead returns story to Developer with the failure report as input
328
+ - Developer fixes in the original worktree (which is preserved until merge succeeds)
329
+ - Story re-enters the bounce at Step 2 (Dev pass). QA/Arch bounce counts are NOT reset — this is a merge issue, not a gate failure.
330
+ - If post-merge fails 3+ times → Escalate to human
313
331
  ```
314
332
  Update sprint-{XX}.md: V-Bounce State → "Done"
315
333
 
334
+ ### Step 5.5: Immediate Lesson Recording
335
+ After each story merge, before proceeding to the next story:
336
+ ```
337
+ 1. Read Dev report — check `lessons_flagged` field
338
+ 2. Read QA report — check for lessons flagged during validation
339
+ 3. For each flagged lesson:
340
+ - Present to the human for approval
341
+ - If approved → record to LESSONS.md immediately (follow lesson skill format)
342
+ - If rejected → note as "No" in Sprint Report §4
343
+ 4. Do NOT defer this to Step 7 — context decays fast
344
+ ```
345
+
316
346
  ### Step 6: Sprint Integration Audit
317
347
  After ALL stories are merged into `sprint/S-01`:
318
348
  ```
@@ -320,7 +350,11 @@ After ALL stories are merged into `sprint/S-01`:
320
350
  2. First, Architect runs `./scripts/hotfix_manager.sh audit` to check for hotfix drift. If it fails, perform deep audit on flagged files.
321
351
  3. Run Sprint Integration Audit — Deep Audit on combined changes
322
352
  4. Check for: duplicate routes, competing state, overlapping migrations
323
- 5. If issues found → create new stories to fix, bounce individually
353
+ 5. If issues found:
354
+ - Present findings to human with severity assessment
355
+ - AI suggests which epic the fix story should belong to
356
+ - Fix stories are added to the BACKLOG (not the current sprint) — they enter the next sprint through normal planning
357
+ - Exception: if the issue blocks the sprint release (e.g., broken build), fix inline on the sprint branch without creating a story
324
358
  ```
325
359
 
326
360
  ### Step 7: Sprint Consolidation
@@ -336,12 +370,12 @@ After ALL stories are merged into `sprint/S-01`:
336
370
  ```
337
371
  4. V-Bounce State → "Sprint Review" for all stories
338
372
  4. Present Sprint Report to human
339
- 5. **BLOCKING STEP — Lesson Approval:**
340
- Review and approve/reject ALL flagged lessons from §4 of the Sprint Report.
341
- Do NOT proceed to Sprint Release until every lesson has a status of "Yes" or "No".
342
- Stale lessons lose context approve them while the sprint is fresh.
343
- Present each lesson to the human and record approved ones to LESSONS.md immediately.
344
- 6. After approval → Spawn devops subagent for Sprint Release:
373
+ 5. **Lesson Review (non-blocking):**
374
+ Most lessons should already be recorded to LESSONS.md during Step 5.5.
375
+ Review §4 of the Sprint Report confirm all flagged lessons have a status.
376
+ If any lessons were missed during Step 5.5, present them now and record approved ones.
377
+ This is a review step, not a first-time approval gate.
378
+ 6. After review → Spawn devops subagent for Sprint Release:
345
379
  - Merge sprint/S-01 → main (--no-ff)
346
380
  - Tag release: v{VERSION}
347
381
  - Run full test suite + build + lint on main
@@ -435,7 +469,7 @@ The Team Lead MUST update the active `sprint-{XX}.md` at every state transition.
435
469
  | Architect passes | §1: V-Bounce State → "Architect Passed" | **Nothing** |
436
470
  | DevOps merges story | §1: V-Bounce State → "Done". §4: Add Execution Log row (via `vbounce story complete`) | **Nothing** |
437
471
  | Escalated | §1: Move story to Escalated section | **Nothing** |
438
- | Sprint CLOSES | Status → "Completed" in frontmatter | §2: sprint → Completed. §4: add summary. §3: remove delivered stories |
472
+ | Sprint CLOSES | Status → "Completed" in frontmatter | §2: sprint → Completed. §4: add summary. §3: remove delivered stories. **This is the ONLY time Delivery Plan updates.** |
439
473
 
440
474
  > **Key rule**: The Delivery Plan is updated ONLY at sprint close, never during active bouncing.
441
475
  > See `skills/agent-team/references/delivery-sync.md` for full sync rules.
@@ -501,6 +535,7 @@ If merging story branch into sprint branch creates conflicts:
501
535
  - **Check risks before bouncing.** Read RISK_REGISTRY.md at sprint start. Flag high-severity risks that affect planned stories.
502
536
  - **Resolve open questions first.** Read the active `sprint-{XX}.md` §2 Sprint Open Questions at sprint start. Do not bounce stories with unresolved blocking questions.
503
537
  - **Know what's documented.** If `vdocs/_manifest.json` exists, read it at sprint start. Pass relevant doc references to agents. Offer documentation updates after sprints that deliver new features.
538
+ - **Resolve discovery before bouncing.** L4 stories and 🔴 ambiguity stories MUST complete spikes before entering the bounce sequence. See `skills/agent-team/references/discovery.md`.
504
539
 
505
540
  ## Keywords
506
541
 
@@ -0,0 +1,97 @@
1
+ # Discovery Phase — Spike Execution Protocol
2
+
3
+ > On-demand reference from agent-team/SKILL.md. When and how to run discovery spikes for ambiguous work.
4
+
5
+ ## When Discovery Triggers
6
+
7
+ 1. **Epic-level 🔴 ambiguity** — detected during Epic creation or review via the doc-manager ambiguity assessment rubric.
8
+ 2. **Story labeled L4** — transitioning to Probing/Spiking state. L4 stories MUST have at least one linked spike before they can progress.
9
+ 3. **Blocking Open Questions** — Epic §8 has items marked "Blocking" with no ADR or prior decision.
10
+
11
+ ## Spike Lifecycle
12
+
13
+ ```
14
+ Open → Investigating → Findings Ready → Validated → Closed
15
+ ```
16
+
17
+ | Status | Who Acts | What Happens |
18
+ |--------|----------|-------------|
19
+ | **Open** | Team Lead | Spike created from `templates/spike.md`, linked in Epic §9 |
20
+ | **Investigating** | Developer | Code exploration, prototyping, benchmarks. Fills §4 Findings and §5 Decision |
21
+ | **Findings Ready** | Developer → Architect | Developer marks complete. Awaiting Architect validation |
22
+ | **Validated** | Architect | Confirms findings against Safe Zone and ADRs. PASS → Validated. FAIL → back to Investigating |
23
+ | **Closed** | Team Lead | All §7 Affected Documents checked off. Findings propagated to Epic, Roadmap, Risk Registry |
24
+
25
+ ## Execution Protocol
26
+
27
+ ### Step 1: Create Spike (Team Lead)
28
+
29
+ 1. Identify the blocking unknown (from Epic §8 Open Questions or 🔴 ambiguity signals)
30
+ 2. Create spike document from `templates/spike.md`
31
+ 3. Fill §1 Question, §2 Constraints, §3 Approach
32
+ 4. Set status → Open
33
+ 5. Link spike in parent Epic §9 Artifact Links
34
+ 6. Set time box (default: 4 hours for focused questions, 1 day for broader exploration)
35
+
36
+ ### Step 2: Investigate (Developer)
37
+
38
+ 1. Read the spike §1 Question, §2 Constraints, §3 Approach
39
+ 2. Read parent Epic §4 Technical Context for existing knowledge
40
+ 3. Investigate using the specified approach (code exploration, prototyping, benchmarks, doc research)
41
+ 4. Fill §4 Findings with evidence and data
42
+ 5. Fill §5 Decision with chosen approach, rationale, and rejected alternatives
43
+ 6. Mark §5 ADR Required if the decision is architectural
44
+ 7. Fill §6 Residual Risk if unknowns remain
45
+ 8. Set status → Findings Ready
46
+
47
+ ### Step 3: Validate (Architect)
48
+
49
+ 1. Read the spike §4 Findings and §5 Decision
50
+ 2. Validate against:
51
+ - Safe Zone compliance (no unauthorized patterns or libraries)
52
+ - Existing ADRs in Roadmap §3 (no contradictions)
53
+ - Risk profile (§6 Residual Risk is acceptable)
54
+ 3. If **PASS** → status → Validated
55
+ 4. If **FAIL** → provide specific feedback, status remains Findings Ready, Developer re-investigates
56
+
57
+ ### Step 4: Close & Propagate (Team Lead)
58
+
59
+ 1. Walk through §7 Affected Documents checklist:
60
+ - Update Epic §4 Technical Context with findings
61
+ - Mark Epic §8 Open Questions as resolved
62
+ - Add spike reference to Epic §9 Artifact Links
63
+ - If §5 ADR Required → create new ADR row in Roadmap §3
64
+ - If §6 Residual Risk has entries → add to Risk Registry §1
65
+ - If story-level spike → update Story §3 Implementation Guide
66
+ 2. Check off all items in §7
67
+ 3. Set status → Closed
68
+ 4. Parent story transitions: Probing/Spiking → Refinement
69
+
70
+ ## Timing Rules
71
+
72
+ - Spikes happen during **planning/refinement**, NOT during sprint execution
73
+ - No worktrees needed — spikes produce documents, not code
74
+ - Time box is enforced — if the time box expires without findings, the spike is escalated to the human with a status report
75
+ - Prototypes created during spikes are **throwaway** — they are NOT committed to any branch
76
+
77
+ ## What Spikes Are NOT
78
+
79
+ - **Not production code.** Spikes produce findings and decisions, not shippable code.
80
+ - **Not QA/DevOps passes.** No bounce sequence, no gate reports, no merge operations.
81
+ - **Not a worktree activity.** Spikes are document-level work, not branch-level work.
82
+ - **Not open-ended research.** Every spike has a time box and a specific question. If the question is too broad, split into multiple spikes.
83
+
84
+ ## Integration with Bounce Sequence
85
+
86
+ Spikes gate the transition from Probing/Spiking → Refinement → Ready to Bounce:
87
+
88
+ ```
89
+ Story (L4 / 🔴) → Probing/Spiking
90
+ └── Spike(s) created
91
+ └── Developer investigates → Architect validates → Team Lead propagates
92
+ └── All spikes Validated/Closed
93
+ └── Story ambiguity updated (should now be 🟡 or 🟢)
94
+ └── Story → Refinement → Ready to Bounce
95
+ ```
96
+
97
+ No story may enter Ready to Bounce while it has linked spikes in Open, Investigating, or Findings Ready status.