bigpowers 2.0.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.
- package/CHANGELOG.md +14 -0
- package/CONVENTIONS.md +18 -2
- package/README.md +39 -13
- package/RELEASE.md +16 -1
- package/SKILL-INDEX.md +2 -2
- package/dashboard/src/tui/epic-queue.js +58 -17
- package/dashboard/src/tui/filesystem.js +0 -2
- package/dashboard/src/tui/index.js +45 -17
- package/dashboard/src/tui/ledger.js +23 -28
- package/dashboard/src/tui/metrics-bar.js +69 -17
- package/dashboard/src/tui/pipeline.js +19 -27
- package/dashboard/src/tui/state-yaml.js +45 -27
- package/orchestrate-project/REFERENCE.md +8 -4
- package/package.json +1 -1
- package/seed-conventions/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [2.1.0](https://github.com/danielvm-git/bigpowers/compare/v2.0.1...v2.1.0) (2026-06-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **dashboard/tui:** render blessed tags and improve layout ([fba0e49](https://github.com/danielvm-git/bigpowers/commit/fba0e4918a4d99c94c30e595378565263d72734d))
|
|
7
|
+
|
|
8
|
+
## [2.0.1](https://github.com/danielvm-git/bigpowers/compare/v2.0.0...v2.0.1) (2026-06-11)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **dashboard/tui:** render blessed tags and improve layout ([ca3a161](https://github.com/danielvm-git/bigpowers/commit/ca3a1617a341d05dacf3b571abf58cb6c0cfceeb))
|
|
14
|
+
|
|
1
15
|
# [2.0.0](https://github.com/danielvm-git/bigpowers/compare/v1.5.1...v2.0.0) (2026-06-11)
|
|
2
16
|
|
|
3
17
|
|
package/CONVENTIONS.md
CHANGED
|
@@ -49,13 +49,29 @@ Every skill that produces written output writes to `specs/` at the project root.
|
|
|
49
49
|
|
|
50
50
|
| Layer | File | Answers |
|
|
51
51
|
|-------|------|---------|
|
|
52
|
-
| Session | `specs/state.yaml` | Active flow, epic/bug, ship-epic step, git |
|
|
53
|
-
| Release index | `specs/release-plan.yaml` | Target semver, WSJF epic list,
|
|
52
|
+
| Session | `specs/state.yaml` | Active flow, epic/bug, ship-epic step, git, `handoff.next_skill`, `metrics.story_start` |
|
|
53
|
+
| Release index | `specs/release-plan.yaml` | Target semver, WSJF epic list, BCP baseline per story |
|
|
54
54
|
| Progress | `specs/execution-status.yaml` | Flat status keys (`e01`, `e01s01`) — sole SoT for story state |
|
|
55
|
+
| Cycle-time ledger | `specs/metrics/cycle-times.yaml` | Per-story: BCPs, start, end, cycle minutes, BCP/hr **(v2.0.0)** |
|
|
55
56
|
| Planning UI | `specs/planning-status.yaml` | Discover-phase workflow checklist (optional) |
|
|
56
57
|
|
|
57
58
|
**Do not** put story status in `release-plan.yaml`. **Do not** duplicate the release plan inside `state.yaml`.
|
|
58
59
|
|
|
60
|
+
### BCP accounting mandate (v2.0.0)
|
|
61
|
+
|
|
62
|
+
Every task written by `plan-work` MUST be labeled `[BCP N]` where N is the estimated Build Commit Points for that task. The story total is summed and written to `state.yaml` as `epic_cycle.story_bcps`. The BCP baseline for each story MUST appear in `specs/release-plan.yaml` before implementation begins. `release-branch` automatically appends a row to `specs/metrics/cycle-times.yaml` with the final BCP/hr after the story lands.
|
|
63
|
+
|
|
64
|
+
### Timestamp mandate (v2.0.0)
|
|
65
|
+
|
|
66
|
+
- `survey-context` MUST write `metrics.story_start` (ISO 8601) to `specs/state.yaml` at the start of every story.
|
|
67
|
+
- `release-branch` MUST write `metrics.story_end`, `metrics.cycle_minutes`, and `metrics.bcp_per_hour` to `specs/state.yaml` and append a row to `specs/metrics/cycle-times.yaml` when the story lands.
|
|
68
|
+
|
|
69
|
+
Missing timestamps are a gate violation — do not advance past `release-branch` without them.
|
|
70
|
+
|
|
71
|
+
### next_skill signaling mandate (v2.0.0)
|
|
72
|
+
|
|
73
|
+
Every critical-path skill (survey-context, plan-work, kickoff-branch, develop-tdd, verify-work, audit-code, commit-message, release-branch) MUST write `handoff.next_skill` to `specs/state.yaml` as its last action. Agents MUST read `state.yaml` and follow `handoff.next_skill` before asking "what comes next?".
|
|
74
|
+
|
|
59
75
|
### Intent vs delivery vs execution
|
|
60
76
|
|
|
61
77
|
| Question | File | Format |
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
`bigpowers` provides a prescriptive, vertical-slice methodology for building software with AI agents (Claude Code, Gemini CLI, Cursor). It bridges the gap between raw LLM capabilities and professional engineering standards.
|
|
10
10
|
|
|
11
|
-
Published on npm: [bigpowers@
|
|
11
|
+
Published on npm: [bigpowers@2.0.0](https://www.npmjs.com/package/bigpowers)
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -96,16 +96,39 @@ bigpowers
|
|
|
96
96
|
|
|
97
97
|
---
|
|
98
98
|
|
|
99
|
-
## 🏗 The
|
|
99
|
+
## 🏗 The v2.0.0 Lifecycle
|
|
100
100
|
|
|
101
|
-
Every
|
|
101
|
+
Every project follows the **orchestrate-project 6-phase model** (full SOP: [`docs/WORKFLOW-SOP-v2.md`](docs/WORKFLOW-SOP-v2.md)):
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
103
|
+
```
|
|
104
|
+
ONE TIME seed-conventions (CLAUDE.md, .claude/, .gemini/, agents/, skill sync)
|
|
105
|
+
↓
|
|
106
|
+
ONCE/PROJECT orchestrate-project
|
|
107
|
+
│
|
|
108
|
+
├─ Ph1 DISCOVER survey-context, research-first, elaborate-spec
|
|
109
|
+
├─ Ph2 ELABORATE model-domain, grill-me, define-language, deepen-architecture
|
|
110
|
+
├─ Ph3 PLAN scope-work, slice-tasks, plan-work → release-plan.yaml (BCP baseline)
|
|
111
|
+
├─ Ph4 BUILD build-epic × N stories
|
|
112
|
+
│
|
|
113
|
+
│ Per story — 8-step build-epic cycle:
|
|
114
|
+
│ 1. survey-context ← stamps story_start in state.yaml
|
|
115
|
+
│ 2. plan-work ← [BCP N] tasks + verify: commands
|
|
116
|
+
│ 3. kickoff-branch ← worktree + feature branch
|
|
117
|
+
│ 4. develop-tdd ← RED → GREEN → REFACTOR
|
|
118
|
+
│ 5. verify-work ← UAT gate
|
|
119
|
+
│ 6. audit-code ← quality gate ≥ 94%
|
|
120
|
+
│ 7. commit-message ← Conventional Commits + semver
|
|
121
|
+
│ 8. release-branch ← land to main; writes story_end + cycle-times.yaml
|
|
122
|
+
│
|
|
123
|
+
├─ Ph5 VERIFY run-evals, verify-work (project-level)
|
|
124
|
+
└─ Ph6 RELEASE semantic-release → v1.0.0 MVP tag
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Semver:** projects start at `0.0.0-β`; each `feat:` story → minor bump; developer declares MVP → `1.0.0`.
|
|
128
|
+
|
|
129
|
+
**BCP accounting:** every task labeled `[BCP N]`; story total in `state.yaml`; BCP/hr logged to `specs/metrics/cycle-times.yaml`.
|
|
130
|
+
|
|
131
|
+
**next_skill signaling:** each critical-path skill writes `handoff.next_skill` to `state.yaml`. Call `survey-context` after any interruption to resume exactly where you left off.
|
|
109
132
|
|
|
110
133
|
---
|
|
111
134
|
|
|
@@ -118,8 +141,9 @@ Every task in `bigpowers` follows a prescriptive lifecycle (see `SKILL-INDEX.md`
|
|
|
118
141
|
| **Scope** | `specs/requirements/SCOPE_LATEST.yaml` | In-scope / out-of-scope and success criteria. |
|
|
119
142
|
| **Vision** | `specs/requirements/VISION_LATEST.yaml` | North star and initiative success criteria. |
|
|
120
143
|
| **Decisions** | `specs/adr/` | Architectural Decision Records (irreversible choices). |
|
|
121
|
-
| **Roadmap** | `specs/release-plan.yaml` + `specs/epics/` | WSJF-prioritized epics and stories. |
|
|
122
|
-
| **Current** | `specs/state.yaml` | Session flow, active epic, handoff. |
|
|
144
|
+
| **Roadmap** | `specs/release-plan.yaml` + `specs/epics/` | WSJF-prioritized epics and stories with BCP baseline. |
|
|
145
|
+
| **Current** | `specs/state.yaml` | Session flow, active epic, `handoff.next_skill`, timestamps. |
|
|
146
|
+
| **Metrics** | `specs/metrics/cycle-times.yaml` | Per-story BCPs, cycle minutes, BCP/hr (v2.0.0). |
|
|
123
147
|
| **Index** | `SKILL-INDEX.md` | Canonical list of all active skills. |
|
|
124
148
|
| **Style** | `CONVENTIONS.md` | Coding, testing, and naming standards. |
|
|
125
149
|
|
|
@@ -128,8 +152,10 @@ Every task in `bigpowers` follows a prescriptive lifecycle (see `SKILL-INDEX.md`
|
|
|
128
152
|
## 📁 Project Structure
|
|
129
153
|
|
|
130
154
|
- `scripts/`: Installation, syncing, and compliance tools.
|
|
131
|
-
- `specs/`:
|
|
132
|
-
- `specs/
|
|
155
|
+
- `specs/`: YAML cockpit — `state.yaml`, `release-plan.yaml`, `epics/`, `execution-status.yaml`, `requirements/`.
|
|
156
|
+
- `specs/metrics/`: Cycle-time ledger (`cycle-times.yaml`) — per-story BCPs, timestamps, BCP/hr (v2.0.0).
|
|
157
|
+
- `dashboard/`: Live monitoring tool — TUI (`npm run dashboard`) and web (`npm run dashboard:web`, port 7742).
|
|
158
|
+
- `docs/`: Guides including `WORKFLOW-SOP-v2.md` (full SDLC SOP) and `using-bigpowers.md`.
|
|
133
159
|
- `docs/references/`: Theoretical foundations (Uncle Bob, Ousterhout, Karpathy, etc.).
|
|
134
160
|
- `[skill-name]/`: Source files for each of the 61 skills.
|
|
135
161
|
|
package/RELEASE.md
CHANGED
|
@@ -48,12 +48,27 @@ npm view bigpowers
|
|
|
48
48
|
# GitHub → Releases
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
## Per-Project Semver Convention (v2.0.0)
|
|
52
|
+
|
|
53
|
+
When using bigpowers to build a *project* (not the bigpowers package itself), the recommended semver lifecycle is:
|
|
54
|
+
|
|
55
|
+
| Stage | Version | How |
|
|
56
|
+
|-------|---------|-----|
|
|
57
|
+
| Pre-delivery | `0.0.0-β` | Initial state after `seed-conventions` |
|
|
58
|
+
| Each `feat:` story lands | `0.1.0`, `0.2.0`, … | `semantic-release` minor bump |
|
|
59
|
+
| Developer declares MVP | `1.0.0` | Allow the `1.0.0` tag in release config |
|
|
60
|
+
| Post-MVP features | `1.1.0`, `1.2.0`, … | Normal semver from Conventional Commits |
|
|
61
|
+
|
|
62
|
+
> This keeps all pre-MVP work in the `0.x.x` range, making the `1.0.0` tag a meaningful project milestone rather than an arbitrary number.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
51
66
|
## Commit Message Format
|
|
52
67
|
|
|
53
68
|
**Required for automatic releases:**
|
|
54
69
|
|
|
55
70
|
```
|
|
56
|
-
feat(scope): description # Minor version bump (1.0.0 → 1.1.0)
|
|
71
|
+
feat(scope): description # Minor version bump (0.1.0 → 0.2.0 or 1.0.0 → 1.1.0)
|
|
57
72
|
fix(scope): description # Patch version bump (1.0.0 → 1.0.1)
|
|
58
73
|
docs: description # No version bump
|
|
59
74
|
```
|
package/SKILL-INDEX.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Purpose:** One canonical reference for all bigpowers skills. Referenced by README.md, RELEASE-PLAN.md, and CONVENTIONS.md. Updated per-release.
|
|
4
4
|
|
|
5
|
-
**Last updated:** 2026-06-11 (
|
|
5
|
+
**Last updated:** 2026-06-11 (v2.0.0 — verified 61 active skills; 18 sub-op concepts absorbed as sub-sections into parent SKILL.md files; count confirmed accurate)
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
| 49 | Utility | `edit-document` | Edit documents in specs/ | specs/<name>.md | ✅ Active |
|
|
85
85
|
| 50 | Utility | `session-state` | Track decisions in state.yaml; absorbs show-state, reset-state, compact-state, list-epics, check-gates | state.yaml | ✅ Active |
|
|
86
86
|
| 51 | Utility | `migrate-spec` | Migrate foreign spec formats | specs/ | ✅ Active |
|
|
87
|
-
| 52 | Utility | `visual-dashboard` | Browser dashboard |
|
|
87
|
+
| 52 | Utility | `visual-dashboard` | Browser dashboard (HTTP cockpit) | `npm run visual-dashboard` → GET /cockpit.html | ✅ Active |
|
|
88
88
|
| 53 | Utility | `write-document` | BMAD technical documents | specs/<name>.md | ✅ Active |
|
|
89
89
|
| 54 | Utility | `setup-environment` | Pre-install deps before work | (.env, packages) | ✅ Active |
|
|
90
90
|
| 55 | Utility | `reset-baseline` | Restore clean known state | (clean tree) | ✅ Active |
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const { gateIcon, gateColor } = require('../loaders/gate-status');
|
|
2
|
-
|
|
3
1
|
function renderEpicQueue(box, epics, executionStatus) {
|
|
4
2
|
if (!box || typeof box.setContent !== 'function') {
|
|
5
3
|
return;
|
|
@@ -10,25 +8,68 @@ function renderEpicQueue(box, epics, executionStatus) {
|
|
|
10
8
|
return;
|
|
11
9
|
}
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const statusMap = executionStatus?.epics || new Map();
|
|
12
|
+
const doneStatuses = new Set(['done', 'complete', 'completed', 'released']);
|
|
13
|
+
const activeStatuses = new Set(['in_progress', 'running', 'active']);
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
function dot(id) {
|
|
16
|
+
const s = statusMap.get(id) || '';
|
|
17
|
+
if (doneStatuses.has(s)) return '{green-fg}●{/green-fg}';
|
|
18
|
+
if (activeStatuses.has(s)) return '{yellow-fg}●{/yellow-fg}';
|
|
19
|
+
return '{dim}·{/dim}';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function epicDot(epic) {
|
|
23
|
+
const s = statusMap.get(epic.id) || '';
|
|
24
|
+
if (doneStatuses.has(s)) return '{green-fg}●{/green-fg}';
|
|
25
|
+
if (activeStatuses.has(s)) return '{yellow-fg}●{/yellow-fg}';
|
|
26
|
+
const stories = epic.stories || [];
|
|
27
|
+
if (stories.length > 0 && stories.every(st => doneStatuses.has(statusMap.get(st.id) || ''))) {
|
|
28
|
+
return '{green-fg}●{/green-fg}';
|
|
27
29
|
}
|
|
30
|
+
return '{dim}·{/dim}';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const lines = [];
|
|
34
|
+
let grandTotalBcps = 0;
|
|
35
|
+
const perEpicBcp = [];
|
|
36
|
+
|
|
37
|
+
epics.forEach(epic => {
|
|
38
|
+
lines.push(`${epicDot(epic)} {bold}${epic.id}{/bold} {dim}·{/dim} ${epic.title || '—'}`);
|
|
39
|
+
|
|
40
|
+
const stories = epic.stories || [];
|
|
41
|
+
let epicBcpDone = 0;
|
|
42
|
+
let epicBcpTotal = 0;
|
|
43
|
+
let epicStoriesDone = 0;
|
|
44
|
+
|
|
45
|
+
stories.forEach(story => {
|
|
46
|
+
const bcp = story.bcps || 0;
|
|
47
|
+
epicBcpTotal += bcp;
|
|
48
|
+
grandTotalBcps += bcp;
|
|
49
|
+
const isDone = doneStatuses.has(statusMap.get(story.id) || '');
|
|
50
|
+
if (isDone) { epicBcpDone += bcp; epicStoriesDone++; }
|
|
51
|
+
|
|
52
|
+
const bcpStr = isDone
|
|
53
|
+
? `{green-fg}${bcp} BCP{/green-fg}`
|
|
54
|
+
: `{dim}${bcp} BCP{/dim}`;
|
|
55
|
+
lines.push(` ${dot(story.id)} {dim}${story.id}{/dim} ${story.title || '—'} ${bcpStr}`);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
perEpicBcp.push({ id: epic.id, bcps: epicBcpTotal });
|
|
59
|
+
|
|
60
|
+
const bcpProgressStr = epicBcpDone > 0
|
|
61
|
+
? `{green-fg}${epicBcpDone}{/green-fg}{dim}/${epicBcpTotal} BCP{/dim}`
|
|
62
|
+
: `{dim}0/${epicBcpTotal} BCP{/dim}`;
|
|
63
|
+
lines.push(` {dim}${epicStoriesDone}/${stories.length} done · ${bcpProgressStr}`);
|
|
64
|
+
lines.push('');
|
|
28
65
|
});
|
|
29
66
|
|
|
30
|
-
|
|
31
|
-
lines.push(
|
|
67
|
+
// Release baseline footer
|
|
68
|
+
lines.push('{dim}─────────────────────────{/dim}');
|
|
69
|
+
lines.push(`{dim}release baseline{/dim}`);
|
|
70
|
+
lines.push(`{dim}total: {/dim}{yellow-fg}${grandTotalBcps} BCPs{/yellow-fg}`);
|
|
71
|
+
lines.push(perEpicBcp.map(e => `{dim}${e.id}: ${e.bcps} BCP{/dim}`).join(' '));
|
|
72
|
+
lines.push(`{dim}target: {/dim}{bold}v2.0.0{/bold}`);
|
|
32
73
|
|
|
33
74
|
box.setContent(lines.join('\n'));
|
|
34
75
|
}
|
|
@@ -9,6 +9,9 @@ const { renderStateYaml } = require('./state-yaml');
|
|
|
9
9
|
const { renderFilesystem } = require('./filesystem');
|
|
10
10
|
const { renderLedger } = require('./ledger');
|
|
11
11
|
|
|
12
|
+
// Blessed box defaults — tags must be enabled for markup to render
|
|
13
|
+
const BOX_DEFAULTS = { tags: true };
|
|
14
|
+
|
|
12
15
|
let blessed;
|
|
13
16
|
try {
|
|
14
17
|
blessed = require('blessed');
|
|
@@ -32,15 +35,16 @@ function start(projectRoot) {
|
|
|
32
35
|
});
|
|
33
36
|
|
|
34
37
|
// Check terminal size
|
|
35
|
-
if (screen.width < 120 || screen.height <
|
|
38
|
+
if (screen.width < 120 || screen.height < 34) {
|
|
36
39
|
const msg = blessed.box({
|
|
37
40
|
parent: screen,
|
|
38
41
|
top: 'center',
|
|
39
42
|
left: 'center',
|
|
40
43
|
width: 60,
|
|
41
44
|
height: 10,
|
|
42
|
-
content: '{center}Terminal must be at least
|
|
45
|
+
content: '{center}Terminal must be at least 120x34{/center}\n{center}Please resize and try again{/center}',
|
|
43
46
|
border: 'line',
|
|
47
|
+
tags: true,
|
|
44
48
|
style: { border: { fg: 'yellow' } }
|
|
45
49
|
});
|
|
46
50
|
screen.render();
|
|
@@ -48,76 +52,97 @@ function start(projectRoot) {
|
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
// Create layout zones
|
|
51
|
-
const
|
|
55
|
+
const titleBar = blessed.box({
|
|
52
56
|
parent: screen,
|
|
53
57
|
top: 0,
|
|
54
58
|
left: 0,
|
|
55
59
|
width: '100%',
|
|
56
|
-
height:
|
|
60
|
+
height: 1,
|
|
61
|
+
tags: true,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// height 5 = 3 content lines + top/bottom borders: status bar, step info, stats row
|
|
65
|
+
const metricsBar = blessed.box({
|
|
66
|
+
parent: screen,
|
|
67
|
+
top: 1,
|
|
68
|
+
left: 0,
|
|
69
|
+
width: '100%',
|
|
70
|
+
height: 5,
|
|
57
71
|
border: 'line',
|
|
72
|
+
tags: true,
|
|
58
73
|
style: { border: { fg: 'blue' } }
|
|
59
74
|
});
|
|
60
75
|
|
|
61
76
|
const pipeline = blessed.box({
|
|
62
77
|
parent: screen,
|
|
63
|
-
top:
|
|
78
|
+
top: 6,
|
|
64
79
|
left: 0,
|
|
65
80
|
width: '100%',
|
|
66
81
|
height: 5,
|
|
67
82
|
border: 'line',
|
|
68
|
-
|
|
83
|
+
tags: true,
|
|
84
|
+
label: ' {green-fg}≡ PIPELINE{/green-fg} ',
|
|
85
|
+
style: { border: { fg: 'green' } }
|
|
69
86
|
});
|
|
70
87
|
|
|
71
88
|
const epicQueue = blessed.box({
|
|
72
89
|
parent: screen,
|
|
73
|
-
top:
|
|
90
|
+
top: 11,
|
|
74
91
|
left: 0,
|
|
75
92
|
width: '33%',
|
|
76
|
-
height: screen.height -
|
|
93
|
+
height: screen.height - 23,
|
|
77
94
|
border: 'line',
|
|
78
95
|
scrollable: true,
|
|
79
96
|
mouse: true,
|
|
80
97
|
keys: true,
|
|
98
|
+
tags: true,
|
|
99
|
+
label: ' {green-fg}≡ EPIC QUEUE{/green-fg} ',
|
|
81
100
|
style: { border: { fg: 'green' } }
|
|
82
101
|
});
|
|
83
102
|
|
|
84
103
|
const actionLog = blessed.box({
|
|
85
104
|
parent: screen,
|
|
86
|
-
top:
|
|
105
|
+
top: 11,
|
|
87
106
|
left: '33%',
|
|
88
107
|
width: '33%',
|
|
89
|
-
height: screen.height -
|
|
108
|
+
height: screen.height - 23,
|
|
90
109
|
border: 'line',
|
|
91
110
|
scrollable: true,
|
|
92
111
|
mouse: true,
|
|
93
112
|
keys: true,
|
|
94
|
-
|
|
113
|
+
tags: true,
|
|
114
|
+
label: ' {cyan-fg}>_ STATE.YAML{/cyan-fg} ',
|
|
115
|
+
style: { border: { fg: 'cyan' } }
|
|
95
116
|
});
|
|
96
117
|
|
|
97
118
|
const fsPanel = blessed.box({
|
|
98
119
|
parent: screen,
|
|
99
|
-
top:
|
|
120
|
+
top: 11,
|
|
100
121
|
left: '66%',
|
|
101
122
|
width: '34%',
|
|
102
|
-
height: screen.height -
|
|
123
|
+
height: screen.height - 23,
|
|
103
124
|
border: 'line',
|
|
104
125
|
scrollable: true,
|
|
105
126
|
mouse: true,
|
|
106
127
|
keys: true,
|
|
128
|
+
tags: true,
|
|
129
|
+
label: ' {magenta-fg}□ FILESYSTEM{/magenta-fg} ',
|
|
107
130
|
style: { border: { fg: 'magenta' } }
|
|
108
131
|
});
|
|
109
132
|
|
|
110
133
|
const ledger = blessed.box({
|
|
111
134
|
parent: screen,
|
|
112
|
-
top: screen.height -
|
|
135
|
+
top: screen.height - 11,
|
|
113
136
|
left: 0,
|
|
114
137
|
width: '100%',
|
|
115
|
-
height:
|
|
138
|
+
height: 11,
|
|
116
139
|
border: 'line',
|
|
117
140
|
scrollable: true,
|
|
118
141
|
mouse: true,
|
|
119
142
|
keys: true,
|
|
120
|
-
|
|
143
|
+
tags: true,
|
|
144
|
+
label: ' {cyan-fg}⊙ CYCLE TIME LEDGER — BCPS · TIMESTAMPS · THROUGHPUT{/cyan-fg} ',
|
|
145
|
+
style: { border: { fg: 'cyan' } }
|
|
121
146
|
});
|
|
122
147
|
|
|
123
148
|
// Refresh function
|
|
@@ -128,7 +153,10 @@ function start(projectRoot) {
|
|
|
128
153
|
const cycleTimes = readCycleTimes(projectRoot);
|
|
129
154
|
const metrics = computeProjectMetrics(cycleTimes);
|
|
130
155
|
|
|
131
|
-
|
|
156
|
+
// Title bar: fixed project identity
|
|
157
|
+
titleBar.setContent(' {bold}{cyan-fg}⚙ bigpowers factory{/cyan-fg}{/bold} {dim}v2 — seed {cyan-fg}→{/cyan-fg} epics {cyan-fg}→{/cyan-fg} mvp{/dim}');
|
|
158
|
+
|
|
159
|
+
renderMetricsBar(metricsBar, metrics, stateData, epics, cycleTimes);
|
|
132
160
|
renderPipeline(pipeline, stateData);
|
|
133
161
|
renderEpicQueue(epicQueue, epics, executionStatus);
|
|
134
162
|
renderStateYaml(actionLog, stateData);
|
|
@@ -1,61 +1,56 @@
|
|
|
1
|
+
function pad(str, width) {
|
|
2
|
+
const s = String(str);
|
|
3
|
+
return s.length >= width ? s : s + ' '.repeat(width - s.length);
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
function renderLedger(box, cycleTimes) {
|
|
2
7
|
if (!box || typeof box.setContent !== 'function') {
|
|
3
8
|
return;
|
|
4
9
|
}
|
|
5
10
|
|
|
6
11
|
if (!cycleTimes || cycleTimes.length === 0) {
|
|
7
|
-
box.setContent('{
|
|
12
|
+
box.setContent('{dim}stories complete here as they land{/dim}');
|
|
8
13
|
return;
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
const lines = [];
|
|
12
|
-
lines.push('{bold}{cyan}Ledger{/cyan}{/bold}');
|
|
13
|
-
lines.push('');
|
|
14
17
|
|
|
15
|
-
//
|
|
18
|
+
// Column widths
|
|
19
|
+
const W = { sid: 12, epic: 6, bcps: 5, min: 8, bhr: 7 };
|
|
20
|
+
|
|
21
|
+
// Header
|
|
16
22
|
lines.push(
|
|
17
|
-
|
|
18
|
-
'{cyan}Story ID{/cyan} ' +
|
|
19
|
-
'{cyan}Epic{/cyan} ' +
|
|
20
|
-
'{cyan}BCPs{/cyan} ' +
|
|
21
|
-
'{cyan}Minutes{/cyan} ' +
|
|
22
|
-
'{cyan}BCP/hr{/cyan}' +
|
|
23
|
-
'{/bold}'
|
|
23
|
+
`{cyan-fg}{bold}${pad('Story ID', W.sid)}${pad('Epic', W.epic)}${pad('BCPs', W.bcps)}${pad('Minutes', W.min)}${pad('BCP/hr', W.bhr)}{/bold}{/cyan-fg}`
|
|
24
24
|
);
|
|
25
|
+
lines.push('{dim}' + '─'.repeat(W.sid + W.epic + W.bcps + W.min + W.bhr) + '{/dim}');
|
|
25
26
|
|
|
26
27
|
let totalBCPs = 0;
|
|
27
28
|
let totalMinutes = 0;
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
cycleTimes.forEach((cycle) => {
|
|
30
|
+
cycleTimes.forEach(cycle => {
|
|
31
31
|
const storyId = cycle.id || '—';
|
|
32
|
-
const
|
|
32
|
+
const epicId = cycle.epic || '—';
|
|
33
33
|
const bcps = cycle.bcps || 0;
|
|
34
34
|
const minutes = cycle.cycleMin || 0;
|
|
35
|
-
const
|
|
35
|
+
const bph = minutes > 0 ? ((bcps * 60) / minutes).toFixed(1) : '—';
|
|
36
36
|
|
|
37
37
|
totalBCPs += bcps;
|
|
38
38
|
totalMinutes += minutes;
|
|
39
39
|
|
|
40
40
|
lines.push(
|
|
41
|
-
`
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
`{white-fg}${pad(storyId, W.sid)}{/white-fg}` +
|
|
42
|
+
`{dim}${pad(epicId, W.epic)}{/dim}` +
|
|
43
|
+
`{yellow-fg}${pad(bcps, W.bcps)}{/yellow-fg}` +
|
|
44
|
+
`{dim}${pad(minutes, W.min)}{/dim}` +
|
|
45
|
+
`{green-fg}${pad(bph, W.bhr)}{/green-fg}`
|
|
46
46
|
);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
lines.push('');
|
|
50
|
-
|
|
51
|
-
// Totals row
|
|
52
|
-
const avgBcpPerHour =
|
|
53
|
-
totalMinutes > 0 ? ((totalBCPs * 60) / totalMinutes).toFixed(1) : '—';
|
|
49
|
+
lines.push('{dim}' + '─'.repeat(W.sid + W.epic + W.bcps + W.min + W.bhr) + '{/dim}');
|
|
54
50
|
|
|
51
|
+
const avgBph = totalMinutes > 0 ? ((totalBCPs * 60) / totalMinutes).toFixed(1) : '—';
|
|
55
52
|
lines.push(
|
|
56
|
-
|
|
57
|
-
`TOTAL — ${totalBCPs} ${totalMinutes} ${avgBcpPerHour}` +
|
|
58
|
-
'{/yellow}{/bold}'
|
|
53
|
+
`{bold}{yellow-fg}${pad('TOTAL', W.sid)}${pad('—', W.epic)}${pad(totalBCPs, W.bcps)}${pad(totalMinutes, W.min)}${pad(avgBph, W.bhr)}{/yellow-fg}{/bold}`
|
|
59
54
|
);
|
|
60
55
|
|
|
61
56
|
box.setContent(lines.join('\n'));
|
|
@@ -1,30 +1,82 @@
|
|
|
1
|
-
function renderMetricsBar(box, projectMetrics, stateData) {
|
|
1
|
+
function renderMetricsBar(box, projectMetrics, stateData, epics, cycleTimes) {
|
|
2
2
|
if (!box || typeof box.setContent !== 'function') {
|
|
3
3
|
return;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const avgBcpPerHour = projectMetrics?.avgBcpPerHour ?? '-';
|
|
9
|
-
const version = stateData?.release?.target_version ?? '-';
|
|
6
|
+
const ct = cycleTimes || [];
|
|
7
|
+
const epicList = epics && Array.isArray(epics) ? epics : [];
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
bcpHrColor = 'red';
|
|
9
|
+
// Compute totals
|
|
10
|
+
let totalStories = 0;
|
|
11
|
+
let targetBcps = 0;
|
|
12
|
+
epicList.forEach(epic => {
|
|
13
|
+
if (epic.stories && Array.isArray(epic.stories)) {
|
|
14
|
+
totalStories += epic.stories.length;
|
|
15
|
+
epic.stories.forEach(s => { targetBcps += s.bcps || 0; });
|
|
19
16
|
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const doneStories = ct.length;
|
|
20
|
+
const totalEpics = epicList.length;
|
|
21
|
+
const doneStoryIds = new Set(ct.map(c => c.id));
|
|
22
|
+
const doneEpics = epicList.filter(e =>
|
|
23
|
+
e.stories && e.stories.length > 0 && e.stories.every(s => doneStoryIds.has(s.id))
|
|
24
|
+
).length;
|
|
25
|
+
|
|
26
|
+
const deliveredBcps = projectMetrics?.totalBcps ?? 0;
|
|
27
|
+
const totalMin = projectMetrics?.totalMin ?? 0;
|
|
28
|
+
const avgBcpPerHour = projectMetrics?.avgBcpPerHour;
|
|
29
|
+
const version = stateData?.release?.target_version ?? '—';
|
|
30
|
+
const branch = stateData?.gitBranch ?? 'main';
|
|
31
|
+
|
|
32
|
+
// BCP/hr color threshold
|
|
33
|
+
let bphColor = 'white';
|
|
34
|
+
if (typeof avgBcpPerHour === 'number') {
|
|
35
|
+
if (avgBcpPerHour >= 2.0) bphColor = 'green';
|
|
36
|
+
else if (avgBcpPerHour >= 1.0) bphColor = 'yellow';
|
|
37
|
+
else bphColor = 'red';
|
|
20
38
|
}
|
|
39
|
+
const bphDisplay = typeof avgBcpPerHour === 'number' ? avgBcpPerHour.toFixed(2) : '—';
|
|
40
|
+
const cycleDisplay = totalMin > 0 ? `${totalMin}m` : '—';
|
|
41
|
+
|
|
42
|
+
// ── Line 1: status bar ──
|
|
43
|
+
const statusLine =
|
|
44
|
+
`{dim}BCPs:{/dim} {yellow-fg}${deliveredBcps}{/yellow-fg}{dim}/${targetBcps}{/dim}` +
|
|
45
|
+
` {dim}│{/dim} {dim}Cycle:{/dim} ${cycleDisplay}` +
|
|
46
|
+
` {dim}│{/dim} {dim}BCP/hr:{/dim} {${bphColor}-fg}${bphDisplay}{/${bphColor}-fg}` +
|
|
47
|
+
` {dim}│{/dim} {dim}v{/dim}{green-fg}${version}{/green-fg}` +
|
|
48
|
+
`{|} {dim}branch:{/dim} {green-fg}${branch}{/green-fg} `;
|
|
49
|
+
|
|
50
|
+
// ── Line 2: step info ──
|
|
51
|
+
const currentStep = stateData?.epicCycle?.current_step;
|
|
52
|
+
const activeStory = stateData?.activeStory ?? '—';
|
|
53
|
+
const activeEpic = stateData?.activeEpic ?? '—';
|
|
54
|
+
const activeFlow = stateData?.activeFlow ?? '—';
|
|
21
55
|
|
|
22
|
-
const
|
|
23
|
-
const
|
|
56
|
+
const STEP_NAMES = ['survey-context','plan-work','kickoff-branch','develop-tdd','verify-work','audit-code','commit-message','release-branch'];
|
|
57
|
+
const stepName = typeof currentStep === 'number' ? (STEP_NAMES[currentStep] ?? '—') : (currentStep ?? '—');
|
|
58
|
+
const isActive = stepName !== '—' && activeStory !== '—';
|
|
59
|
+
const statusWord = isActive
|
|
60
|
+
? `{yellow-fg}running{/yellow-fg}`
|
|
61
|
+
: `{green-fg}ready{/green-fg}`;
|
|
62
|
+
const storyDesc = isActive
|
|
63
|
+
? `{dim}${activeEpic}{/dim} {dim}›{/dim} {cyan-fg}${activeStory}{/cyan-fg} {dim}—{/dim} ${stepName}`
|
|
64
|
+
: `{dim}flow:{/dim} ${activeFlow} {dim}next:{/dim} {cyan-fg}${stateData?.epicCycle?.next_skill ?? 'survey-context'}{/cyan-fg}`;
|
|
65
|
+
const stepLine =
|
|
66
|
+
`{dim}step {/dim}{cyan-fg}${doneStories}{/cyan-fg}{dim}/${totalStories}{/dim}` +
|
|
67
|
+
` {dim}—{/dim} ${statusWord}` +
|
|
68
|
+
` {dim}—{/dim} ${storyDesc}`;
|
|
24
69
|
|
|
25
|
-
|
|
70
|
+
// ── Line 3: stats row ──
|
|
71
|
+
const statsLine =
|
|
72
|
+
`{dim}[{/dim} epics {green-fg}${doneEpics}${doneEpics > 0 ? '' : ''}{/green-fg}{dim}/${totalEpics} ]{/dim}` +
|
|
73
|
+
` {dim}[{/dim} stories {cyan-fg}${doneStories}{/cyan-fg}{dim}/${totalStories} ]{/dim}` +
|
|
74
|
+
` {dim}[{/dim} BCPs {yellow-fg}${deliveredBcps}{/yellow-fg}{dim}/${targetBcps} ]{/dim}` +
|
|
75
|
+
` {dim}[{/dim} cycle ${cycleDisplay}{dim} ]{/dim}` +
|
|
76
|
+
` {dim}[{/dim} BCP/hr {${bphColor}-fg}${bphDisplay}{/${bphColor}-fg}{dim} ]{/dim}` +
|
|
77
|
+
` {dim}[{/dim} v{green-fg}${version}{/green-fg}{dim} ]{/dim}`;
|
|
26
78
|
|
|
27
|
-
box.setContent(
|
|
79
|
+
box.setContent([statusLine, stepLine, statsLine].join('\n'));
|
|
28
80
|
}
|
|
29
81
|
|
|
30
82
|
module.exports = { renderMetricsBar };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const { STEPS } = require('../loaders/pipeline-map');
|
|
2
2
|
|
|
3
|
+
const STEP_ABBR = ['sur','pla','kic','tdd','ver','aud','com','rel'];
|
|
4
|
+
|
|
3
5
|
function renderPipeline(box, stateData) {
|
|
4
6
|
if (!box || typeof box.setContent !== 'function') {
|
|
5
7
|
return;
|
|
@@ -10,37 +12,27 @@ function renderPipeline(box, stateData) {
|
|
|
10
12
|
return;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const isCurrent = step === currentStep;
|
|
25
|
-
|
|
26
|
-
let display = ` ${index + 1}. ${step}`;
|
|
27
|
-
|
|
28
|
-
if (isCurrent) {
|
|
29
|
-
display = `{reverse}{bold}${display}{/bold}{/reverse}`;
|
|
30
|
-
} else if (isCompleted) {
|
|
31
|
-
display = `{green-fg}${display}{/green-fg}`;
|
|
15
|
+
// current_step is a 0-based index (number) in state.yaml
|
|
16
|
+
const rawStep = stateData.epicCycle?.current_step;
|
|
17
|
+
const currentIdx = typeof rawStep === 'number' ? rawStep : STEPS.indexOf(rawStep);
|
|
18
|
+
|
|
19
|
+
// Build pipeline strip
|
|
20
|
+
const strip = STEPS.map((step, i) => {
|
|
21
|
+
const abbr = STEP_ABBR[i];
|
|
22
|
+
if (i < currentIdx) {
|
|
23
|
+
return `{green-fg}${i + 1} ${step}{/green-fg}`;
|
|
24
|
+
} else if (i === currentIdx) {
|
|
25
|
+
return `{reverse}{cyan-fg}${i + 1} ${step}{/cyan-fg}{/reverse}`;
|
|
32
26
|
} else {
|
|
33
|
-
|
|
27
|
+
return `{dim}${i + 1} ${step}{/dim}`;
|
|
34
28
|
}
|
|
29
|
+
}).join(' {dim}›{/dim} ');
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
lines.push(stepDisplay);
|
|
40
|
-
lines.push('');
|
|
41
|
-
lines.push(`{dim}Current: ${currentStep || '—'}{/dim}`);
|
|
31
|
+
const stepLabel = currentIdx >= 0 && currentIdx < STEPS.length
|
|
32
|
+
? `{dim}step{/dim} {cyan-fg}${currentIdx + 1}{/cyan-fg}{dim}/${STEPS.length}{/dim} {dim}—{/dim} ${STEPS[currentIdx]}`
|
|
33
|
+
: '{dim}no active step{/dim}';
|
|
42
34
|
|
|
43
|
-
box.setContent(
|
|
35
|
+
box.setContent(stepLabel + '\n\n' + strip);
|
|
44
36
|
}
|
|
45
37
|
|
|
46
38
|
module.exports = { renderPipeline };
|
|
@@ -1,45 +1,63 @@
|
|
|
1
|
+
const STEP_NAMES = [
|
|
2
|
+
'survey-context', 'plan-work', 'kickoff-branch', 'develop-tdd',
|
|
3
|
+
'verify-work', 'audit-code', 'commit-message', 'release-branch'
|
|
4
|
+
];
|
|
5
|
+
const STEP_ABBR = ['sur', 'pla', 'kic', 'tdd', 'ver', 'aud', 'com', 'rel'];
|
|
6
|
+
|
|
1
7
|
function renderStateYaml(box, stateData) {
|
|
2
8
|
if (!box || typeof box.setContent !== 'function') {
|
|
3
9
|
return;
|
|
4
10
|
}
|
|
5
11
|
|
|
6
12
|
if (!stateData) {
|
|
7
|
-
box.setContent('{center}state.yaml not found{/center}');
|
|
13
|
+
box.setContent('{center}{dim}state.yaml not found{/dim}{/center}');
|
|
8
14
|
return;
|
|
9
15
|
}
|
|
10
16
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
const rawStep = stateData.epicCycle?.current_step;
|
|
18
|
+
const currentIdx = typeof rawStep === 'number' ? rawStep : STEP_NAMES.indexOf(rawStep);
|
|
19
|
+
const stepName = currentIdx >= 0 ? STEP_NAMES[currentIdx] : (rawStep ?? '—');
|
|
20
|
+
const nextSkill = stateData.epicCycle?.next_skill ?? stateData.handoff?.next_skill ?? '—';
|
|
14
21
|
|
|
15
|
-
const
|
|
16
|
-
{ key: 'active_flow',
|
|
17
|
-
{ key: 'active_epic',
|
|
18
|
-
{ key: 'active_story',
|
|
19
|
-
{ key: 'current_step',
|
|
20
|
-
{ key: 'next_skill',
|
|
21
|
-
{ key: 'git.branch',
|
|
22
|
-
{ key: 'metrics.story_start', value: stateData.metrics?.story_start },
|
|
22
|
+
const fields = [
|
|
23
|
+
{ key: 'active_flow', value: stateData.activeFlow, color: 'cyan' },
|
|
24
|
+
{ key: 'active_epic', value: stateData.activeEpic, color: 't' },
|
|
25
|
+
{ key: 'active_story', value: stateData.activeStory, color: 't' },
|
|
26
|
+
{ key: 'current_step', value: stepName, color: currentIdx >= 0 ? 'yellow' : 'dim' },
|
|
27
|
+
{ key: 'next_skill', value: nextSkill, color: 'green' },
|
|
28
|
+
{ key: 'git.branch', value: stateData.gitBranch, color: 'green' },
|
|
29
|
+
{ key: 'metrics.story_start', value: stateData.metrics?.story_start, color: 'dim' },
|
|
23
30
|
];
|
|
24
31
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
32
|
+
const lines = fields.map(({ key, value }) => {
|
|
33
|
+
const v = value ?? '—';
|
|
34
|
+
const isDash = v === '—';
|
|
35
|
+
let colored;
|
|
36
|
+
if (isDash) {
|
|
37
|
+
colored = `{dim}${v}{/dim}`;
|
|
38
|
+
} else if (fields.find(f => f.key === key)?.color === 'cyan') {
|
|
39
|
+
colored = `{cyan-fg}${v}{/cyan-fg}`;
|
|
40
|
+
} else if (fields.find(f => f.key === key)?.color === 'green') {
|
|
41
|
+
colored = `{green-fg}${v}{/green-fg}`;
|
|
42
|
+
} else if (fields.find(f => f.key === key)?.color === 'yellow') {
|
|
43
|
+
colored = `{yellow-fg}${v}{/yellow-fg}`;
|
|
44
|
+
} else if (fields.find(f => f.key === key)?.color === 'dim') {
|
|
45
|
+
colored = `{dim}${v}{/dim}`;
|
|
46
|
+
} else {
|
|
47
|
+
colored = v;
|
|
35
48
|
}
|
|
49
|
+
return `{dim}${key}:{/dim} ${colored}`;
|
|
50
|
+
});
|
|
36
51
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
52
|
+
// Stage tracker strip
|
|
53
|
+
const tracker = STEP_ABBR.map((abbr, i) => {
|
|
54
|
+
if (i < currentIdx) return `{green-fg}${abbr}{/green-fg}`;
|
|
55
|
+
if (i === currentIdx) return `{cyan-fg}{bold}${abbr}{/bold}{/cyan-fg}`;
|
|
56
|
+
return `{dim}${abbr}{/dim}`;
|
|
57
|
+
}).join(' {dim}·{/dim} ');
|
|
40
58
|
|
|
41
|
-
|
|
42
|
-
|
|
59
|
+
lines.push('');
|
|
60
|
+
lines.push(tracker);
|
|
43
61
|
|
|
44
62
|
box.setContent(lines.join('\n'));
|
|
45
63
|
}
|
|
@@ -23,10 +23,14 @@ Detailed documentation for the `orchestrate-project` meta-skill.
|
|
|
23
23
|
- **Gate**: Quality (request-review ≥94%) + slopcheck [SUS]/[SLOP].
|
|
24
24
|
|
|
25
25
|
### PHASE 4: BUILD
|
|
26
|
-
- **Goal**: Execute the plan
|
|
27
|
-
- **Deliverables**: Code; `execution-status.yaml` updated per story.
|
|
28
|
-
- **Skills**: `kickoff-branch`, `develop-tdd`, `
|
|
29
|
-
- **
|
|
26
|
+
- **Goal**: Execute the plan story-by-story using the 8-step `build-epic` cycle with TDD and vertical slices.
|
|
27
|
+
- **Deliverables**: Code; `execution-status.yaml` updated per story; `specs/metrics/cycle-times.yaml` row per story.
|
|
28
|
+
- **Skills**: `build-epic` (conductor) → per-story: `survey-context`, `plan-work`, `kickoff-branch`, `develop-tdd`, `verify-work`, `audit-code`, `commit-message`, `release-branch`.
|
|
29
|
+
- **BCP tracking**: `plan-work` labels every task `[BCP N]`; total written to `state.yaml` as `epic_cycle.story_bcps`. BCP baseline must exist in `release-plan.yaml` before starting.
|
|
30
|
+
- **Timestamps**: `survey-context` stamps `metrics.story_start`; `release-branch` stamps `metrics.story_end` and writes BCP/hr to `specs/metrics/cycle-times.yaml`.
|
|
31
|
+
- **next_skill**: Each critical-path skill writes `handoff.next_skill` to `state.yaml`. Agents resume by reading `state.yaml` — no guessing.
|
|
32
|
+
- **Dashboard**: `npm run dashboard` (TUI) or `npm run dashboard:web` (browser, port 7742) shows live pipeline, epic queue, BCP metrics, and cycle-time ledger.
|
|
33
|
+
- **Gate**: Integration tests PASS; all 8 build-epic steps completed per story.
|
|
30
34
|
|
|
31
35
|
### PHASE 5: VERIFY
|
|
32
36
|
- **Goal**: Validate success criteria and ensure production readiness.
|
package/package.json
CHANGED
|
@@ -203,7 +203,7 @@ touch specs/tech-architecture/design.md
|
|
|
203
203
|
touch specs/tech-architecture/REFACTOR_LATEST.md
|
|
204
204
|
touch specs/tech-architecture/IMPACT_LATEST.md
|
|
205
205
|
touch specs/bugs/registry.yaml
|
|
206
|
-
echo "# Specs\n\nAll planning documents for this project. Evolved bigpowers structure (
|
|
206
|
+
echo "# Specs\n\nAll planning documents for this project. Evolved bigpowers structure (v2.0.0+)." > specs/README.md
|
|
207
207
|
```
|
|
208
208
|
|
|
209
209
|
**Note:** `specs/state.yaml.lock` is NOT pre-created — it is acquired and released dynamically during writes to prevent concurrency conflicts.
|