Skip to content

Commit 9ed9188

Browse files
committed
dashboard: surface approval-needed checkpoints
1 parent 0230ed6 commit 9ed9188

File tree

2 files changed

+425
-8
lines changed

2 files changed

+425
-8
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2+
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'fs';
3+
import { join } from 'path';
4+
import { tmpdir } from 'os';
5+
6+
// eslint-disable-next-line @typescript-eslint/no-require-imports
7+
const { detectDashboardApprovalGate } = require('../../../lib/dashboard/tui/app');
8+
9+
describe('dashboard approval gate detection', () => {
10+
let workspacePath: string;
11+
12+
beforeEach(() => {
13+
workspacePath = join(tmpdir(), `dashboard-approval-${Date.now()}-${Math.random().toString(36).slice(2)}`);
14+
mkdirSync(workspacePath, { recursive: true });
15+
});
16+
17+
afterEach(() => {
18+
if (existsSync(workspacePath)) {
19+
rmSync(workspacePath, { recursive: true, force: true });
20+
}
21+
});
22+
23+
it('detects FIRE confirm-mode checkpoint waiting for approval', () => {
24+
const runPath = join(workspacePath, '.specs-fire', 'runs', 'run-001');
25+
mkdirSync(runPath, { recursive: true });
26+
writeFileSync(join(runPath, 'plan.md'), `---
27+
run: run-001
28+
work_item: wi-01
29+
mode: confirm
30+
checkpoint: confirm-plan
31+
approved_at: null
32+
---
33+
`, 'utf8');
34+
35+
const snapshot = {
36+
flow: 'fire',
37+
activeRuns: [
38+
{
39+
id: 'run-001',
40+
scope: 'single',
41+
folderPath: runPath,
42+
hasPlan: true,
43+
hasWalkthrough: false,
44+
hasTestReport: false,
45+
currentItem: 'wi-01',
46+
workItems: [
47+
{
48+
id: 'wi-01',
49+
mode: 'confirm',
50+
status: 'in_progress',
51+
currentPhase: 'plan'
52+
}
53+
]
54+
}
55+
]
56+
};
57+
58+
const gate = detectDashboardApprovalGate(snapshot, 'fire');
59+
60+
expect(gate).not.toBeNull();
61+
expect(gate.message).toContain('run-001');
62+
expect(gate.message).toContain('waiting at plan checkpoint');
63+
});
64+
65+
it('clears FIRE approval gate after plan approval timestamp exists', () => {
66+
const runPath = join(workspacePath, '.specs-fire', 'runs', 'run-002');
67+
mkdirSync(runPath, { recursive: true });
68+
writeFileSync(join(runPath, 'plan.md'), `---
69+
run: run-002
70+
work_item: wi-02
71+
mode: confirm
72+
checkpoint: confirm-plan
73+
approved_at: 2026-02-22T03:00:00Z
74+
---
75+
`, 'utf8');
76+
77+
const snapshot = {
78+
flow: 'fire',
79+
activeRuns: [
80+
{
81+
id: 'run-002',
82+
scope: 'single',
83+
folderPath: runPath,
84+
hasPlan: true,
85+
hasWalkthrough: false,
86+
hasTestReport: false,
87+
currentItem: 'wi-02',
88+
workItems: [
89+
{
90+
id: 'wi-02',
91+
mode: 'confirm',
92+
status: 'in_progress',
93+
currentPhase: 'plan'
94+
}
95+
]
96+
}
97+
]
98+
};
99+
100+
const gate = detectDashboardApprovalGate(snapshot, 'fire');
101+
expect(gate).toBeNull();
102+
});
103+
104+
it('detects AIDLC checkpoint wait for simple bolt when stage artifact exists', () => {
105+
const snapshot = {
106+
flow: 'aidlc',
107+
activeBolts: [
108+
{
109+
id: '001-auth-service',
110+
type: 'simple-construction-bolt',
111+
status: 'in_progress',
112+
currentStage: 'plan',
113+
files: ['bolt.md', 'implementation-plan.md'],
114+
stages: [
115+
{ name: 'plan', status: 'in_progress' },
116+
{ name: 'implement', status: 'pending' }
117+
]
118+
}
119+
]
120+
};
121+
122+
const gate = detectDashboardApprovalGate(snapshot, 'aidlc');
123+
expect(gate).not.toBeNull();
124+
expect(gate.message).toContain('001-auth-service');
125+
});
126+
127+
it('does not flag AIDLC bolt when checkpoint artifact is not present yet', () => {
128+
const snapshot = {
129+
flow: 'aidlc',
130+
activeBolts: [
131+
{
132+
id: '002-payments',
133+
type: 'simple-construction-bolt',
134+
status: 'in_progress',
135+
currentStage: 'plan',
136+
files: ['bolt.md'],
137+
stages: [
138+
{ name: 'plan', status: 'in_progress' },
139+
{ name: 'implement', status: 'pending' }
140+
]
141+
}
142+
]
143+
};
144+
145+
const gate = detectDashboardApprovalGate(snapshot, 'aidlc');
146+
expect(gate).toBeNull();
147+
});
148+
});

0 commit comments

Comments
 (0)