orchestrix-yuri 4.6.5 โ 4.6.6
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.
|
@@ -238,7 +238,8 @@ class PhaseOrchestrator {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
if (this._phase === 'test') {
|
|
241
|
-
|
|
241
|
+
const card = this._buildTestProgressCard();
|
|
242
|
+
return { phase: 'test', message: card };
|
|
242
243
|
}
|
|
243
244
|
|
|
244
245
|
if (this._phase === 'iterate') {
|
|
@@ -947,11 +948,14 @@ class PhaseOrchestrator {
|
|
|
947
948
|
|
|
948
949
|
// Start first epic test
|
|
949
950
|
this._loadAndTestEpic();
|
|
951
|
+
this._testStartedAt = Date.now();
|
|
952
|
+
this._lastReportTime = Date.now();
|
|
950
953
|
|
|
951
954
|
const pollInterval = this.config.phase_poll_interval || 30000;
|
|
952
955
|
this._timer = setInterval(() => this._pollTest(), pollInterval);
|
|
953
956
|
|
|
954
|
-
|
|
957
|
+
const reportMin = Math.round(this._reportInterval / 60000);
|
|
958
|
+
log.engine(`Test phase started: session=${this._session}, epics=${epicIds.join(',')}, startIdx=${startIdx}, report every ${reportMin}min`);
|
|
955
959
|
return `๐งช Testing started! ${epicIds.length} epic(s) to test.\n\nQA will smoke-test each epic. Failed tests auto-trigger Dev fixes (max 3 rounds).\nI'll notify you of results.`;
|
|
956
960
|
}
|
|
957
961
|
|
|
@@ -1054,6 +1058,14 @@ class PhaseOrchestrator {
|
|
|
1054
1058
|
return;
|
|
1055
1059
|
}
|
|
1056
1060
|
|
|
1061
|
+
// Periodic progress report (reuses dev phase's _reportInterval)
|
|
1062
|
+
const now = Date.now();
|
|
1063
|
+
if (now - this._lastReportTime >= this._reportInterval) {
|
|
1064
|
+
this._lastReportTime = now;
|
|
1065
|
+
const card = this._buildTestProgressCard();
|
|
1066
|
+
this.onProgress(card);
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1057
1069
|
// Determine which window to watch
|
|
1058
1070
|
const window = ctx.subPhase === 'dev-fixing' ? 2 : 3;
|
|
1059
1071
|
const result = tmx.checkCompletion(this._session, window, this._lastHash);
|
|
@@ -1154,6 +1166,62 @@ class PhaseOrchestrator {
|
|
|
1154
1166
|
}
|
|
1155
1167
|
}
|
|
1156
1168
|
|
|
1169
|
+
/**
|
|
1170
|
+
* Build a progress card for the test phase (used by getStatus + periodic reports).
|
|
1171
|
+
*/
|
|
1172
|
+
_buildTestProgressCard() {
|
|
1173
|
+
const ctx = this._testContext;
|
|
1174
|
+
if (!ctx) return '๐งช Testing in progress.';
|
|
1175
|
+
|
|
1176
|
+
const total = ctx.epicIds.length;
|
|
1177
|
+
const passed = Object.values(ctx.results).filter((r) => r.status === 'passed').length;
|
|
1178
|
+
const failed = Object.values(ctx.results).filter((r) => r.status === 'failed').length;
|
|
1179
|
+
const tested = passed + failed;
|
|
1180
|
+
const pct = total > 0 ? Math.round((tested / total) * 100) : 0;
|
|
1181
|
+
|
|
1182
|
+
const currentEpic = ctx.epicIds[ctx.epicIdx];
|
|
1183
|
+
const subPhaseLabel = {
|
|
1184
|
+
'qa-testing': '๐ QA smoke-testing',
|
|
1185
|
+
'dev-fixing': '๐ง Dev fixing bug',
|
|
1186
|
+
}[ctx.subPhase] || 'โณ Loading agent';
|
|
1187
|
+
|
|
1188
|
+
const elapsed = this._devStartedAt ? this._formatDuration(Date.now() - this._devStartedAt) : '';
|
|
1189
|
+
const startedAt = this._testStartedAt ? this._formatDuration(Date.now() - this._testStartedAt) : '';
|
|
1190
|
+
|
|
1191
|
+
const lines = [
|
|
1192
|
+
`๐งช **Test Phase Progress**`,
|
|
1193
|
+
``,
|
|
1194
|
+
this._progressBar(pct),
|
|
1195
|
+
``,
|
|
1196
|
+
`๐ Epics: ${tested}/${total} tested (โ
${passed} passed, โ ${failed} failed)`,
|
|
1197
|
+
];
|
|
1198
|
+
|
|
1199
|
+
if (ctx.epicIdx < total) {
|
|
1200
|
+
lines.push(`๐ฏ Current: Epic ${currentEpic} โ ${subPhaseLabel} (round ${ctx.round + 1}/${ctx.maxRounds})`);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// Per-epic summary
|
|
1204
|
+
if (tested > 0) {
|
|
1205
|
+
lines.push('', '๐ Results:');
|
|
1206
|
+
for (const epicId of ctx.epicIds) {
|
|
1207
|
+
const r = ctx.results[epicId];
|
|
1208
|
+
if (r && r.status === 'passed') {
|
|
1209
|
+
lines.push(` โ
Epic ${epicId} โ passed (${r.rounds} round(s))`);
|
|
1210
|
+
} else if (r && r.status === 'failed') {
|
|
1211
|
+
lines.push(` โ Epic ${epicId} โ failed (${r.rounds} rounds)`);
|
|
1212
|
+
} else if (epicId === currentEpic) {
|
|
1213
|
+
lines.push(` โณ Epic ${epicId} โ in progress`);
|
|
1214
|
+
} else {
|
|
1215
|
+
lines.push(` โฌ Epic ${epicId} โ pending`);
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
if (startedAt) lines.push(`\nโฑ๏ธ Elapsed: ${startedAt}`);
|
|
1221
|
+
|
|
1222
|
+
return lines.join('\n');
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1157
1225
|
/**
|
|
1158
1226
|
* Complete test phase: aggregate results, update state files, send summary.
|
|
1159
1227
|
*/
|
|
@@ -1220,6 +1288,7 @@ class PhaseOrchestrator {
|
|
|
1220
1288
|
|
|
1221
1289
|
this._phase = null;
|
|
1222
1290
|
this._testContext = null;
|
|
1291
|
+
this._testStartedAt = null;
|
|
1223
1292
|
log.engine(`Test phase complete: ${passed}/${total} passed, ${failed} failed`);
|
|
1224
1293
|
this.onComplete('test', lines.join('\n'));
|
|
1225
1294
|
}
|
package/lib/gateway/router.js
CHANGED
|
@@ -99,25 +99,38 @@ class Router {
|
|
|
99
99
|
|
|
100
100
|
try {
|
|
101
101
|
const focus = yaml.load(fs.readFileSync(focusPath, 'utf8')) || {};
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
102
|
+
const phaseNum = parseInt(focus.phase, 10);
|
|
103
|
+
|
|
104
|
+
if (phaseNum === 3) {
|
|
105
|
+
// Skip if dev phase already complete
|
|
106
|
+
const phase3Path = path.join(projectRoot, '.yuri', 'state', 'phase3.yaml');
|
|
107
|
+
if (fs.existsSync(phase3Path)) {
|
|
108
|
+
const phase3 = yaml.load(fs.readFileSync(phase3Path, 'utf8')) || {};
|
|
109
|
+
if (phase3.status === 'complete') return;
|
|
110
|
+
}
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
// Check tmux session is alive
|
|
113
|
+
const { execSync } = require('child_process');
|
|
114
|
+
const sessions = execSync('tmux list-sessions -F "#{session_name}" 2>/dev/null', { encoding: 'utf8' }).trim();
|
|
115
|
+
if (!sessions.split('\n').some((s) => s.startsWith('orchestrix-'))) return;
|
|
115
116
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
const card = this._buildStatusCard(projectRoot, focus);
|
|
118
|
+
if (card) {
|
|
119
|
+
log.router('Auto-reporting dev progress');
|
|
120
|
+
this._sendProactive(card);
|
|
121
|
+
}
|
|
122
|
+
} else if (phaseNum === 4) {
|
|
123
|
+
// Skip if test phase already complete
|
|
124
|
+
const phase4Path = path.join(projectRoot, '.yuri', 'state', 'phase4.yaml');
|
|
125
|
+
if (!fs.existsSync(phase4Path)) return;
|
|
126
|
+
const phase4 = yaml.load(fs.readFileSync(phase4Path, 'utf8')) || {};
|
|
127
|
+
if (phase4.status === 'complete' || phase4.status === 'complete_with_failures') return;
|
|
128
|
+
|
|
129
|
+
const card = this._buildTestStatusCard(projectRoot);
|
|
130
|
+
if (card) {
|
|
131
|
+
log.router('Auto-reporting test progress');
|
|
132
|
+
this._sendProactive(card);
|
|
133
|
+
}
|
|
121
134
|
}
|
|
122
135
|
} catch { /* silent */ }
|
|
123
136
|
}, interval);
|
|
@@ -407,6 +420,16 @@ Reply with ONLY one word: small, medium, or large. Nothing else.`;
|
|
|
407
420
|
}
|
|
408
421
|
}
|
|
409
422
|
|
|
423
|
+
// Test phase: generate test progress card from phase4.yaml
|
|
424
|
+
if (phaseNum === 4 && parts.length === 0) {
|
|
425
|
+
try {
|
|
426
|
+
const card = this._buildTestStatusCard(projectRoot);
|
|
427
|
+
if (card) parts.push(card);
|
|
428
|
+
} catch (err) {
|
|
429
|
+
log.warn(`Test progress card failed: ${err.message}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
410
433
|
// Fallback or non-dev phase
|
|
411
434
|
if (parts.length === 0) {
|
|
412
435
|
if (focus.pulse) parts.push(`Pulse: ${focus.pulse}`);
|
|
@@ -824,6 +847,52 @@ Reply with ONLY one word: small, medium, or large. Nothing else.`;
|
|
|
824
847
|
return lines.join('\n');
|
|
825
848
|
}
|
|
826
849
|
|
|
850
|
+
// โโ Test Progress Card (from phase4.yaml, for *status when orchestrator is not tracking) โโ
|
|
851
|
+
|
|
852
|
+
_buildTestStatusCard(projectRoot) {
|
|
853
|
+
const phase4Path = path.join(projectRoot, '.yuri', 'state', 'phase4.yaml');
|
|
854
|
+
if (!fs.existsSync(phase4Path)) return null;
|
|
855
|
+
|
|
856
|
+
const state = yaml.load(fs.readFileSync(phase4Path, 'utf8')) || {};
|
|
857
|
+
if (!Array.isArray(state.epics) || state.epics.length === 0) return null;
|
|
858
|
+
|
|
859
|
+
const total = state.epics.length;
|
|
860
|
+
const passed = state.epics.filter((e) => e.status === 'passed').length;
|
|
861
|
+
const failed = state.epics.filter((e) => e.status === 'failed').length;
|
|
862
|
+
const tested = passed + failed;
|
|
863
|
+
const pct = total > 0 ? Math.round((tested / total) * 100) : 0;
|
|
864
|
+
|
|
865
|
+
const barTotal = 20;
|
|
866
|
+
const filled = Math.round(pct / 100 * barTotal);
|
|
867
|
+
const bar = 'โ'.repeat(filled) + 'โ'.repeat(barTotal - filled) + ` ${pct}%`;
|
|
868
|
+
|
|
869
|
+
const lines = [
|
|
870
|
+
'๐งช **Test Phase Progress**',
|
|
871
|
+
'',
|
|
872
|
+
bar,
|
|
873
|
+
'',
|
|
874
|
+
`๐ Epics: ${tested}/${total} tested (โ
${passed} passed, โ ${failed} failed)`,
|
|
875
|
+
'',
|
|
876
|
+
'๐ Results:',
|
|
877
|
+
];
|
|
878
|
+
|
|
879
|
+
for (const epic of state.epics) {
|
|
880
|
+
if (epic.status === 'passed') {
|
|
881
|
+
lines.push(` โ
Epic ${epic.id} โ passed (${epic.rounds || 0} round(s))`);
|
|
882
|
+
} else if (epic.status === 'failed') {
|
|
883
|
+
lines.push(` โ Epic ${epic.id} โ failed (${epic.rounds || 0} rounds)`);
|
|
884
|
+
} else {
|
|
885
|
+
lines.push(` โฌ Epic ${epic.id} โ ${epic.status || 'pending'}`);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
if (state.status === 'complete' || state.status === 'complete_with_failures') {
|
|
890
|
+
lines.push(`\nโ
Testing finished at ${state.completed_at || 'unknown'}`);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
return lines.join('\n');
|
|
894
|
+
}
|
|
895
|
+
|
|
827
896
|
// โโ Help โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
828
897
|
|
|
829
898
|
_buildHelpText() {
|