vbounce-engine 2.5.1 → 2.5.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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  <p>
7
7
  <a href="https://github.com/sandrinio/v-bounce-engine/blob/main/LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg"></a>
8
- <a href="https://www.npmjs.com/package/vbounce"><img alt="NPM Version" src="https://img.shields.io/npm/v/vbounce"></a>
8
+ <a href="https://www.npmjs.com/package/vbounce-engine"><img alt="NPM Version" src="https://img.shields.io/npm/v/vbounce-engine"></a>
9
9
  </p>
10
10
 
11
11
  <p>
@@ -21,14 +21,14 @@ Get your new AI team up and running in seconds. No complex setup, no vector data
21
21
 
22
22
  ```bash
23
23
  # 1. Install the framework for your platform of choice
24
- npx vbounce install claude # Claude Code (Full Orchestration)
25
- # Or: npx vbounce install cursor|gemini|codex|vscode
24
+ npx vbounce-engine install claude # Claude Code (Full Orchestration)
25
+ # Or: npx vbounce-engine install cursor|gemini|codex|vscode
26
26
 
27
27
  # 2. Verify your installation
28
- npx vbounce doctor
28
+ npx vbounce-engine doctor
29
29
 
30
30
  # 3. Initialize your first sprint!
31
- npx vbounce sprint init S-01 D-01
31
+ npx vbounce-engine sprint init S-01 D-01
32
32
  ```
33
33
 
34
34
  > **Requirements**: Node.js and a project directory. That's it. One person to set the vision, the AI handles the execution.
@@ -4,8 +4,8 @@
4
4
  > Any modification to `.claude/agents/`, `.vbounce/skills/`, `.vbounce/templates/`, or `.vbounce/scripts/` MUST also update this file.
5
5
  > Run `vbounce doctor` to validate file existence against this manifest.
6
6
 
7
- **Version:** 2.5.1
8
- **Last updated:** 2026-03-25
7
+ **Version:** 2.5.3
8
+ **Last updated:** 2026-03-26
9
9
 
10
10
  ---
11
11
 
@@ -185,6 +185,16 @@ Skills are modular instructions loaded by agents. Located in `.vbounce/skills/`.
185
185
 
186
186
  Scripts automate framework operations. Located in `.vbounce/scripts/`.
187
187
 
188
+ ### Script Execution Wrapper
189
+ | Script | When | Input | Output |
190
+ |--------|------|-------|--------|
191
+ | `.vbounce/scripts/run_script.sh` | **Every script invocation** | Script name + args | Passthrough stdout/stderr, pre-flight checks, structured diagnostics on failure |
192
+
193
+ ### Shared Constants
194
+ | Script | When | Input | Output |
195
+ |--------|------|-------|--------|
196
+ | `.vbounce/scripts/constants.mjs` | Imported by lifecycle scripts | — | `VALID_STATES`, `TERMINAL_STATES` (single source of truth) |
197
+
188
198
  ### Sprint Lifecycle
189
199
  | Script | When | Input | Output |
190
200
  |--------|------|-------|--------|
@@ -364,16 +374,21 @@ Regression suite for validating the engine after any path, script, or template c
364
374
 
365
375
  | File | Suite | What it checks |
366
376
  |------|-------|----------------|
367
- | `tests/harness.mjs` | — | Test primitives: `suite()`, `record()`, `assertFileExists()`, `assertNoMatch()`, `assertScriptRuns()`, `generateReport()` |
368
- | `tests/run.mjs` | — | Main runner: installs to temp dir, runs all suites, generates JSON + Markdown reports |
377
+ | `tests/harness.mjs` | — | Test primitives: `suite()`, `record()`, `assertFileExists()`, `assertNoMatch()`, `assertScriptRuns()`, `assertBashRuns()`, `generateReport()` |
378
+ | `tests/fixtures.mjs` | — | Shared fixture generator: `createSprintFixtures()`, `createSyntheticReport()`, `removeSprintFixtures()` |
379
+ | `tests/run.mjs` | — | Main runner: installs to temp dir, runs all 12 suites, generates JSON + Markdown reports |
369
380
  | `tests/suites/install.mjs` | Install Integrity | 76+ file existence checks across all installed components |
370
381
  | `tests/suites/paths.mjs` | Path Integrity | 500+ stale path pattern scans across all shipped `.md`/`.mjs` files |
371
382
  | `tests/suites/doctor.mjs` | Doctor Accuracy | False positive and false negative detection |
372
- | `tests/suites/scripts.mjs` | Script Validation | Import checks, functional tests, ROOT resolution for all 22 `.mjs` scripts |
383
+ | `tests/suites/scripts.mjs` | Script Validation | Import checks, functional tests, ROOT resolution for all `.mjs` scripts |
373
384
  | `tests/suites/brains.mjs` | Agent Contracts | Frontmatter, report YAML signatures, CLAUDE.md ↔ agents consistency |
374
385
  | `tests/suites/manifest.mjs` | Manifest Completeness | All backtick paths resolve, orphan file detection |
375
386
  | `tests/suites/templates.mjs` | Template/Skill Integrity | Structure validation, stale paths, CLAUDE.md ↔ skills cross-reference |
376
387
  | `tests/suites/lifecycle.mjs` | Full Lifecycle | 41-test simulation: fixtures → init → transitions → context prep → complete → close → analytics → edge cases |
388
+ | `tests/suites/agent-errors.mjs` | Agent Error Paths | Scripts called in wrong order, wrong state, wrong/missing args — verifies actionable errors, not raw crashes |
389
+ | `tests/suites/run-script-wrapper.mjs` | Script Wrapper | `run_script.sh` pre-flight checks, diagnostic block output, success/failure passthrough, bash script support |
390
+ | `tests/suites/parallel-stories.mjs` | Parallel Stories | Concurrent state management: 3 stories transition independently, bounce counts isolated, re-init behavior |
391
+ | `tests/suites/report-parsing.mjs` | Report Parsing | Malformed agent reports (no frontmatter, empty, truncated YAML, missing fields) handled gracefully |
377
392
 
378
393
  Reports output to `tests/reports/report-{timestamp}.{json,md}`.
379
394
 
@@ -396,9 +411,9 @@ Reports output to `tests/reports/report-{timestamp}.{json,md}`.
396
411
  | Templates | 13 |
397
412
  | Skills (SKILL.md + references) | 26 |
398
413
  | React rules | 57 |
399
- | Scripts | 27 |
400
- | Test suite | 10 |
414
+ | Scripts | 29 |
415
+ | Test suite | 16 |
401
416
  | Diagrams | 6 |
402
417
  | Docs + Visual Assets | 6 + ~15 icons/images |
403
418
  | CLI | 1 |
404
- | **Total** | **~185** |
419
+ | **Total** | **~193** |
package/brains/CLAUDE.md CHANGED
@@ -58,12 +58,12 @@ Determine which phase you're in from what the human is asking, then load the rig
58
58
  > - **Planning (Phase 1 & 2):** Load `@.vbounce/skills/doc-manager/SKILL.md` + `@.vbounce/skills/product-graph/SKILL.md`
59
59
  > - **Execution (Phase 3):** Load `@.vbounce/skills/agent-team/SKILL.md`
60
60
 
61
- > **On-demand skills:**
62
- > - `/doc` → `@.vbounce/skills/doc-manager/SKILL.md`
63
- > - `/review` → `@.vbounce/skills/vibe-code-review/SKILL.md` — code review
64
- > - `/write-skill` → `@.vbounce/skills/write-skill/SKILL.md` — skill authoring
65
- > - `/improve` → `@.vbounce/skills/improve/SKILL.md` — framework improvement
66
- > - `/react` → `@.vbounce/skills/react-best-practices/SKILL.md` — frontend patterns
61
+ > **On-demand skills** (read the file directly when the user asks — these are NOT slash commands):
62
+ > - "doc"read `@.vbounce/skills/doc-manager/SKILL.md`
63
+ > - "review"read `@.vbounce/skills/vibe-code-review/SKILL.md` — code review
64
+ > - "write-skill"read `@.vbounce/skills/write-skill/SKILL.md` — skill authoring
65
+ > - "improve"read `@.vbounce/skills/improve/SKILL.md` — framework improvement
66
+ > - "react"read `@.vbounce/skills/react-best-practices/SKILL.md` — frontend patterns
67
67
 
68
68
  ## Subagents
69
69
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vbounce-engine",
3
- "version": "2.5.1",
3
+ "version": "2.5.3",
4
4
  "description": "V-Bounce Engine: Turn your AI coding assistant into a full engineering team through structured SDLC skills.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,7 @@ import fs from 'fs';
12
12
  import path from 'path';
13
13
  import { fileURLToPath } from 'url';
14
14
  import { spawnSync } from 'child_process';
15
+ import { TERMINAL_STATES } from './constants.mjs';
15
16
 
16
17
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
18
  const ROOT = path.resolve(__dirname, '../..');
@@ -37,7 +38,13 @@ if (!fs.existsSync(stateFile)) {
37
38
  process.exit(1);
38
39
  }
39
40
 
40
- const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
41
+ let state;
42
+ try {
43
+ state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
44
+ } catch (e) {
45
+ console.error(`ERROR: state.json is not valid JSON — ${e.message}`);
46
+ process.exit(1);
47
+ }
41
48
 
42
49
  if (state.sprint_id !== sprintId) {
43
50
  console.error(`ERROR: state.json is for sprint ${state.sprint_id}, not ${sprintId}`);
@@ -46,7 +53,7 @@ if (state.sprint_id !== sprintId) {
46
53
 
47
54
  // 2. Check all stories are terminal
48
55
  const activeStories = Object.entries(state.stories || {}).filter(
49
- ([, s]) => !['Done', 'Escalated', 'Parking Lot'].includes(s.state)
56
+ ([, s]) => !TERMINAL_STATES.includes(s.state)
50
57
  );
51
58
 
52
59
  if (activeStories.length > 0) {
@@ -42,7 +42,13 @@ if (!fs.existsSync(stateFile)) {
42
42
  console.error('ERROR: .vbounce/state.json not found');
43
43
  process.exit(1);
44
44
  }
45
- const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
45
+ let state;
46
+ try {
47
+ state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
48
+ } catch (e) {
49
+ console.error(`ERROR: state.json is not valid JSON — ${e.message}`);
50
+ process.exit(1);
51
+ }
46
52
  if (!state.stories[storyId]) {
47
53
  console.error(`ERROR: Story "${storyId}" not found in state.json`);
48
54
  process.exit(1);
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * constants.mjs
5
+ * Shared constants for V-Bounce Engine scripts.
6
+ * Single source of truth for story states and terminal states.
7
+ */
8
+
9
+ /** All valid story states in lifecycle order. */
10
+ export const VALID_STATES = [
11
+ 'Draft', 'Refinement', 'Ready to Bounce', 'Bouncing',
12
+ 'QA Passed', 'Architect Passed', 'Done', 'Escalated', 'Parking Lot'
13
+ ];
14
+
15
+ /** Terminal states — stories that are no longer active. */
16
+ export const TERMINAL_STATES = ['Done', 'Escalated', 'Parking Lot'];
@@ -100,6 +100,7 @@ if (skillCount === requiredSkills.length) pass(`Skills: ${skillCount}/${required
100
100
 
101
101
  // Check scripts
102
102
  const requiredScripts = [
103
+ 'run_script.sh',
103
104
  'validate_report.mjs', 'update_state.mjs', 'validate_state.mjs',
104
105
  'validate_sprint_plan.mjs', 'validate_bounce_readiness.mjs',
105
106
  'init_sprint.mjs', 'close_sprint.mjs', 'complete_story.mjs',
@@ -536,6 +536,53 @@ check_file_size_limit() {
536
536
  fi
537
537
  }
538
538
 
539
+ # ── Pre-merge report verification ────────────────────────────────────
540
+
541
+ check_gate_reports_exist() {
542
+ local dir="$1" story_id="$2"
543
+ local reports_dir="${dir}/.vbounce/reports"
544
+ local missing=0
545
+ local details=""
546
+
547
+ if [[ ! -d "$reports_dir" ]]; then
548
+ record_result "gate_reports" "FAIL" ".vbounce/reports/ directory not found in worktree"
549
+ record_result_plain "gate_reports" "FAIL" ".vbounce/reports/ directory not found in worktree"
550
+ return
551
+ fi
552
+
553
+ # Check for QA report (any bounce)
554
+ local qa_report
555
+ qa_report=$(find "$reports_dir" -name "${story_id}-qa*" -o -name "${story_id}*-qa*" 2>/dev/null | head -1)
556
+ if [[ -z "$qa_report" ]]; then
557
+ missing=$((missing + 1))
558
+ details="${details}QA report missing. "
559
+ fi
560
+
561
+ # Check for Architect report (any bounce)
562
+ local arch_report
563
+ arch_report=$(find "$reports_dir" -name "${story_id}-arch*" -o -name "${story_id}*-arch*" 2>/dev/null | head -1)
564
+ if [[ -z "$arch_report" ]]; then
565
+ missing=$((missing + 1))
566
+ details="${details}Architect report missing. "
567
+ fi
568
+
569
+ # Check for Dev report
570
+ local dev_report
571
+ dev_report=$(find "$reports_dir" -name "${story_id}-dev*" -o -name "${story_id}*-dev*" 2>/dev/null | head -1)
572
+ if [[ -z "$dev_report" ]]; then
573
+ missing=$((missing + 1))
574
+ details="${details}Dev report missing. "
575
+ fi
576
+
577
+ if [[ $missing -eq 0 ]]; then
578
+ record_result "gate_reports" "PASS" "Dev, QA, and Architect reports present"
579
+ record_result_plain "gate_reports" "PASS" "Dev, QA, and Architect reports present"
580
+ else
581
+ record_result "gate_reports" "FAIL" "${details}"
582
+ record_result_plain "gate_reports" "FAIL" "${details}"
583
+ fi
584
+ }
585
+
539
586
  # ── Custom check runner ──────────────────────────────────────────────
540
587
 
541
588
  run_custom_check() {
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env bash
2
+ # run_script.sh — Safe wrapper for V-Bounce script execution
3
+ # Usage: ./.vbounce/scripts/run_script.sh <script> [args...]
4
+ #
5
+ # All agents MUST invoke .vbounce scripts through this wrapper.
6
+ # Captures exit code, stdout, stderr. On failure, prints a structured
7
+ # diagnostic block that agents can parse and act on.
8
+
9
+ set -uo pipefail
10
+
11
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12
+ ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
13
+
14
+ RED='\033[0;31m'
15
+ GREEN='\033[0;32m'
16
+ YELLOW='\033[1;33m'
17
+ CYAN='\033[0;36m'
18
+ NC='\033[0m'
19
+
20
+ # ── Arguments ────────────────────────────────────────────────────────
21
+
22
+ SCRIPT_NAME="${1:-}"
23
+
24
+ if [[ -z "$SCRIPT_NAME" ]]; then
25
+ echo "Usage: ./.vbounce/scripts/run_script.sh <script> [args...]"
26
+ echo ""
27
+ echo "Examples:"
28
+ echo " ./.vbounce/scripts/run_script.sh validate_state.mjs"
29
+ echo " ./.vbounce/scripts/run_script.sh pre_gate_runner.sh qa .worktrees/STORY-001-01/"
30
+ echo " ./.vbounce/scripts/run_script.sh complete_story.mjs STORY-001-01"
31
+ exit 1
32
+ fi
33
+
34
+ shift
35
+ SCRIPT_ARGS=("$@")
36
+
37
+ # ── Resolve script path ─────────────────────────────────────────────
38
+
39
+ SCRIPT_PATH="${SCRIPT_DIR}/${SCRIPT_NAME}"
40
+
41
+ if [[ ! -f "$SCRIPT_PATH" ]]; then
42
+ echo ""
43
+ echo -e "${RED}┌─ SCRIPT FAILURE ─────────────────────────────────────────┐${NC}"
44
+ echo -e "${RED}│ Script: ${SCRIPT_NAME}${NC}"
45
+ echo -e "${RED}│ Exit Code: 127 (not found)${NC}"
46
+ echo -e "${RED}│ Error: ${SCRIPT_PATH} does not exist${NC}"
47
+ echo -e "${RED}│${NC}"
48
+ echo -e "${RED}│ Available scripts:${NC}"
49
+ for f in "${SCRIPT_DIR}"/*.{mjs,sh}; do
50
+ [[ -f "$f" ]] && [[ "$(basename "$f")" != "run_script.sh" ]] && echo -e "${RED}│ $(basename "$f")${NC}"
51
+ done
52
+ echo -e "${RED}│${NC}"
53
+ echo -e "${RED}│ Action: Check script name for typos${NC}"
54
+ echo -e "${RED}└──────────────────────────────────────────────────────────┘${NC}"
55
+ exit 127
56
+ fi
57
+
58
+ # ── Pre-flight checks ───────────────────────────────────────────────
59
+
60
+ PREFLIGHT_WARNINGS=""
61
+
62
+ # Check state.json for scripts that need it
63
+ NEEDS_STATE=(
64
+ "update_state.mjs" "validate_state.mjs" "complete_story.mjs"
65
+ "close_sprint.mjs" "prep_sprint_context.mjs" "validate_bounce_readiness.mjs"
66
+ )
67
+
68
+ for s in "${NEEDS_STATE[@]}"; do
69
+ if [[ "$SCRIPT_NAME" == "$s" ]]; then
70
+ if [[ ! -f "${ROOT}/.vbounce/state.json" ]]; then
71
+ PREFLIGHT_WARNINGS="${PREFLIGHT_WARNINGS}\n ⚠ state.json missing — ${SCRIPT_NAME} will fail"
72
+ elif ! node -e "JSON.parse(require('fs').readFileSync('${ROOT}/.vbounce/state.json','utf8'))" 2>/dev/null; then
73
+ PREFLIGHT_WARNINGS="${PREFLIGHT_WARNINGS}\n ⚠ state.json is invalid JSON — ${SCRIPT_NAME} will fail"
74
+ fi
75
+ break
76
+ fi
77
+ done
78
+
79
+ # Check .vbounce directory exists
80
+ if [[ ! -d "${ROOT}/.vbounce" ]]; then
81
+ PREFLIGHT_WARNINGS="${PREFLIGHT_WARNINGS}\n ⚠ .vbounce/ directory missing"
82
+ fi
83
+
84
+ if [[ -n "$PREFLIGHT_WARNINGS" ]]; then
85
+ echo -e "${YELLOW}Pre-flight warnings:${PREFLIGHT_WARNINGS}${NC}"
86
+ echo ""
87
+ fi
88
+
89
+ # ── Execute ──────────────────────────────────────────────────────────
90
+
91
+ STDOUT_FILE=$(mktemp)
92
+ STDERR_FILE=$(mktemp)
93
+ trap 'rm -f "$STDOUT_FILE" "$STDERR_FILE"' EXIT
94
+
95
+ # Determine runner
96
+ RUNNER=""
97
+ case "$SCRIPT_NAME" in
98
+ *.mjs) RUNNER="node" ;;
99
+ *.sh) RUNNER="bash" ;;
100
+ *) RUNNER="" ;;
101
+ esac
102
+
103
+ if [[ -n "$RUNNER" ]]; then
104
+ $RUNNER "$SCRIPT_PATH" ${SCRIPT_ARGS[@]+"${SCRIPT_ARGS[@]}"} > "$STDOUT_FILE" 2> "$STDERR_FILE"
105
+ else
106
+ "$SCRIPT_PATH" ${SCRIPT_ARGS[@]+"${SCRIPT_ARGS[@]}"} > "$STDOUT_FILE" 2> "$STDERR_FILE"
107
+ fi
108
+ EXIT_CODE=$?
109
+
110
+ # ── Output ───────────────────────────────────────────────────────────
111
+
112
+ # Always show stdout
113
+ cat "$STDOUT_FILE"
114
+
115
+ if [[ $EXIT_CODE -eq 0 ]]; then
116
+ # Success — show stderr as warnings if any
117
+ if [[ -s "$STDERR_FILE" ]]; then
118
+ echo ""
119
+ echo -e "${YELLOW}Warnings (stderr):${NC}"
120
+ cat "$STDERR_FILE"
121
+ fi
122
+ exit 0
123
+ fi
124
+
125
+ # ── Failure diagnostic ───────────────────────────────────────────────
126
+
127
+ STDERR_CONTENT=$(cat "$STDERR_FILE")
128
+
129
+ echo ""
130
+ echo -e "${RED}┌─ SCRIPT FAILURE ─────────────────────────────────────────┐${NC}"
131
+ echo -e "${RED}│ Script: ${SCRIPT_NAME} ${SCRIPT_ARGS[*]+"${SCRIPT_ARGS[*]}"}${NC}"
132
+ echo -e "${RED}│ Exit Code: ${EXIT_CODE}${NC}"
133
+ echo -e "${RED}│${NC}"
134
+
135
+ # Show stderr (truncated to 20 lines)
136
+ if [[ -n "$STDERR_CONTENT" ]]; then
137
+ echo -e "${RED}│ Stderr:${NC}"
138
+ echo "$STDERR_CONTENT" | head -20 | while IFS= read -r line; do
139
+ echo -e "${RED}│ ${line}${NC}"
140
+ done
141
+ STDERR_LINES=$(echo "$STDERR_CONTENT" | wc -l | tr -d ' ')
142
+ if [[ "$STDERR_LINES" -gt 20 ]]; then
143
+ echo -e "${RED}│ ... (${STDERR_LINES} total lines, truncated)${NC}"
144
+ fi
145
+ else
146
+ echo -e "${RED}│ Stderr: (empty)${NC}"
147
+ fi
148
+
149
+ echo -e "${RED}│${NC}"
150
+
151
+ # ── Diagnosis ────────────────────────────────────────────────────────
152
+
153
+ echo -e "${RED}│ Diagnosis:${NC}"
154
+
155
+ # Check common root causes
156
+ if echo "$STDERR_CONTENT" | grep -qi "state.json not found\|state.json missing"; then
157
+ echo -e "${RED}│ Missing state.json — sprint was never initialized${NC}"
158
+ echo -e "${RED}│${NC}"
159
+ echo -e "${RED}│ Fix: Run ./.vbounce/scripts/init_sprint.mjs S-XX D-XX --stories STORY-IDS${NC}"
160
+
161
+ elif echo "$STDERR_CONTENT" | grep -qi "not valid JSON\|Unexpected token\|SyntaxError"; then
162
+ echo -e "${RED}│ state.json is corrupted (invalid JSON)${NC}"
163
+ echo -e "${RED}│${NC}"
164
+ echo -e "${RED}│ Fix: Run ./.vbounce/scripts/validate_state.mjs to see errors,${NC}"
165
+ echo -e "${RED}│ then repair or regenerate with init_sprint.mjs${NC}"
166
+
167
+ elif echo "$STDERR_CONTENT" | grep -qi "not found in state.json\|not found in stories"; then
168
+ echo -e "${RED}│ Story ID not registered in state.json${NC}"
169
+ echo -e "${RED}│${NC}"
170
+ echo -e "${RED}│ Fix: Verify the story ID, or add it via update_state.mjs${NC}"
171
+
172
+ elif echo "$STDERR_CONTENT" | grep -qi "ENOENT\|no such file"; then
173
+ echo -e "${RED}│ A required file or directory is missing${NC}"
174
+ echo -e "${RED}│${NC}"
175
+ echo -e "${RED}│ Fix: Run ./.vbounce/scripts/doctor.mjs to identify missing files${NC}"
176
+
177
+ elif echo "$STDERR_CONTENT" | grep -qi "permission denied\|EACCES"; then
178
+ echo -e "${RED}│ Permission denied on a file or directory${NC}"
179
+ echo -e "${RED}│${NC}"
180
+ echo -e "${RED}│ Fix: Check file permissions — shell scripts may need chmod +x${NC}"
181
+
182
+ elif [[ $EXIT_CODE -eq 1 ]]; then
183
+ echo -e "${RED}│ Script reported failure (exit 1) — check stderr above${NC}"
184
+ echo -e "${RED}│${NC}"
185
+ echo -e "${RED}│ Fix: Run ./.vbounce/scripts/doctor.mjs for a full health check${NC}"
186
+
187
+ else
188
+ echo -e "${RED}│ Unexpected exit code ${EXIT_CODE}${NC}"
189
+ echo -e "${RED}│${NC}"
190
+ echo -e "${RED}│ Fix: Run ./.vbounce/scripts/doctor.mjs for a full health check${NC}"
191
+ fi
192
+
193
+ echo -e "${RED}└──────────────────────────────────────────────────────────┘${NC}"
194
+
195
+ exit $EXIT_CODE
@@ -16,16 +16,12 @@ import fs from 'fs';
16
16
  import path from 'path';
17
17
  import { fileURLToPath } from 'url';
18
18
  import { validateState } from './validate_state.mjs';
19
+ import { VALID_STATES } from './constants.mjs';
19
20
 
20
21
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
22
  const ROOT = path.resolve(__dirname, '../..');
22
23
  const STATE_FILE = path.join(ROOT, '.vbounce', 'state.json');
23
24
 
24
- const VALID_STATES = [
25
- 'Draft', 'Refinement', 'Ready to Bounce', 'Bouncing',
26
- 'QA Passed', 'Architect Passed', 'Done', 'Escalated', 'Parking Lot'
27
- ];
28
-
29
25
  function readState() {
30
26
  if (!fs.existsSync(STATE_FILE)) {
31
27
  console.error(`ERROR: ${STATE_FILE} not found. Run: vbounce sprint init S-XX D-XX`);
@@ -10,16 +10,12 @@
10
10
  import fs from 'fs';
11
11
  import path from 'path';
12
12
  import { fileURLToPath } from 'url';
13
+ import { VALID_STATES } from './constants.mjs';
13
14
 
14
15
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
16
  const ROOT = path.resolve(__dirname, '../..');
16
17
  const STATE_FILE = path.join(ROOT, '.vbounce', 'state.json');
17
18
 
18
- const VALID_STATES = [
19
- 'Draft', 'Refinement', 'Ready to Bounce', 'Bouncing',
20
- 'QA Passed', 'Architect Passed', 'Done', 'Escalated', 'Parking Lot'
21
- ];
22
-
23
19
  /**
24
20
  * Validates a state object. Returns { valid, errors }.
25
21
  * @param {object} state
@@ -184,6 +184,14 @@ Examples:
184
184
  git checkout -b sprint/S-01 main
185
185
  mkdir -p .vbounce/archive
186
186
 
187
+ 1b. Initialize sprint state (MANDATORY):
188
+ ./.vbounce/scripts/run_script.sh init_sprint.mjs S-{XX} D-{NN} --stories STORY-ID1,STORY-ID2,...
189
+ - Extract story IDs and delivery ID from the confirmed Sprint Plan §1 table
190
+ - This creates .vbounce/state.json — required by all downstream scripts
191
+ - If state.json already exists for this sprint, the script will warn and overwrite
192
+ - Verify success: run_script.sh validate_state.mjs
193
+ - If this step fails, DO NOT proceed — no scripts will work without state.json
194
+
187
195
  2. Verify Sprint Plan:
188
196
  - Sprint Plan status must be "Confirmed" (human-approved in Phase 2)
189
197
  - §0 Sprint Readiness Gate must be fully checked
@@ -197,13 +205,13 @@ Examples:
197
205
  4. **Hotfix Path** (L1 Trivial tasks only — triaged during Phase 1):
198
206
  a. Create `HOTFIX-{Date}-{Name}.md` using the template.
199
207
  b. Delegate to Developer (no worktree needed if acting on active branch).
200
- c. Developer runs `hotfix_manager.sh ledger "{Title}" "{Description}"` after implementation.
208
+ c. Developer runs `run_script.sh hotfix_manager.sh ledger "{Title}" "{Description}"` after implementation.
201
209
  d. Human/Lead verifies manually.
202
- e. DevOps runs `hotfix_manager.sh sync` to update any active story worktrees.
210
+ e. DevOps runs `run_script.sh hotfix_manager.sh sync` to update any active story worktrees.
203
211
  f. Update Delivery Plan Status to "Done".
204
212
 
205
213
  5. **Gate Config Check**:
206
- - If `.vbounce/gate-checks.json` does not exist, run `./.vbounce/scripts/init_gate_config.sh` to auto-detect the project stack and generate default gate checks.
214
+ - If `.vbounce/gate-checks.json` does not exist, run `./.vbounce/scripts/run_script.sh init_gate_config.sh` to auto-detect the project stack and generate default gate checks.
207
215
  - If it exists, verify it's current (stack detection may have changed).
208
216
 
209
217
  6. **Parallel Readiness Check** (before bouncing multiple stories simultaneously):
@@ -267,7 +275,7 @@ mkdir -p .worktrees/STORY-{ID}-{StoryName}/.vbounce/{tasks,reports}
267
275
  ### Step 3: QA Pass
268
276
  ```
269
277
  0. Run pre-QA gate scan:
270
- ./.vbounce/scripts/pre_gate_runner.sh qa .worktrees/STORY-{ID}-{StoryName}/ sprint/S-{XX}
278
+ ./.vbounce/scripts/run_script.sh pre_gate_runner.sh qa .worktrees/STORY-{ID}-{StoryName}/ sprint/S-{XX}
271
279
  - If scan FAILS on trivial issues (debug statements, missing JSDoc, TODOs):
272
280
  Return to Developer for quick fix. Do NOT spawn QA for mechanical failures.
273
281
  If pre-gate scan fails 3+ times → Escalate: present failures to human with options:
@@ -293,7 +301,7 @@ mkdir -p .worktrees/STORY-{ID}-{StoryName}/.vbounce/{tasks,reports}
293
301
  ### Step 4: Architect Pass
294
302
  ```
295
303
  0. Run pre-Architect gate scan:
296
- ./.vbounce/scripts/pre_gate_runner.sh arch .worktrees/STORY-{ID}-{StoryName}/ sprint/S-{XX}
304
+ ./.vbounce/scripts/run_script.sh pre_gate_runner.sh arch .worktrees/STORY-{ID}-{StoryName}/ sprint/S-{XX}
297
305
  - If scan reveals new dependencies or structural violations:
298
306
  Return to Developer for resolution. Do NOT spawn Architect for mechanical failures.
299
307
  If pre-gate scan fails 3+ times → Escalate to human (same options as pre-QA escalation).
@@ -315,6 +323,13 @@ mkdir -p .worktrees/STORY-{ID}-{StoryName}/.vbounce/{tasks,reports}
315
323
 
316
324
  ### Step 5: Story Merge (DevOps)
317
325
  ```
326
+ 0. Verify gate reports exist (MANDATORY before merge):
327
+ - Dev report: .worktrees/STORY-{ID}-{StoryName}/.vbounce/reports/STORY-{ID}-{StoryName}-dev*.md
328
+ - QA report: .worktrees/STORY-{ID}-{StoryName}/.vbounce/reports/STORY-{ID}-{StoryName}-qa*.md
329
+ - Arch report: .worktrees/STORY-{ID}-{StoryName}/.vbounce/reports/STORY-{ID}-{StoryName}-arch*.md
330
+ If ANY report is missing, DO NOT proceed with merge.
331
+ Return to Lead with: which reports are missing and which agents need to re-run.
332
+ (Fast Track stories skip QA/Arch — only Dev report required.)
318
333
  1. Spawn devops subagent with:
319
334
  - Story ID and sprint branch name
320
335
  - All gate reports (QA PASS + Architect PASS)
@@ -375,7 +390,7 @@ This phase gives ad-hoc post-delivery feedback a proper home. Without it, users
375
390
  After ALL stories are merged into `sprint/S-01`:
376
391
  ```
377
392
  1. Spawn architect subagent on sprint/S-01 branch
378
- 2. First, Architect runs `./.vbounce/scripts/hotfix_manager.sh audit` to check for hotfix drift. If it fails, perform deep audit on flagged files.
393
+ 2. First, Architect runs `./.vbounce/scripts/run_script.sh hotfix_manager.sh audit` to check for hotfix drift. If it fails, perform deep audit on flagged files.
379
394
  3. Run Sprint Integration Audit — Deep Audit on combined changes
380
395
  4. Check for: duplicate routes, competing state, overlapping migrations
381
396
  5. If issues found:
@@ -423,14 +438,14 @@ After ALL stories are merged into `sprint/S-01`:
423
438
  7. **Framework Self-Assessment** (aggregated from agent reports):
424
439
  - Collect all `## Process Feedback` sections from agent reports in `.vbounce/archive/S-{XX}/`
425
440
  - Populate §5 Framework Self-Assessment tables in the Sprint Report by category
426
- - **Always run** `suggest_improvements.mjs` — every sprint, unconditionally. First sprints generate the most friction.
441
+ - **Always run** `run_script.sh suggest_improvements.mjs` — every sprint, unconditionally. First sprints generate the most friction.
427
442
  - **Verbally present** the top improvement suggestions to the user. Do NOT just embed them in the report — tell the user directly:
428
443
  - Summarize each P0/P1 suggestion in plain language (what's broken, why it matters, what to change)
429
444
  - For P2/P3 suggestions, give a brief list and note they're in `.vbounce/improvement-suggestions.md`
430
445
  - Ask the user: *"Want me to run `/improve` to apply any of these?"*
431
446
  - If user approves → read `.vbounce/skills/improve/SKILL.md` and execute the improvement process
432
447
  8. Product Documentation check (runs on `main` after sprint merge):
433
- a. **Staleness Detection** — run `./.vbounce/scripts/vdoc_staleness.mjs S-{XX}`
448
+ a. **Staleness Detection** — run `./.vbounce/scripts/run_script.sh vdoc_staleness.mjs S-{XX}`
434
449
  - Cross-references all Dev Reports' `files_modified` against manifest key files
435
450
  - Generates `.vbounce/scribe-task-S-{XX}.md` with targeted list of stale docs
436
451
  - Populates Sprint Report §1 "Product Docs Affected" table
@@ -558,8 +573,50 @@ If merging story branch into sprint branch creates conflicts:
558
573
 
559
574
  ---
560
575
 
576
+ ## Script Execution Protocol
577
+
578
+ **All `.vbounce/scripts/*` invocations MUST go through the wrapper:**
579
+
580
+ ```bash
581
+ ./.vbounce/scripts/run_script.sh <script-name> [args...]
582
+ ```
583
+
584
+ **Never call scripts directly.** The wrapper captures exit codes, stdout, and stderr separately, runs pre-flight checks (e.g. state.json existence), and on failure prints a structured diagnostic block with root cause and suggested fix.
585
+
586
+ ### When a Script Fails
587
+
588
+ 1. **Stop the current step.** Do not retry blindly or continue as if the script succeeded.
589
+ 2. **Read the diagnostic block.** The wrapper prints the exit code, stderr, root cause, and a suggested fix.
590
+ 3. **Attempt self-repair (once).** If the fix is within the agent's capability:
591
+ - Missing `state.json` → run `run_script.sh init_sprint.mjs S-{XX} D-{XX} --stories {IDS}`
592
+ - Invalid JSON → run `run_script.sh validate_state.mjs`, repair, retry
593
+ - Missing file/directory → run `run_script.sh doctor.mjs`, fix what's reported, retry
594
+ - Permission denied → `chmod +x` the script, retry
595
+ 4. **Re-run through the wrapper.** If the retry succeeds, continue the step. Log the failure and fix in the agent report under `## Script Incidents`.
596
+ 5. **Escalate if retry fails.** Write a **Script Failure Report** in the agent report and return it to the Lead:
597
+
598
+ ```markdown
599
+ ## Script Incidents
600
+
601
+ ### [FAIL] {script_name} {args}
602
+ - **Exit code:** {N}
603
+ - **Stderr:** {first 10 lines}
604
+ - **Root cause:** {from diagnostic block or agent analysis}
605
+ - **Self-repair attempted:** {what was tried}
606
+ - **Status:** Resolved / Escalated
607
+ - **Suggested fix:** {if escalated — what the Lead or human should do}
608
+ ```
609
+
610
+ 6. **Lead routes escalated failures:**
611
+ - Infrastructure issue (missing state, corrupt config) → Lead fixes and re-delegates
612
+ - Script bug → Lead presents to human with the Script Failure Report and the diagnostic output
613
+ - Repeated failure (same script fails 3+ times across stories) → Flag in Sprint Report §5 Framework Self-Assessment as a **Blocker**
614
+
615
+ ---
616
+
561
617
  ## Critical Rules
562
618
 
619
+ - **All scripts go through run_script.sh.** Never invoke `.vbounce/scripts/*.mjs` or `*.sh` directly. The wrapper provides error capture, pre-flight validation, and structured diagnostics that agents depend on for self-repair.
563
620
  - **The Lead never writes code.** It plans, delegates, monitors, and consolidates.
564
621
  - **Enforce Sequential Dependencies.** Never parallelize stories where one depends on the other. Wait for merge.
565
622
  - **One story = one worktree.** Never mix stories in a single worktree.