@ai-qa/workflow 2.0.16 → 2.0.18

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.
@@ -23,7 +23,51 @@ router.get('/', (req, res) => {
23
23
  });
24
24
  }
25
25
 
26
- res.render('test-data', { project, projects, dataFiles });
26
+ let testSpecs = [];
27
+ let testPlans = [];
28
+ if (project) {
29
+ const bridge = pm.getBridge(project.id);
30
+ testSpecs = bridge.listTestSpecs().map(specName => ({
31
+ name: specName,
32
+ content: bridge.getSpecContent(specName),
33
+ }));
34
+ testPlans = bridge.listTestPlans().map(planName => ({
35
+ name: planName,
36
+ content: bridge.getPlanContent(planName),
37
+ }));
38
+ }
39
+
40
+ res.render('test-data', { project, projects, dataFiles, testSpecs, testPlans });
41
+ });
42
+
43
+ router.get('/spec/:specName', (req, res) => {
44
+ const projects = pm.getAll();
45
+ const projectId = req.query.project || (projects.length > 0 ? projects[0].id : null);
46
+ const project = projects.find(p => p.id === projectId);
47
+ if (!project) return res.status(404).send('Project not found');
48
+
49
+ const bridge = pm.getBridge(project.id);
50
+ const content = bridge.getSpecContent(req.params.specName);
51
+ if (!content) return res.status(404).send('Spec not found');
52
+
53
+ res.setHeader('Content-Type', 'text/plain; charset=utf-8');
54
+ res.setHeader('Content-Disposition', `attachment; filename="${req.params.specName}"`);
55
+ res.send(content);
56
+ });
57
+
58
+ router.get('/plan/:planName', (req, res) => {
59
+ const projects = pm.getAll();
60
+ const projectId = req.query.project || (projects.length > 0 ? projects[0].id : null);
61
+ const project = projects.find(p => p.id === projectId);
62
+ if (!project) return res.status(404).send('Project not found');
63
+
64
+ const bridge = pm.getBridge(project.id);
65
+ const content = bridge.getPlanContent(req.params.planName);
66
+ if (!content) return res.status(404).send('Plan not found');
67
+
68
+ res.setHeader('Content-Type', 'text/markdown; charset=utf-8');
69
+ res.setHeader('Content-Disposition', `attachment; filename="${req.params.planName}"`);
70
+ res.send(content);
27
71
  });
28
72
 
29
73
  router.post('/generate', (req, res) => {
@@ -79,4 +123,4 @@ router.delete('/:file', (req, res) => {
79
123
  }
80
124
  });
81
125
 
82
- module.exports = router;
126
+ module.exports = router;
@@ -173,6 +173,10 @@ class CliBridge {
173
173
  return fs.readFileSync(reportPath, 'utf-8');
174
174
  }
175
175
 
176
+ runCommand(command) {
177
+ return this._run(command);
178
+ }
179
+
176
180
  hasAllureReport() {
177
181
  return fs.existsSync(path.join(this.projectPath, 'allure-report', 'index.html'));
178
182
  }
@@ -1,242 +1,254 @@
1
1
  <% layout='layouts/main' %>
2
- <% title='Dashboard' %>
3
-
4
- <div class="hero">
5
- <h1>AI QA Pipeline </h1>
6
- <p>User Story → Test Plan → Test Generation → Execution → Self-Healing → Report</p>
7
- <% if (project) { %>
8
- <p class="hero-project">
9
- Project: <strong>
10
- <%= project.name %>
11
- </strong>
12
- <% if (stats.lastRunStatus==='passed' ) { %><span class="badge badge-pass" style="margin-left:0.5rem;">Last
13
- Run ✅</span>
2
+ <% title='Dashboard' %>
3
+
4
+ <div class="hero">
5
+ <h1>Orchestrator QA</h1>
6
+ <p>User Story → Test Plan → Test Generation → Execution → Self-Healing → Report</p>
7
+ <% if (project) { %>
8
+ <p class="hero-project">
9
+ Project: <strong><%= project.name %></strong>
10
+ <% if (stats.lastRunStatus==='passed' ) { %><span class="badge badge-pass" style="margin-left:0.5rem;">Last Run ✅</span><% } %>
11
+ <% if (stats.lastRunStatus==='failed' ) { %><span class="badge badge-fail" style="margin-left:0.5rem;">Last Run ❌</span><% } %>
12
+ </p>
13
+ <% } %>
14
+ </div>
15
+
16
+ <% if (!project) { %>
17
+ <div class="empty-state">
18
+ <div style="font-size:3rem;margin-bottom:1rem;">🚀</div>
19
+ <h3>Aucun projet configuré</h3>
20
+ <p>Pointez le dashboard vers votre projet pour voir les données.</p>
21
+ <a href="/projects/add" class="btn btn-primary" style="margin-top:0.5rem;">Configurer un projet</a>
22
+ </div>
23
+ <% } else { %>
24
+
25
+ <!-- ─── Stats cliquables ─── -->
26
+ <div class="stats-row">
27
+ <a href="/stories?project=<%= project.id %>" class="stat-card" style="text-decoration:none;color:inherit;display:block;cursor:pointer;">
28
+ <div class="stat-number"><%= stats.stories %></div>
29
+ <div class="stat-label">User Stories</div>
30
+ </a>
31
+ <a href="/stories?project=<%= project.id %>&filter=planned" class="stat-card" style="text-decoration:none;color:inherit;display:block;cursor:pointer;">
32
+ <div class="stat-number"><%= stats.plans %></div>
33
+ <div class="stat-label">Test Plans</div>
34
+ </a>
35
+ <a href="/stories?project=<%= project.id %>&filter=tested" class="stat-card" style="text-decoration:none;color:inherit;display:block;cursor:pointer;">
36
+ <div class="stat-number"><%= stats.specs %></div>
37
+ <div class="stat-label">Test Specs</div>
38
+ </a>
39
+ <a href="/runs?project=<%= project.id %>" class="stat-card" style="text-decoration:none;color:inherit;display:block;cursor:pointer;">
40
+ <div class="stat-number"><%= stats.runs %></div>
41
+ <div class="stat-label">Executions</div>
42
+ </a>
43
+ <a href="/analytics?project=<%= project.id %>" class="stat-card" style="text-decoration:none;color:inherit;display:block;cursor:pointer;">
44
+ <div class="stat-number <%= stats.passRate >= 80 ? 'text-success' : stats.passRate >= 50 ? 'text-warning' : stats.runs > 0 ? 'text-danger' : '' %>">
45
+ <%= stats.runs > 0 ? stats.passRate + '%' : '—' %>
46
+ </div>
47
+ <div class="stat-label">Pass Rate</div>
48
+ </a>
49
+ </div>
50
+
51
+ <!-- ─── Activités récentes (stories) ─── -->
52
+ <% if (stats.storiesList && stats.storiesList.length > 0) { %>
53
+ <div class="card">
54
+ <h3>📋 Activités récentes</h3>
55
+ <table class="info-table">
56
+ <thead>
57
+ <tr>
58
+ <th>User Story</th>
59
+ <th>Plan</th>
60
+ <th>Test</th>
61
+ <th>Actions</th>
62
+ </tr>
63
+ </thead>
64
+ <tbody>
65
+ <% stats.storiesList.slice(0, 10).forEach(s => { %>
66
+ <tr>
67
+ <td><a href="/stories/<%= s.name %>?project=<%= project.id %>" style="font-weight:600;"><%= s.name.replace('.md', '') %></a></td>
68
+ <td>
69
+ <% if (s.hasPlan) { %><span class="badge badge-plan">✅ Planned</span>
70
+ <% } else { %><span class="badge badge-draft">⏳ Pending</span><% } %>
71
+ </td>
72
+ <td>
73
+ <% if (s.hasSpec) { %><span class="badge badge-spec">✅ Tested</span>
74
+ <% } else if (s.hasPlan) { %><span class="badge badge-draft">⏳ Pending</span>
75
+ <% } else { %><span class="badge badge-draft">—</span><% } %>
76
+ </td>
77
+ <td>
78
+ <a href="/stories/<%= s.name %>?project=<%= project.id %>" class="btn btn-sm">View</a>
79
+ <% if (s.hasSpec) { %>
80
+ <button class="btn btn-sm btn-primary" onclick="quickExecute('<%= project.id %>', '<%= s.name %>')">▶ Run</button>
14
81
  <% } %>
15
- <% if (stats.lastRunStatus==='failed' ) { %><span class="badge badge-fail"
16
- style="margin-left:0.5rem;">Last Run ❌</span>
17
- <% } %>
18
- </p>
19
- <% } %>
82
+ </td>
83
+ </tr>
84
+ <% }) %>
85
+ </tbody>
86
+ </table>
87
+ </div>
88
+ <% } %>
89
+
90
+ <!-- ─── Latest Run Summary ─── -->
91
+ <% if (stats.latestRun) { %>
92
+ <div class="card card-lavender">
93
+ <h3>⚡ Dernière exécution</h3>
94
+ <div style="display:flex;gap:2rem;flex-wrap:wrap;">
95
+ <div><strong>Test:</strong> <%= stats.latestRun.testName || 'All' %></div>
96
+ <div><strong>Durée:</strong> <%= stats.latestRun.duration ? (stats.latestRun.duration / 1000).toFixed(1) + 's' : 'N/A' %></div>
97
+ <div><strong>Passed:</strong> <%= stats.latestRun.passedCount %></div>
98
+ <div><strong>Failed:</strong> <%= stats.latestRun.failedCount %></div>
99
+ <div>
100
+ <a href="/runs/<%= stats.latestRun.id %>?project=<%= project.id %>" class="btn btn-sm">Details</a>
101
+ <a href="/export/report/<%= stats.latestRun.id %>?project=<%= project.id %>" class="btn btn-sm">📥 Report</a>
102
+ </div>
20
103
  </div>
21
-
22
- <% if (!project) { %>
23
- <!-- ─── No project state ─── -->
24
- <div class="empty-state">
25
- <div style="font-size:3rem;margin-bottom:1rem;">🚀</div>
26
- <h3>Dashboard prêt — aucun projet configuré</h3>
27
- <p>Ce dashboard est une template. Pointez-le vers votre dossier de test pour voir vos données.</p>
28
- <a href="/projects/add" class="btn btn-primary" style="margin-top:0.5rem;">Configurer un projet</a>
104
+ </div>
105
+ <% } %>
106
+
107
+ <!-- ─── Pipeline flow ─── -->
108
+ <div class="pipeline-flow">
109
+ <h2>Pipeline</h2>
110
+ <div class="flow-steps">
111
+ <div class="flow-step <%= stats.stories > 0 ? 'step-active' : '' %>">
112
+ <div class="step-icon">📝</div>
113
+ <div class="step-name">User Story</div>
114
+ <div class="step-desc"><%= stats.stories %> story<%= stats.stories !== 1 ? 's' : '' %></div>
29
115
  </div>
30
-
31
- <% } else { %>
32
-
33
- <!-- ─── Real stats from project folder ─── -->
34
- <div class="stats-row">
35
- <div class="stat-card">
36
- <div class="stat-number">
37
- <%= stats.stories %>
38
- </div>
39
- <div class="stat-label">User Stories</div>
40
- </div>
41
- <div class="stat-card">
42
- <div class="stat-number">
43
- <%= stats.plans %>
44
- </div>
45
- <div class="stat-label">Test Plans</div>
46
- </div>
47
- <div class="stat-card">
48
- <div class="stat-number">
49
- <%= stats.specs %>
50
- </div>
51
- <div class="stat-label">Test Specs</div>
52
- </div>
53
- <div class="stat-card">
54
- <div class="stat-number">
55
- <%= stats.runs %>
56
- </div>
57
- <div class="stat-label">Executions</div>
58
- </div>
59
- <div class="stat-card">
60
- <div
61
- class="stat-number <%= stats.passRate >= 80 ? 'text-success' : stats.passRate >= 50 ? 'text-warning' : stats.runs > 0 ? 'text-danger' : '' %>">
62
- <%= stats.runs> 0 ? stats.passRate + '%' : '—' %>
63
- </div>
64
- <div class="stat-label">Pass Rate</div>
65
- </div>
66
- </div>
67
-
68
- <!-- ─── Pipeline flow ─── -->
69
- <div class="pipeline-flow">
70
- <h2>Pipeline</h2>
71
- <div class="flow-steps">
72
- <div class="flow-step <%= stats.stories > 0 ? 'step-active' : '' %>">
73
- <div class="step-icon">📝</div>
74
- <div class="step-name">User Story</div>
75
- <div class="step-desc">
76
- <%= stats.stories %> story<%= stats.stories !==1 ? 's' : '' %>
77
- </div>
78
- </div>
79
- <div class="flow-arrow">→</div>
80
- <div class="flow-step <%= stats.plans > 0 ? 'step-active' : '' %>">
81
- <div class="step-icon">📋</div>
82
- <div class="step-name">Plan</div>
83
- <div class="step-desc">
84
- <%= stats.plans %> plan<%= stats.plans !==1 ? 's' : '' %>
85
- </div>
86
- </div>
87
- <div class="flow-arrow">→</div>
88
- <div class="flow-step <%= stats.specs > 0 ? 'step-active' : '' %>">
89
- <div class="step-icon">🧪</div>
90
- <div class="step-name">Generate</div>
91
- <div class="step-desc">
92
- <%= stats.specs %> spec<%= stats.specs !==1 ? 's' : '' %>
93
- </div>
94
- </div>
95
- <div class="flow-arrow">→</div>
96
- <div class="flow-step <%= stats.runs > 0 ? 'step-active' : '' %>">
97
- <div class="step-icon">⚡</div>
98
- <div class="step-name">Execute</div>
99
- <div class="step-desc">
100
- <%= stats.runs %> run<%= stats.runs !==1 ? 's' : '' %>
101
- </div>
102
- </div>
103
- <div class="flow-arrow">→</div>
104
- <div class="flow-step">
105
- <div class="step-icon">🛠️</div>
106
- <div class="step-name">Heal</div>
107
- <div class="step-desc">Auto-fix failures</div>
108
- </div>
109
- <div class="flow-arrow">→</div>
110
- <div class="flow-step">
111
- <div class="step-icon">📊</div>
112
- <div class="step-name">Report</div>
113
- <div class="step-desc">Comprehensive results</div>
114
- </div>
115
- </div>
116
- </div>
117
-
118
- <!-- ─── Quick links ─── -->
119
- <div class="quick-links-row">
120
- <a href="/stories?project=<%= project.id %>" class="quick-link-card">
121
- <div class="ql-icon">📝</div>
122
- <div class="ql-label">Stories <span class="badge badge-project">
123
- <%= stats.stories %>
124
- </span></div>
125
- </a>
126
- <a href="/runs?project=<%= project.id %>" class="quick-link-card">
127
- <div class="ql-icon">⚡</div>
128
- <div class="ql-label">Runs <span class="badge badge-project">
129
- <%= stats.runs %>
130
- </span></div>
131
- </a>
132
- <a href="/analytics?project=<%= project.id %>" class="quick-link-card">
133
- <div class="ql-icon">📊</div>
134
- <div class="ql-label">Analytics</div>
135
- </a>
136
- <a href="/projects/<%= project.id %>" class="quick-link-card">
137
- <div class="ql-icon">⚙️</div>
138
- <div class="ql-label">Pipeline</div>
139
- </a>
140
- </div>
141
-
142
- <% if (stats.stories===0) { %>
143
- <div class="alert alert-info" style="margin-top:1.5rem;">
144
- 💡 <strong>Aucune user story trouvée</strong> dans <code><%= project.path %>/user-story/</code>.
145
- Ajoutez un fichier <code>.md</code> pour commencer.
146
- </div>
147
- <% } %>
148
-
149
- <% } %>
150
-
151
- <style>
152
- .hero-project {
153
- color: var(--text-muted);
154
- font-size: 0.95rem;
155
- margin-top: 0.5rem;
156
- }
157
-
158
- /* Active pipeline step */
159
- .step-active {
160
- border-color: var(--primary);
161
- background: rgba(99, 102, 241, 0.08);
162
- }
163
-
164
- .step-active .step-name {
165
- color: var(--primary);
166
- }
167
-
168
- /* Quick links row */
169
- .quick-links-row {
170
- display: flex;
171
- gap: 1rem;
172
- flex-wrap: wrap;
173
- margin: 2rem 0;
174
- }
175
-
176
- .quick-link-card {
177
- flex: 1;
178
- min-width: 140px;
179
- background: var(--surface);
180
- border: 1px solid var(--border);
181
- border-radius: var(--radius);
182
- padding: 1.25rem 1rem;
183
- text-align: center;
184
- text-decoration: none;
185
- color: var(--text);
186
- transition: border-color 0.2s, transform 0.15s;
187
- }
188
-
189
- .quick-link-card:hover {
190
- border-color: var(--primary);
191
- transform: translateY(-2px);
192
- }
193
-
194
- .ql-icon {
195
- font-size: 1.75rem;
196
- margin-bottom: 0.4rem;
197
- }
198
-
199
- .ql-label {
200
- font-size: 0.9rem;
201
- font-weight: 600;
202
- display: flex;
203
- align-items: center;
204
- justify-content: center;
205
- gap: 0.4rem;
206
- }
207
-
208
- /* Info alert */
209
- .alert-info {
210
- background: #1e1b4b;
211
- border: 1px solid #3730a3;
212
- color: #a5b4fc;
213
- }
214
-
215
- @media (max-width: 768px) {
216
- .stats-row {
217
- flex-wrap: wrap;
218
- }
219
-
220
- .stat-card {
221
- flex: 1 1 calc(50% - 0.5rem);
222
- min-width: unset;
223
- }
224
-
225
- .quick-links-row {
226
- gap: 0.75rem;
227
- }
228
-
229
- .quick-link-card {
230
- min-width: calc(50% - 0.375rem);
231
- }
232
-
233
- .flow-steps {
234
- flex-direction: column;
235
- align-items: flex-start;
236
- }
237
-
238
- .flow-arrow {
239
- transform: rotate(90deg);
240
- }
241
- }
242
- </style>
116
+ <div class="flow-arrow">→</div>
117
+ <div class="flow-step <%= stats.plans > 0 ? 'step-active' : '' %>">
118
+ <div class="step-icon">📋</div>
119
+ <div class="step-name">Plan</div>
120
+ <div class="step-desc"><%= stats.plans %> plan<%= stats.plans !== 1 ? 's' : '' %></div>
121
+ </div>
122
+ <div class="flow-arrow">→</div>
123
+ <div class="flow-step <%= stats.specs > 0 ? 'step-active' : '' %>">
124
+ <div class="step-icon">🧪</div>
125
+ <div class="step-name">Generate</div>
126
+ <div class="step-desc"><%= stats.specs %> spec<%= stats.specs !== 1 ? 's' : '' %></div>
127
+ </div>
128
+ <div class="flow-arrow">→</div>
129
+ <div class="flow-step <%= stats.runs > 0 ? 'step-active' : '' %>">
130
+ <div class="step-icon">⚡</div>
131
+ <div class="step-name">Execute</div>
132
+ <div class="step-desc"><%= stats.runs %> run<%= stats.runs !== 1 ? 's' : '' %></div>
133
+ </div>
134
+ <div class="flow-arrow">→</div>
135
+ <div class="flow-step">
136
+ <div class="step-icon">🛠️</div>
137
+ <div class="step-name">Heal</div>
138
+ <div class="step-desc">Auto-fix failures</div>
139
+ </div>
140
+ <div class="flow-arrow">→</div>
141
+ <div class="flow-step">
142
+ <div class="step-icon">📊</div>
143
+ <div class="step-name">Report</div>
144
+ <div class="step-desc">Comprehensive results</div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+
149
+ <!-- ─── Quick Actions ─── -->
150
+ <div class="quick-links-row">
151
+ <a href="/stories?project=<%= project.id %>" class="quick-link-card">
152
+ <div class="ql-icon">📝</div>
153
+ <div class="ql-label">Stories <span class="badge badge-project"><%= stats.stories %></span></div>
154
+ </a>
155
+ <a href="/runs?project=<%= project.id %>" class="quick-link-card">
156
+ <div class="ql-icon">⚡</div>
157
+ <div class="ql-label">Runs <span class="badge badge-project"><%= stats.runs %></span></div>
158
+ </a>
159
+ <a href="/analytics?project=<%= project.id %>" class="quick-link-card">
160
+ <div class="ql-icon">📊</div>
161
+ <div class="ql-label">Analytics</div>
162
+ </a>
163
+ <a href="/projects/<%= project.id %>" class="quick-link-card">
164
+ <div class="ql-icon">⚙️</div>
165
+ <div class="ql-label">Pipeline Orchestrator</div>
166
+ </a>
167
+ <% if (stats.hasAllure) { %>
168
+ <a href="/allure-report?project=<%= project.id %>" class="quick-link-card" target="_blank">
169
+ <div class="ql-icon">📈</div>
170
+ <div class="ql-label">Allure Report</div>
171
+ </a>
172
+ <% } %>
173
+ </div>
174
+
175
+ <% if (stats.stories === 0) { %>
176
+ <div class="alert alert-info" style="margin-top:1.5rem;">
177
+ 💡 <strong>Aucune user story trouvée</strong> dans <code><%= project.path %>/user-story/</code>.
178
+ Ajoutez un fichier <code>.md</code> pour commencer.
179
+ </div>
180
+ <% } %>
181
+ <% } %>
182
+
183
+ <script>
184
+ async function quickExecute(projectId, storyName) {
185
+ window.location.href = `/stories/${encodeURIComponent(storyName)}?project=${projectId}&autoRun=true`;
186
+ }
187
+ </script>
188
+
189
+ <style>
190
+ .hero-project {
191
+ color: var(--text-muted);
192
+ font-size: 0.95rem;
193
+ margin-top: 0.5rem;
194
+ }
195
+ .step-active {
196
+ border-color: var(--primary);
197
+ background: rgba(99, 102, 241, 0.08);
198
+ }
199
+ .step-active .step-name {
200
+ color: var(--primary);
201
+ }
202
+ .quick-links-row {
203
+ display: flex;
204
+ gap: 1rem;
205
+ flex-wrap: wrap;
206
+ margin: 2rem 0;
207
+ }
208
+ .quick-link-card {
209
+ flex: 1;
210
+ min-width: 140px;
211
+ background: var(--surface);
212
+ border: 1px solid var(--border);
213
+ border-radius: var(--radius);
214
+ padding: 1.25rem 1rem;
215
+ text-align: center;
216
+ text-decoration: none;
217
+ color: var(--text);
218
+ transition: border-color 0.2s, transform 0.15s;
219
+ }
220
+ .quick-link-card:hover {
221
+ border-color: var(--primary);
222
+ transform: translateY(-2px);
223
+ }
224
+ .ql-icon {
225
+ font-size: 1.75rem;
226
+ margin-bottom: 0.4rem;
227
+ }
228
+ .ql-label {
229
+ font-size: 0.9rem;
230
+ font-weight: 600;
231
+ display: flex;
232
+ align-items: center;
233
+ justify-content: center;
234
+ gap: 0.4rem;
235
+ }
236
+ .alert-info {
237
+ background: #1e1b4b;
238
+ border: 1px solid #3730a3;
239
+ color: #a5b4fc;
240
+ }
241
+ .stat-card:hover {
242
+ border-color: var(--primary) !important;
243
+ transform: translateY(-2px);
244
+ box-shadow: var(--shadow-2);
245
+ }
246
+ @media (max-width: 768px) {
247
+ .stats-row { flex-wrap: wrap; }
248
+ .stat-card { flex: 1 1 calc(50% - 0.5rem); min-width: unset; }
249
+ .quick-links-row { gap: 0.75rem; }
250
+ .quick-link-card { min-width: calc(50% - 0.375rem); }
251
+ .flow-steps { flex-direction: column; align-items: flex-start; }
252
+ .flow-arrow { transform: rotate(90deg); }
253
+ }
254
+ </style>
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title><%= title || 'QA Dashboard' %> — AI QA Pipeline</title>
6
+ <title><%= title || 'QA Dashboard' %> — Orchestrator QA</title>
7
7
  <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@500;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet">
8
8
  <link rel="stylesheet" href="/css/style.css">
9
9
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
@@ -12,13 +12,14 @@
12
12
  <body>
13
13
  <nav class="navbar">
14
14
  <div class="nav-inner">
15
- <a href="/" class="nav-brand">AI QA Pipeline</a>
15
+ <a href="/" class="nav-brand">Orchestrator QA</a>
16
16
  <div class="nav-links">
17
17
  <a href="/projects">Projects</a>
18
18
  <a href="/stories">Stories</a>
19
19
  <a href="/runs">Runs</a>
20
20
  <a href="/review">Review</a>
21
21
  <a href="/analytics">Analytics</a>
22
+ <a href="/terminal">Terminal</a>
22
23
  <a href="/data">Test Data</a>
23
24
  <button onclick="location.reload()" class="btn-refresh" title="Refresh">↻</button>
24
25
  </div>