vbounce-engine 2.5.1
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 +142 -0
- package/VBOUNCE_MANIFEST.md +404 -0
- package/bin/vbounce.mjs +882 -0
- package/brains/AGENTS.md +71 -0
- package/brains/CHANGELOG.md +398 -0
- package/brains/CLAUDE.md +90 -0
- package/brains/GEMINI.md +102 -0
- package/brains/SETUP.md +195 -0
- package/brains/claude-agents/architect.md +226 -0
- package/brains/claude-agents/developer.md +133 -0
- package/brains/claude-agents/devops.md +267 -0
- package/brains/claude-agents/explorer.md +157 -0
- package/brains/claude-agents/qa.md +225 -0
- package/brains/claude-agents/scribe.md +171 -0
- package/brains/copilot/copilot-instructions.md +54 -0
- package/brains/cursor-rules/vbounce-docs.mdc +45 -0
- package/brains/cursor-rules/vbounce-process.mdc +51 -0
- package/brains/cursor-rules/vbounce-rules.mdc +29 -0
- package/brains/windsurf/.windsurfrules +35 -0
- package/docs/HOTFIX_EDGE_CASES.md +37 -0
- package/docs/IMPROVEMENT.md +46 -0
- package/docs/agent-skill-profiles.docx +0 -0
- package/docs/icons/alert.svg +1 -0
- package/docs/icons/beaker.svg +1 -0
- package/docs/icons/book.svg +1 -0
- package/docs/icons/git-branch.svg +1 -0
- package/docs/icons/git-merge.svg +1 -0
- package/docs/icons/graph.svg +1 -0
- package/docs/icons/light-bulb.svg +1 -0
- package/docs/icons/logo.svg +9 -0
- package/docs/icons/pencil.svg +1 -0
- package/docs/icons/rocket.svg +1 -0
- package/docs/icons/shield.svg +1 -0
- package/docs/icons/sync.svg +1 -0
- package/docs/icons/terminal.svg +1 -0
- package/docs/icons/tools.svg +1 -0
- package/docs/icons/zap.svg +1 -0
- package/docs/images/bounce_loop_diagram.png +0 -0
- package/docs/vbounce-os-manual.docx +0 -0
- package/package.json +48 -0
- package/scripts/close_sprint.mjs +134 -0
- package/scripts/complete_story.mjs +121 -0
- package/scripts/count_tokens.mjs +494 -0
- package/scripts/doctor.mjs +144 -0
- package/scripts/hotfix_manager.sh +157 -0
- package/scripts/init_gate_config.sh +151 -0
- package/scripts/init_sprint.mjs +129 -0
- package/scripts/post_sprint_improve.mjs +486 -0
- package/scripts/pre_gate_common.sh +576 -0
- package/scripts/pre_gate_runner.sh +176 -0
- package/scripts/prep_arch_context.mjs +178 -0
- package/scripts/prep_qa_context.mjs +152 -0
- package/scripts/prep_sprint_context.mjs +141 -0
- package/scripts/prep_sprint_summary.mjs +154 -0
- package/scripts/product_graph.mjs +387 -0
- package/scripts/product_impact.mjs +167 -0
- package/scripts/sprint_trends.mjs +160 -0
- package/scripts/suggest_improvements.mjs +363 -0
- package/scripts/update_state.mjs +132 -0
- package/scripts/validate_bounce_readiness.mjs +152 -0
- package/scripts/validate_report.mjs +165 -0
- package/scripts/validate_sprint_plan.mjs +117 -0
- package/scripts/validate_state.mjs +99 -0
- package/scripts/vdoc_match.mjs +269 -0
- package/scripts/vdoc_staleness.mjs +199 -0
- package/scripts/verify_framework.mjs +122 -0
- package/scripts/verify_framework.sh +13 -0
- package/skills/agent-team/SKILL.md +579 -0
- package/skills/agent-team/references/cleanup.md +42 -0
- package/skills/agent-team/references/delivery-sync.md +43 -0
- package/skills/agent-team/references/discovery.md +97 -0
- package/skills/agent-team/references/git-strategy.md +52 -0
- package/skills/agent-team/references/mid-sprint-triage.md +85 -0
- package/skills/agent-team/references/report-naming.md +34 -0
- package/skills/doc-manager/SKILL.md +444 -0
- package/skills/file-organization/SKILL.md +146 -0
- package/skills/file-organization/TEST-RESULTS.md +193 -0
- package/skills/file-organization/evals/evals.json +41 -0
- package/skills/file-organization/references/gitignore-template.md +53 -0
- package/skills/file-organization/references/quick-checklist.md +48 -0
- package/skills/improve/SKILL.md +296 -0
- package/skills/lesson/SKILL.md +136 -0
- package/skills/product-graph/SKILL.md +102 -0
- package/skills/react-best-practices/SKILL.md +3014 -0
- package/skills/react-best-practices/rules/_sections.md +46 -0
- package/skills/react-best-practices/rules/_template.md +28 -0
- package/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/react-best-practices/rules/advanced-init-once.md +42 -0
- package/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/skills/react-best-practices/rules/async-dependencies.md +51 -0
- package/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skills/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skills/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/skills/vibe-code-review/SKILL.md +70 -0
- package/skills/vibe-code-review/references/deep-audit.md +259 -0
- package/skills/vibe-code-review/references/pr-review.md +234 -0
- package/skills/vibe-code-review/references/quick-scan.md +178 -0
- package/skills/vibe-code-review/references/report-template.md +189 -0
- package/skills/vibe-code-review/references/trend-check.md +224 -0
- package/skills/vibe-code-review/scripts/generate-snapshot.sh +89 -0
- package/skills/vibe-code-review/scripts/pr-analyze.sh +180 -0
- package/skills/write-skill/SKILL.md +133 -0
- package/templates/bug.md +100 -0
- package/templates/change_request.md +105 -0
- package/templates/charter.md +144 -0
- package/templates/delivery_plan.md +44 -0
- package/templates/epic.md +203 -0
- package/templates/hotfix.md +58 -0
- package/templates/risk_registry.md +87 -0
- package/templates/roadmap.md +174 -0
- package/templates/spike.md +143 -0
- package/templates/sprint.md +134 -0
- package/templates/sprint_context.md +61 -0
- package/templates/sprint_report.md +215 -0
- package/templates/story.md +193 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* close_sprint.mjs
|
|
5
|
+
* Sprint close automation — validates, archives, updates state.json.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ./.vbounce/scripts/close_sprint.mjs S-05
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { spawnSync } from 'child_process';
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const ROOT = path.resolve(__dirname, '../..');
|
|
18
|
+
|
|
19
|
+
const args = process.argv.slice(2);
|
|
20
|
+
if (args.length < 1) {
|
|
21
|
+
console.error('Usage: close_sprint.mjs S-XX');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const sprintId = args[0];
|
|
26
|
+
if (!/^S-\d{2}$/.test(sprintId)) {
|
|
27
|
+
console.error(`ERROR: sprint_id "${sprintId}" must match S-XX format`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const sprintNum = sprintId.replace('S-', '');
|
|
32
|
+
const stateFile = path.join(ROOT, '.vbounce', 'state.json');
|
|
33
|
+
|
|
34
|
+
// 1. Read state.json
|
|
35
|
+
if (!fs.existsSync(stateFile)) {
|
|
36
|
+
console.error(`ERROR: .vbounce/state.json not found`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
41
|
+
|
|
42
|
+
if (state.sprint_id !== sprintId) {
|
|
43
|
+
console.error(`ERROR: state.json is for sprint ${state.sprint_id}, not ${sprintId}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 2. Check all stories are terminal
|
|
48
|
+
const activeStories = Object.entries(state.stories || {}).filter(
|
|
49
|
+
([, s]) => !['Done', 'Escalated', 'Parking Lot'].includes(s.state)
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (activeStories.length > 0) {
|
|
53
|
+
console.warn(`⚠ ${activeStories.length} stories are not in a terminal state:`);
|
|
54
|
+
activeStories.forEach(([id, s]) => console.warn(` - ${id}: ${s.state}`));
|
|
55
|
+
console.warn(' Proceed? These stories will be left incomplete.');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 3. Create archive directory
|
|
59
|
+
const archiveDir = path.join(ROOT, '.vbounce', 'archive', sprintId);
|
|
60
|
+
fs.mkdirSync(archiveDir, { recursive: true });
|
|
61
|
+
|
|
62
|
+
// 4. Move sprint report if it exists
|
|
63
|
+
const reportSrc = path.join(ROOT, '.vbounce', `sprint-report-${sprintId}.md`);
|
|
64
|
+
const reportLegacy = path.join(ROOT, '.vbounce', 'sprint-report.md');
|
|
65
|
+
const reportDst = path.join(archiveDir, `sprint-report-${sprintId}.md`);
|
|
66
|
+
|
|
67
|
+
if (fs.existsSync(reportSrc)) {
|
|
68
|
+
fs.copyFileSync(reportSrc, reportDst);
|
|
69
|
+
console.log(`✓ Archived sprint report → .vbounce/archive/${sprintId}/sprint-report-${sprintId}.md`);
|
|
70
|
+
} else if (fs.existsSync(reportLegacy)) {
|
|
71
|
+
fs.copyFileSync(reportLegacy, reportDst);
|
|
72
|
+
console.log(`✓ Archived sprint report → .vbounce/archive/${sprintId}/sprint-report-${sprintId}.md`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 5. Update state.json
|
|
76
|
+
state.last_action = `Sprint ${sprintId} closed`;
|
|
77
|
+
state.updated_at = new Date().toISOString();
|
|
78
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
79
|
+
console.log(`✓ Updated state.json`);
|
|
80
|
+
|
|
81
|
+
// 6. Print manual steps
|
|
82
|
+
const sprintPlanPath = `product_plans/sprints/sprint-${sprintNum}`;
|
|
83
|
+
const archivePath = `product_plans/archive/sprints/sprint-${sprintNum}`;
|
|
84
|
+
|
|
85
|
+
// 7. Auto-run improvement pipeline
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log('Running self-improvement pipeline...');
|
|
88
|
+
const suggestScript = path.join(__dirname, 'suggest_improvements.mjs');
|
|
89
|
+
if (fs.existsSync(suggestScript)) {
|
|
90
|
+
// Run trends first (if available)
|
|
91
|
+
const trendsScript = path.join(__dirname, 'sprint_trends.mjs');
|
|
92
|
+
if (fs.existsSync(trendsScript)) {
|
|
93
|
+
const trendsResult = spawnSync(process.execPath, [trendsScript], {
|
|
94
|
+
stdio: 'inherit',
|
|
95
|
+
cwd: process.cwd(),
|
|
96
|
+
});
|
|
97
|
+
if (trendsResult.status !== 0) {
|
|
98
|
+
console.warn(' ⚠ Trends analysis returned non-zero — continuing.');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Run suggest (which internally runs post_sprint_improve.mjs)
|
|
103
|
+
const suggestResult = spawnSync(process.execPath, [suggestScript, sprintId], {
|
|
104
|
+
stdio: 'inherit',
|
|
105
|
+
cwd: process.cwd(),
|
|
106
|
+
});
|
|
107
|
+
if (suggestResult.status !== 0) {
|
|
108
|
+
console.warn(' ⚠ Improvement suggestions returned non-zero.');
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
console.warn(' ⚠ suggest_improvements.mjs not found — skipping improvement pipeline.');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Regenerate product graph (non-blocking)
|
|
115
|
+
const graphScript = path.join(__dirname, 'product_graph.mjs');
|
|
116
|
+
if (fs.existsSync(graphScript)) {
|
|
117
|
+
const graphResult = spawnSync(process.execPath, [graphScript], { stdio: 'pipe', cwd: process.cwd() });
|
|
118
|
+
if (graphResult.status === 0) {
|
|
119
|
+
console.log('✓ Product graph regenerated');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.log('');
|
|
124
|
+
console.log('Manual steps remaining:');
|
|
125
|
+
console.log(` 1. Archive sprint plan folder:`);
|
|
126
|
+
console.log(` mv ${sprintPlanPath}/ ${archivePath}/`);
|
|
127
|
+
console.log(` 2. Update Delivery Plan §4 Completed Sprints with a summary row`);
|
|
128
|
+
console.log(` 3. Remove delivered stories from Delivery Plan §3 Backlog`);
|
|
129
|
+
console.log(` 4. Delete sprint branch (after merge to main):`);
|
|
130
|
+
console.log(` git branch -d sprint/${sprintId}`);
|
|
131
|
+
console.log(` 5. Review .vbounce/improvement-suggestions.md — approve/reject/defer each item`);
|
|
132
|
+
console.log(` 6. Run /improve to apply approved changes with brain-file sync`);
|
|
133
|
+
console.log('');
|
|
134
|
+
console.log(`✓ Sprint ${sprintId} closed.`);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* complete_story.mjs
|
|
5
|
+
* Mark a story as Done — updates Sprint Plan §1 + §4, and state.json atomically.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ./.vbounce/scripts/complete_story.mjs STORY-005-02 --qa-bounces 1 --arch-bounces 0 --correction-tax 5 --notes "Missing validation fixed"
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { fileURLToPath } from 'url';
|
|
14
|
+
import { spawnSync } from 'child_process';
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const ROOT = path.resolve(__dirname, '../..');
|
|
18
|
+
|
|
19
|
+
function parseArgs(argv) {
|
|
20
|
+
const result = { storyId: null, qaBounces: 0, archBounces: 0, correctionTax: '0%', notes: '' };
|
|
21
|
+
const args = argv.slice(2);
|
|
22
|
+
result.storyId = args[0];
|
|
23
|
+
for (let i = 1; i < args.length; i++) {
|
|
24
|
+
if (args[i] === '--qa-bounces') result.qaBounces = parseInt(args[++i], 10) || 0;
|
|
25
|
+
else if (args[i] === '--arch-bounces') result.archBounces = parseInt(args[++i], 10) || 0;
|
|
26
|
+
else if (args[i] === '--correction-tax') result.correctionTax = args[++i] + (args[i].includes('%') ? '' : '%');
|
|
27
|
+
else if (args[i] === '--notes') result.notes = args[++i];
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const { storyId, qaBounces, archBounces, correctionTax, notes } = parseArgs(process.argv);
|
|
33
|
+
|
|
34
|
+
if (!storyId) {
|
|
35
|
+
console.error('Usage: complete_story.mjs STORY-ID [--qa-bounces N] [--arch-bounces N] [--correction-tax N] [--notes "text"]');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 1. Update state.json
|
|
40
|
+
const stateFile = path.join(ROOT, '.vbounce', 'state.json');
|
|
41
|
+
if (!fs.existsSync(stateFile)) {
|
|
42
|
+
console.error('ERROR: .vbounce/state.json not found');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
46
|
+
if (!state.stories[storyId]) {
|
|
47
|
+
console.error(`ERROR: Story "${storyId}" not found in state.json`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
state.stories[storyId].state = 'Done';
|
|
51
|
+
state.stories[storyId].qa_bounces = qaBounces;
|
|
52
|
+
state.stories[storyId].arch_bounces = archBounces;
|
|
53
|
+
state.stories[storyId].worktree = null;
|
|
54
|
+
state.last_action = `${storyId} completed`;
|
|
55
|
+
state.updated_at = new Date().toISOString();
|
|
56
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
57
|
+
console.log(`✓ Updated state.json: ${storyId} → Done`);
|
|
58
|
+
|
|
59
|
+
// 2. Find sprint plan
|
|
60
|
+
const sprintNum = state.sprint_id.replace('S-', '');
|
|
61
|
+
const sprintPlanPath = path.join(ROOT, 'product_plans', 'sprints', `sprint-${sprintNum}`, `sprint-${sprintNum}.md`);
|
|
62
|
+
|
|
63
|
+
if (!fs.existsSync(sprintPlanPath)) {
|
|
64
|
+
console.warn(`⚠ Sprint plan not found at ${sprintPlanPath}. Update §1 and §4 manually.`);
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let content = fs.readFileSync(sprintPlanPath, 'utf8');
|
|
69
|
+
|
|
70
|
+
// 3. Update §1 table — find the row with storyId and change V-Bounce State to Done
|
|
71
|
+
const tableRowRegex = new RegExp(`(\\|[^|]*\\|[^|]*${storyId.replace(/[-]/g, '[-]')}[^|]*\\|[^|]*\\|[^|]*\\|)([^|]+)(\\|[^|]*\\|)`, 'g');
|
|
72
|
+
let updated = false;
|
|
73
|
+
content = content.replace(tableRowRegex, (match, before, stateCell, after) => {
|
|
74
|
+
updated = true;
|
|
75
|
+
return `${before} Done ${after}`;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (!updated) {
|
|
79
|
+
console.warn(`⚠ Could not find ${storyId} row in §1 table. Update V-Bounce State manually.`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// 4. Add row to §4 Execution Log
|
|
83
|
+
const logStart = '<!-- EXECUTION_LOG_START -->';
|
|
84
|
+
const logEnd = '<!-- EXECUTION_LOG_END -->';
|
|
85
|
+
const newRow = `| ${storyId} | Done | ${qaBounces} | ${archBounces} | ${correctionTax} | ${notes || '—'} |`;
|
|
86
|
+
|
|
87
|
+
if (content.includes(logStart)) {
|
|
88
|
+
// Find the table in the execution log section and append a row
|
|
89
|
+
const startIdx = content.indexOf(logStart);
|
|
90
|
+
const endIdx = content.indexOf(logEnd);
|
|
91
|
+
|
|
92
|
+
if (endIdx > startIdx) {
|
|
93
|
+
const before = content.substring(0, endIdx);
|
|
94
|
+
const after = content.substring(endIdx);
|
|
95
|
+
|
|
96
|
+
// Check if header row exists, if not add it
|
|
97
|
+
const section = before.substring(startIdx);
|
|
98
|
+
if (!section.includes('| Story |')) {
|
|
99
|
+
const headerRow = `\n| Story | Final State | QA Bounces | Arch Bounces | Correction Tax | Notes |\n|-------|-------------|------------|--------------|----------------|-------|`;
|
|
100
|
+
content = before + headerRow + '\n' + newRow + '\n' + after;
|
|
101
|
+
} else {
|
|
102
|
+
content = before + newRow + '\n' + after;
|
|
103
|
+
}
|
|
104
|
+
console.log(`✓ Added row to §4 Execution Log`);
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
// Append §4 section at end
|
|
108
|
+
content += `\n\n<!-- EXECUTION_LOG_START -->\n## 4. Execution Log\n\n| Story | Final State | QA Bounces | Arch Bounces | Correction Tax | Notes |\n|-------|-------------|------------|--------------|----------------|-------|\n${newRow}\n<!-- EXECUTION_LOG_END -->\n`;
|
|
109
|
+
console.log(`✓ Created §4 Execution Log with first row`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
fs.writeFileSync(sprintPlanPath, content);
|
|
113
|
+
console.log(`✓ Updated sprint plan: ${storyId} Done`);
|
|
114
|
+
console.log(`\n QA bounces: ${qaBounces} | Arch bounces: ${archBounces} | Correction tax: ${correctionTax}`);
|
|
115
|
+
|
|
116
|
+
// Regenerate product graph (non-blocking)
|
|
117
|
+
const graphScript = path.join(__dirname, 'product_graph.mjs');
|
|
118
|
+
if (fs.existsSync(graphScript)) {
|
|
119
|
+
const graphResult = spawnSync(process.execPath, [graphScript], { stdio: 'pipe', cwd: ROOT });
|
|
120
|
+
if (graphResult.status === 0) console.log('✓ Product graph regenerated');
|
|
121
|
+
}
|