bigpowers 1.3.2 → 1.4.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/SKILL-INDEX.md +9 -9
  3. package/audit-code/SKILL.md +12 -0
  4. package/build-epic/SKILL.md +7 -4
  5. package/commit-message/SKILL.md +12 -0
  6. package/compose-workflow/SKILL.md +2 -0
  7. package/dashboard/bin/dashboard.js +93 -0
  8. package/dashboard/package-lock.json +1035 -0
  9. package/dashboard/package.json +18 -0
  10. package/dashboard/src/data/gate-status.js +32 -0
  11. package/dashboard/src/data/metrics.js +89 -0
  12. package/dashboard/src/data/pipeline-map.js +32 -0
  13. package/dashboard/src/data/reader.js +122 -0
  14. package/dashboard/src/data/watcher.js +108 -0
  15. package/dashboard/src/loaders/gate-status.js +32 -0
  16. package/dashboard/src/loaders/metrics.js +89 -0
  17. package/dashboard/src/loaders/pipeline-map.js +32 -0
  18. package/dashboard/src/loaders/reader.js +122 -0
  19. package/dashboard/src/loaders/watcher.js +108 -0
  20. package/dashboard/src/tui/epic-queue.js +36 -0
  21. package/dashboard/src/tui/filesystem.js +95 -0
  22. package/dashboard/src/tui/index.js +161 -0
  23. package/dashboard/src/tui/ledger.js +66 -0
  24. package/dashboard/src/tui/metrics-bar.js +30 -0
  25. package/dashboard/src/tui/pipeline.js +46 -0
  26. package/dashboard/src/tui/state-yaml.js +49 -0
  27. package/dashboard/src/web/client.html +477 -0
  28. package/dashboard/src/web/server.js +112 -0
  29. package/dashboard/test/fixtures/state.yaml +2 -0
  30. package/deepen-architecture/SKILL.md +2 -0
  31. package/define-language/SKILL.md +2 -0
  32. package/define-success/SKILL.md +4 -0
  33. package/delegate-task/SKILL.md +2 -0
  34. package/design-interface/SKILL.md +2 -0
  35. package/develop-tdd/SKILL.md +32 -0
  36. package/dispatch-agents/SKILL.md +2 -0
  37. package/edit-document/SKILL.md +6 -0
  38. package/elaborate-spec/SKILL.md +2 -0
  39. package/enforce-first/SKILL.md +2 -0
  40. package/grill-me/SKILL.md +2 -0
  41. package/guard-git/SKILL.md +2 -0
  42. package/hook-commits/SKILL.md +2 -0
  43. package/inspect-quality/SKILL.md +2 -0
  44. package/kickoff-branch/SKILL.md +5 -0
  45. package/map-codebase/SKILL.md +2 -0
  46. package/model-domain/SKILL.md +4 -0
  47. package/orchestrate-project/REFERENCE.md +1 -1
  48. package/orchestrate-project/SKILL.md +3 -1
  49. package/organize-workspace/SKILL.md +2 -0
  50. package/package.json +4 -2
  51. package/plan-refactor/SKILL.md +2 -0
  52. package/plan-work/SKILL.md +44 -0
  53. package/release-branch/SKILL.md +28 -0
  54. package/respond-review/SKILL.md +2 -0
  55. package/run-planning/SKILL.md +2 -0
  56. package/search-skills/SKILL.md +2 -0
  57. package/seed-conventions/SKILL.md +2 -0
  58. package/session-state/SKILL.md +16 -0
  59. package/setup-environment/SKILL.md +2 -0
  60. package/simulate-agents/SKILL.md +2 -0
  61. package/spike-prototype/SKILL.md +2 -0
  62. package/stocktake-skills/SKILL.md +2 -0
  63. package/survey-context/SKILL.md +23 -0
  64. package/terse-mode/SKILL.md +2 -0
  65. package/using-bigpowers/SKILL.md +4 -0
  66. package/validate-fix/SKILL.md +2 -0
  67. package/verify-work/SKILL.md +33 -0
  68. package/visual-dashboard/SKILL.md +2 -0
  69. package/wire-observability/SKILL.md +2 -0
@@ -0,0 +1,477 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>BigPowers Dashboard</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ background: #0d1117;
16
+ color: #c9d1d9;
17
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
18
+ font-size: 12px;
19
+ line-height: 1.6;
20
+ padding: 16px;
21
+ }
22
+
23
+ .container {
24
+ max-width: 1400px;
25
+ margin: 0 auto;
26
+ }
27
+
28
+ h1 {
29
+ font-size: 20px;
30
+ margin-bottom: 24px;
31
+ color: #58a6ff;
32
+ border-bottom: 2px solid #30363d;
33
+ padding-bottom: 8px;
34
+ }
35
+
36
+ .grid {
37
+ display: grid;
38
+ grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
39
+ gap: 16px;
40
+ margin-bottom: 16px;
41
+ }
42
+
43
+ .panel {
44
+ background: #161b22;
45
+ border: 1px solid #30363d;
46
+ border-radius: 6px;
47
+ padding: 16px;
48
+ }
49
+
50
+ .panel h2 {
51
+ font-size: 14px;
52
+ margin-bottom: 12px;
53
+ color: #79c0ff;
54
+ border-bottom: 1px solid #30363d;
55
+ padding-bottom: 8px;
56
+ }
57
+
58
+ .metric-row {
59
+ display: flex;
60
+ justify-content: space-between;
61
+ padding: 6px 0;
62
+ border-bottom: 1px solid #21262d;
63
+ }
64
+
65
+ .metric-row:last-child {
66
+ border-bottom: none;
67
+ }
68
+
69
+ .metric-label {
70
+ color: #8b949e;
71
+ }
72
+
73
+ .metric-value {
74
+ color: #79c0ff;
75
+ font-weight: bold;
76
+ }
77
+
78
+ .step-container {
79
+ display: flex;
80
+ gap: 8px;
81
+ flex-wrap: wrap;
82
+ }
83
+
84
+ .step {
85
+ flex: 1;
86
+ min-width: 80px;
87
+ padding: 8px;
88
+ background: #21262d;
89
+ border: 1px solid #30363d;
90
+ border-radius: 4px;
91
+ text-align: center;
92
+ font-size: 10px;
93
+ color: #8b949e;
94
+ transition: all 0.2s ease;
95
+ }
96
+
97
+ .step.active {
98
+ background: #238636;
99
+ border-color: #2ea043;
100
+ color: #aff5b4;
101
+ font-weight: bold;
102
+ }
103
+
104
+ .step-label {
105
+ display: block;
106
+ margin-bottom: 4px;
107
+ word-break: break-word;
108
+ }
109
+
110
+ .state-pairs {
111
+ display: flex;
112
+ flex-direction: column;
113
+ gap: 8px;
114
+ }
115
+
116
+ .state-pair {
117
+ display: flex;
118
+ flex-direction: column;
119
+ }
120
+
121
+ .state-key {
122
+ color: #8b949e;
123
+ font-size: 11px;
124
+ }
125
+
126
+ .state-value {
127
+ color: #79c0ff;
128
+ word-break: break-all;
129
+ margin-top: 2px;
130
+ }
131
+
132
+ .ledger-row {
133
+ padding: 6px 0;
134
+ border-bottom: 1px solid #21262d;
135
+ display: flex;
136
+ justify-content: space-between;
137
+ }
138
+
139
+ .ledger-row:last-child {
140
+ border-bottom: none;
141
+ }
142
+
143
+ .ledger-id {
144
+ color: #8b949e;
145
+ min-width: 60px;
146
+ }
147
+
148
+ .ledger-value {
149
+ color: #79c0ff;
150
+ font-weight: bold;
151
+ }
152
+
153
+ .ledger-meta {
154
+ color: #6e7681;
155
+ font-size: 10px;
156
+ margin-top: 2px;
157
+ }
158
+
159
+ .epic-item {
160
+ padding: 8px;
161
+ background: #21262d;
162
+ border-left: 3px solid #30363d;
163
+ margin-bottom: 6px;
164
+ border-radius: 2px;
165
+ }
166
+
167
+ .epic-title {
168
+ color: #79c0ff;
169
+ font-weight: bold;
170
+ margin-bottom: 4px;
171
+ }
172
+
173
+ .epic-stories {
174
+ margin-left: 8px;
175
+ color: #8b949e;
176
+ font-size: 11px;
177
+ }
178
+
179
+ .status-badge {
180
+ display: inline-block;
181
+ padding: 2px 6px;
182
+ background: #21262d;
183
+ border: 1px solid #30363d;
184
+ border-radius: 3px;
185
+ font-size: 10px;
186
+ color: #8b949e;
187
+ margin-right: 4px;
188
+ margin-bottom: 2px;
189
+ }
190
+
191
+ .status-active {
192
+ background: #238636;
193
+ border-color: #2ea043;
194
+ color: #aff5b4;
195
+ }
196
+
197
+ .status-complete {
198
+ background: #1a414b;
199
+ border-color: #238636;
200
+ color: #7ee787;
201
+ }
202
+
203
+ .empty {
204
+ color: #6e7681;
205
+ font-style: italic;
206
+ }
207
+
208
+ .connection-status {
209
+ position: fixed;
210
+ bottom: 16px;
211
+ right: 16px;
212
+ padding: 8px 12px;
213
+ background: #238636;
214
+ color: #aff5b4;
215
+ border-radius: 4px;
216
+ font-size: 11px;
217
+ border: 1px solid #2ea043;
218
+ }
219
+
220
+ .connection-status.disconnected {
221
+ background: #da3633;
222
+ border-color: #f85149;
223
+ color: #f5a8a8;
224
+ }
225
+ </style>
226
+ </head>
227
+ <body>
228
+ <div class="container">
229
+ <h1>BigPowers Dashboard</h1>
230
+
231
+ <div class="grid">
232
+ <!-- Pipeline Panel -->
233
+ <div class="panel">
234
+ <h2>Pipeline</h2>
235
+ <div class="step-container" id="pipeline"></div>
236
+ </div>
237
+
238
+ <!-- Metrics Panel -->
239
+ <div class="panel">
240
+ <h2>Metrics</h2>
241
+ <div id="metrics"></div>
242
+ </div>
243
+
244
+ <!-- State Panel -->
245
+ <div class="panel">
246
+ <h2>State</h2>
247
+ <div class="state-pairs" id="state"></div>
248
+ </div>
249
+
250
+ <!-- Epic Queue Panel -->
251
+ <div class="panel">
252
+ <h2>Epic Queue</h2>
253
+ <div id="epics"></div>
254
+ </div>
255
+
256
+ <!-- Cycle-Time Ledger Panel -->
257
+ <div class="panel">
258
+ <h2>Cycle-Time Ledger</h2>
259
+ <div id="ledger"></div>
260
+ </div>
261
+ </div>
262
+ </div>
263
+
264
+ <div class="connection-status" id="status">Live</div>
265
+
266
+ <script>
267
+ const PIPELINE_STEPS = [
268
+ 'survey-context',
269
+ 'plan-work',
270
+ 'kickoff-branch',
271
+ 'develop-tdd',
272
+ 'verify-work',
273
+ 'audit-code',
274
+ 'commit-message',
275
+ 'release-branch'
276
+ ];
277
+
278
+ let eventSource = null;
279
+ let pollInterval = null;
280
+
281
+ function render(data) {
282
+ if (!data) return;
283
+
284
+ renderPipeline(data);
285
+ renderMetrics(data);
286
+ renderState(data);
287
+ renderEpics(data);
288
+ renderLedger(data);
289
+ }
290
+
291
+ function renderPipeline(data) {
292
+ const container = document.getElementById('pipeline');
293
+ const activeFlow = data.state?.activeFlow;
294
+ const activeIndex = activeFlow ? PIPELINE_STEPS.indexOf(activeFlow) : -1;
295
+
296
+ container.innerHTML = PIPELINE_STEPS.map((step, idx) => {
297
+ const isActive = idx === activeIndex;
298
+ const className = isActive ? 'step active' : 'step';
299
+ return `<div class="${className}"><span class="step-label">${step}</span></div>`;
300
+ }).join('');
301
+ }
302
+
303
+ function renderMetrics(data) {
304
+ const container = document.getElementById('metrics');
305
+ const metrics = data.projectMetrics;
306
+ const velocity = data.currentVelocity;
307
+ const timestamp = data.timestamp;
308
+
309
+ let html = '';
310
+ if (metrics) {
311
+ html += `<div class="metric-row">
312
+ <span class="metric-label">Total BCPs</span>
313
+ <span class="metric-value">${metrics.totalBcps}</span>
314
+ </div>`;
315
+ html += `<div class="metric-row">
316
+ <span class="metric-label">Avg BCP/Hour</span>
317
+ <span class="metric-value">${metrics.avgBcpPerHour.toFixed(2)}</span>
318
+ </div>`;
319
+ }
320
+ if (velocity) {
321
+ html += `<div class="metric-row">
322
+ <span class="metric-label">Current Velocity</span>
323
+ <span class="metric-value">${velocity.avgBcpPerHour.toFixed(2)} BCP/h</span>
324
+ </div>`;
325
+ }
326
+ if (data.state?.release?.version) {
327
+ html += `<div class="metric-row">
328
+ <span class="metric-label">Version</span>
329
+ <span class="metric-value">${data.state.release.version}</span>
330
+ </div>`;
331
+ }
332
+ if (timestamp) {
333
+ html += `<div class="metric-row">
334
+ <span class="metric-label">Updated</span>
335
+ <span class="metric-value" style="font-size: 10px;">${new Date(timestamp).toLocaleTimeString()}</span>
336
+ </div>`;
337
+ }
338
+ container.innerHTML = html || '<p class="empty">No metrics available</p>';
339
+ }
340
+
341
+ function renderState(data) {
342
+ const container = document.getElementById('state');
343
+ const state = data.state || {};
344
+
345
+ const pairs = [
346
+ { key: 'Active Flow', value: state.activeFlow },
347
+ { key: 'Active Epic', value: state.activeEpic },
348
+ { key: 'Active Story', value: state.activeStory },
349
+ { key: 'Git Branch', value: state.gitBranch }
350
+ ];
351
+
352
+ container.innerHTML = pairs.map(pair => {
353
+ const val = pair.value || '(none)';
354
+ return `
355
+ <div class="state-pair">
356
+ <span class="state-key">${pair.key}</span>
357
+ <span class="state-value">${val}</span>
358
+ </div>
359
+ `;
360
+ }).join('');
361
+ }
362
+
363
+ function renderEpics(data) {
364
+ const container = document.getElementById('epics');
365
+ const epics = data.epics || [];
366
+
367
+ if (epics.length === 0) {
368
+ container.innerHTML = '<p class="empty">No epics available</p>';
369
+ return;
370
+ }
371
+
372
+ container.innerHTML = epics.map(epic => {
373
+ const stories = epic.stories || [];
374
+ const storyCount = stories.length;
375
+ return `
376
+ <div class="epic-item">
377
+ <div class="epic-title">${epic.id || 'Unknown'}</div>
378
+ <div style="color: #8b949e; font-size: 11px; margin-bottom: 4px;">${epic.title || 'Untitled'}</div>
379
+ <div class="epic-stories">${storyCount} stories</div>
380
+ </div>
381
+ `;
382
+ }).join('');
383
+ }
384
+
385
+ function renderLedger(data) {
386
+ const container = document.getElementById('ledger');
387
+ const cycleTimes = data.cycleTimes || [];
388
+
389
+ if (cycleTimes.length === 0) {
390
+ container.innerHTML = '<p class="empty">No cycle-time data available</p>';
391
+ return;
392
+ }
393
+
394
+ const recentRows = cycleTimes.slice(-8);
395
+ container.innerHTML = recentRows.map(row => {
396
+ const end = row.end ? new Date(row.end).toLocaleDateString() : '(active)';
397
+ return `
398
+ <div class="ledger-row">
399
+ <div class="ledger-id">${row.id}</div>
400
+ <div style="text-align: right;">
401
+ <div class="ledger-value">${row.bcps} BCP</div>
402
+ <div class="ledger-meta">${row.cycleMin}min | ${row.bcpPerHour.toFixed(2)}/h</div>
403
+ </div>
404
+ </div>
405
+ `;
406
+ }).join('');
407
+ }
408
+
409
+ function setConnectionStatus(connected) {
410
+ const status = document.getElementById('status');
411
+ if (connected) {
412
+ status.textContent = 'Live';
413
+ status.classList.remove('disconnected');
414
+ } else {
415
+ status.textContent = 'Polling...';
416
+ status.classList.add('disconnected');
417
+ }
418
+ }
419
+
420
+ function startEventSource() {
421
+ eventSource = new EventSource('/events');
422
+ setConnectionStatus(true);
423
+
424
+ eventSource.onmessage = (event) => {
425
+ try {
426
+ const data = JSON.parse(event.data);
427
+ render(data);
428
+ } catch (err) {
429
+ console.error('Failed to parse SSE data:', err);
430
+ }
431
+ };
432
+
433
+ eventSource.onerror = (err) => {
434
+ console.warn('SSE connection lost, falling back to polling');
435
+ eventSource.close();
436
+ setConnectionStatus(false);
437
+ startPolling();
438
+ };
439
+ }
440
+
441
+ function startPolling() {
442
+ if (pollInterval) return;
443
+
444
+ pollInterval = setInterval(() => {
445
+ fetch('/api/state')
446
+ .then(res => res.json())
447
+ .then(data => {
448
+ render(data);
449
+ setConnectionStatus(false);
450
+ })
451
+ .catch(err => console.error('Poll failed:', err));
452
+ }, 5000);
453
+ }
454
+
455
+ function stopPolling() {
456
+ if (pollInterval) {
457
+ clearInterval(pollInterval);
458
+ pollInterval = null;
459
+ }
460
+ }
461
+
462
+ // Initial load
463
+ window.addEventListener('load', () => {
464
+ fetch('/api/state')
465
+ .then(res => res.json())
466
+ .then(data => {
467
+ render(data);
468
+ startEventSource();
469
+ })
470
+ .catch(err => {
471
+ console.error('Initial load failed:', err);
472
+ startPolling();
473
+ });
474
+ });
475
+ </script>
476
+ </body>
477
+ </html>
@@ -0,0 +1,112 @@
1
+ const path = require('path');
2
+ const { watch } = require('../loaders/watcher');
3
+ const reader = require('../loaders/reader');
4
+ const metrics = require('../loaders/metrics');
5
+
6
+ let express;
7
+ try {
8
+ express = require('express');
9
+ } catch (err) {
10
+ express = null;
11
+ }
12
+
13
+ function createServer(projectRoot, port = 7742) {
14
+ const watcher = watch(projectRoot);
15
+ const clients = new Set();
16
+ let debounceTimer = null;
17
+
18
+ function buildSnapshot() {
19
+ const state = reader.readStateYaml(projectRoot);
20
+ const execStatus = reader.readExecutionStatus(projectRoot);
21
+ const epics = reader.readEpicShards(projectRoot);
22
+ const cycleTimes = reader.readCycleTimes(projectRoot);
23
+
24
+ const projectMetrics = metrics.computeProjectMetrics(cycleTimes);
25
+ const currentVelocity = metrics.computeCurrentVelocity(cycleTimes);
26
+
27
+ return {
28
+ state,
29
+ execStatus,
30
+ epics,
31
+ cycleTimes,
32
+ projectMetrics,
33
+ currentVelocity,
34
+ timestamp: new Date().toISOString()
35
+ };
36
+ }
37
+
38
+ function broadcastSnapshot() {
39
+ if (debounceTimer) {
40
+ clearTimeout(debounceTimer);
41
+ }
42
+ debounceTimer = setTimeout(() => {
43
+ const snapshot = buildSnapshot();
44
+ const message = `data: ${JSON.stringify(snapshot)}\n\n`;
45
+ for (const client of clients) {
46
+ client.write(message);
47
+ }
48
+ }, 300);
49
+ }
50
+
51
+ watcher.on('change', broadcastSnapshot);
52
+
53
+ if (!express) {
54
+ // Fallback: return a minimal server object that doesn't do much
55
+ return {
56
+ listen(portNum, cb) {
57
+ console.warn('Express not installed. Server will not start.');
58
+ if (cb) cb();
59
+ return {
60
+ close() {}
61
+ };
62
+ }
63
+ };
64
+ }
65
+
66
+ const app = express();
67
+
68
+ // GET / - serve client.html
69
+ app.get('/', (req, res) => {
70
+ const clientPath = path.join(__dirname, 'client.html');
71
+ res.sendFile(clientPath);
72
+ });
73
+
74
+ // GET /api/state - return current snapshot as JSON
75
+ app.get('/api/state', (req, res) => {
76
+ const snapshot = buildSnapshot();
77
+ res.json(snapshot);
78
+ });
79
+
80
+ // GET /events - Server-Sent Events endpoint
81
+ app.get('/events', (req, res) => {
82
+ res.writeHead(200, {
83
+ 'Content-Type': 'text/event-stream',
84
+ 'Cache-Control': 'no-cache',
85
+ 'Connection': 'keep-alive'
86
+ });
87
+
88
+ // Send initial snapshot
89
+ const snapshot = buildSnapshot();
90
+ res.write(`data: ${JSON.stringify(snapshot)}\n\n`);
91
+
92
+ // Add client to set
93
+ clients.add(res);
94
+
95
+ // Clean up on disconnect
96
+ req.on('close', () => {
97
+ clients.delete(res);
98
+ });
99
+
100
+ res.on('error', () => {
101
+ clients.delete(res);
102
+ });
103
+ });
104
+
105
+ return {
106
+ listen(portNum = port, cb) {
107
+ return app.listen(portNum, cb);
108
+ }
109
+ };
110
+ }
111
+
112
+ module.exports = { createServer };
@@ -0,0 +1,2 @@
1
+ active_flow: execute
2
+ active_epic_id: null
@@ -8,6 +8,8 @@ description: Find deepening opportunities in a codebase, informed by the domain
8
8
 
9
9
  Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability.
10
10
 
11
+ > **HARD GATE** — Deep modules must solve a forcing function, not just be "nice abstractions." If you cannot articulate why the abstraction exists, it is premature.
12
+
11
13
  ## Glossary
12
14
 
13
15
  Use these terms exactly in every suggestion. Consistent language is the point — don't drift into "component," "service," "API," or "boundary." Full definitions in [LANGUAGE.md](LANGUAGE.md).
@@ -8,6 +8,8 @@ description: Extract a DDD-style ubiquitous language glossary from the current c
8
8
 
9
9
  Extract and formalize domain terminology from the current conversation into a consistent glossary, saved to `specs/UBIQUITOUS_LANGUAGE.md`.
10
10
 
11
+ > **HARD GATE** — Ubiquitous language is NOT optional. Every term in the domain that could be misunderstood must be glossed. Ambiguity = rework.
12
+
11
13
  ## Process
12
14
 
13
15
  1. **Scan the conversation** for domain-relevant nouns, verbs, and concepts
@@ -1,3 +1,5 @@
1
+ ARCHIVED: content absorbed into plan-work
2
+
1
3
  ---
2
4
  name: define-success
3
5
  model: sonnet
@@ -8,6 +10,8 @@ description: Convert an imperative task statement into explicit "step → verify
8
10
 
9
11
  Transform "do X" into "step → verify: <cmd>" pairs. This is the pre-flight check before `plan-work` or `develop-tdd` — it makes success observable and removes ambiguity about when you're done.
10
12
 
13
+ > **HARD GATE** — Success criteria must be testable and user-observable. "Code should be fast" is not testable. "Pageload latency < 2s" is testable.
14
+
11
15
  ## Why this matters
12
16
 
13
17
  "Implement user authentication" is not a plan. It has no checkpoints, no evidence requirement, and no way to know if you're done. The Karpathy principle: every step must be independently verifiable with a runnable command. If you can't verify it, you can't prove it works.
@@ -5,6 +5,8 @@ description: Delegate one complex task to a single subagent, review its work in
5
5
  ---
6
6
 
7
7
  # Delegate Task
8
+ > **HARD GATE** — **HARD GATE** — Delegated work must have clear success criteria and verification commands. The delegate must be able to verify completion independently.
9
+
8
10
 
9
11
  Delegate a single complex task to a subagent with a two-stage review gate before accepting the result. Use when oversight of a single task matters more than speed.
10
12
 
@@ -8,6 +8,8 @@ description: Generate multiple radically different interface designs for a modul
8
8
 
9
9
  Based on "Design It Twice" from "A Philosophy of Software Design": your first idea is unlikely to be the best. Generate multiple radically different designs, then compare.
10
10
 
11
+ > **HARD GATE** — Multiple design options must be explored. Do NOT settle on first idea. Compare trade-offs (UX, complexity, extensibility, performance) before committing.
12
+
11
13
  ## Workflow
12
14
 
13
15
  ### 1. Gather Requirements
@@ -145,6 +145,33 @@ Once the story is complete and all tests pass:
145
145
  2. Present the script to the user as a step-by-step guide.
146
146
  3. Wait for the user to confirm the behavioral correctness before moving to the next story or declaring the task done.
147
147
 
148
+ ## TDD phases
149
+
150
+ ### Red Phase
151
+
152
+ Write a failing test first that confirms the behavior you want to implement:
153
+
154
+ - Test describes the desired observable behavior through the public interface
155
+ - Run the test to confirm it fails for the right reason (not a syntax error, not a typo)
156
+ - Commit the failing test: `git commit -m "test(<scope>): <description>"`
157
+
158
+ ### Green Phase
159
+
160
+ Write the minimum amount of code to make the test pass:
161
+
162
+ - No extra logic, no anticipated future cases, no premature optimization
163
+ - Focus only on making the current test pass
164
+ - Commit the passing code: `git commit -m "feat(<scope>): <description>" or "fix(<scope>): <description>"`
165
+
166
+ ### Refactor Phase
167
+
168
+ Improve the code structure, naming, and clarity without changing behavior:
169
+
170
+ - Extract duplication, apply SOLID principles where natural, deepen modules
171
+ - Run tests after each refactor step to ensure behavior is preserved
172
+ - Commit refactoring: `git commit -m "refactor(<scope>): <description>"`
173
+ - Apply the Boy Scout Rule: leave the code cleaner than you found it
174
+
148
175
  ## Checklist Per Cycle
149
176
 
150
177
  ```
@@ -159,3 +186,8 @@ Once the story is complete and all tests pass:
159
186
  [ ] Progress committed (Conventional Commits)
160
187
  [ ] verify: command passes
161
188
  ```
189
+
190
+ ## Handoff
191
+
192
+ Gate: READY -> next: verify-work
193
+ Writes: state.yaml handoff.next_skill = verify-work
@@ -5,6 +5,8 @@ description: Dispatch multiple subagents in parallel on independent tasks. No wa
5
5
  ---
6
6
 
7
7
  # Dispatch Agents
8
+ > **HARD GATE** — **HARD GATE** — Agent work must be parallelizable and have explicit synchronization points. Do NOT dispatch work that has hidden dependencies between agents.
9
+
8
10
 
9
11
  Run multiple subagents in parallel on independent tasks. Use when tasks are genuinely decoupled — no agent needs the output of another to start.
10
12