opencastle 0.17.0 → 0.19.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/dist/cli/convoy/engine.d.ts.map +1 -1
- package/dist/cli/convoy/engine.js +15 -2
- package/dist/cli/convoy/engine.js.map +1 -1
- package/dist/cli/convoy/export.d.ts +3 -0
- package/dist/cli/convoy/export.d.ts.map +1 -0
- package/dist/cli/convoy/export.js +46 -0
- package/dist/cli/convoy/export.js.map +1 -0
- package/dist/cli/convoy/export.test.d.ts +2 -0
- package/dist/cli/convoy/export.test.d.ts.map +1 -0
- package/dist/cli/convoy/export.test.js +157 -0
- package/dist/cli/convoy/export.test.js.map +1 -0
- package/dist/cli/dashboard.d.ts +14 -0
- package/dist/cli/dashboard.d.ts.map +1 -1
- package/dist/cli/dashboard.js +73 -36
- package/dist/cli/dashboard.js.map +1 -1
- package/dist/cli/run.d.ts +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +16 -1
- package/dist/cli/run.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/convoy/engine.ts +9 -2
- package/src/cli/convoy/export.test.ts +190 -0
- package/src/cli/convoy/export.ts +52 -0
- package/src/cli/dashboard.ts +94 -42
- package/src/cli/run.ts +16 -1
- package/src/dashboard/dist/_astro/{index.Bnq19_1M.css → index.DyyaCW8L.css} +1 -1
- package/src/dashboard/dist/index.html +145 -6
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/src/pages/index.astro +160 -4
- package/src/dashboard/src/styles/dashboard.css +60 -0
- package/src/orchestrator/agents/session-guard.agent.md +14 -0
- package/src/orchestrator/agents/team-lead.agent.md +45 -2
- package/src/orchestrator/prompts/bootstrap-customizations.prompt.md +1 -1
- package/src/orchestrator/prompts/brainstorm.prompt.md +2 -1
- package/src/orchestrator/prompts/{generate-task-spec.prompt.md → generate-convoy.prompt.md} +4 -4
- package/src/orchestrator/prompts/implement-feature.prompt.md +27 -1
- package/src/orchestrator/prompts/quick-refinement.prompt.md +2 -0
|
@@ -26,6 +26,7 @@ const base = import.meta.env.BASE_URL;
|
|
|
26
26
|
<!-- Sidebar Navigation -->
|
|
27
27
|
<nav class="dash-sidebar" id="dash-sidebar">
|
|
28
28
|
<ul class="dash-sidebar__list">
|
|
29
|
+
<li><a class="dash-sidebar__link" href="#convoy-section" data-section="convoy-section">Convoy</a></li>
|
|
29
30
|
<li><a class="dash-sidebar__link dash-sidebar__link--active" href="#kpi-row" data-section="kpi-row">Overview</a></li>
|
|
30
31
|
<li><a class="dash-sidebar__link" href="#pipeline-section" data-section="pipeline-section">Pipeline</a></li>
|
|
31
32
|
<li><a class="dash-sidebar__link" href="#agent-section" data-section="agent-section">Agents</a></li>
|
|
@@ -66,9 +67,25 @@ const base = import.meta.env.BASE_URL;
|
|
|
66
67
|
<option value="failed">Failed</option>
|
|
67
68
|
</select>
|
|
68
69
|
</div>
|
|
70
|
+
<div class="filter-group">
|
|
71
|
+
<label class="filter-label" for="filter-convoy">Convoy</label>
|
|
72
|
+
<select class="filter-select" id="filter-convoy">
|
|
73
|
+
<option value="">All convoys</option>
|
|
74
|
+
</select>
|
|
75
|
+
</div>
|
|
69
76
|
<button class="dash-btn dash-btn--ghost filter-reset" id="filter-reset" type="button">Reset</button>
|
|
70
77
|
</div>
|
|
71
78
|
|
|
79
|
+
<!-- Convoy Status Section -->
|
|
80
|
+
<section class="chart-card convoy-status" id="convoy-section" data-nav-section style="display:none">
|
|
81
|
+
<div class="chart-card__header">
|
|
82
|
+
<h2 class="chart-card__title">Convoy Status</h2>
|
|
83
|
+
<p class="chart-card__desc" id="convoy-desc">Select a convoy to view details</p>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="chart-card__body" id="convoy-body">
|
|
86
|
+
</div>
|
|
87
|
+
</section>
|
|
88
|
+
|
|
72
89
|
<!-- KPI Row -->
|
|
73
90
|
<section class="kpi-row" id="kpi-row" data-nav-section>
|
|
74
91
|
<div class="kpi-card" id="kpi-sessions">
|
|
@@ -1154,12 +1171,14 @@ const base = import.meta.env.BASE_URL;
|
|
|
1154
1171
|
let rawDelegations = [];
|
|
1155
1172
|
let rawPanels = [];
|
|
1156
1173
|
let rawReviews = [];
|
|
1174
|
+
let rawConvoys = [];
|
|
1157
1175
|
|
|
1158
1176
|
function applyFilters() {
|
|
1159
1177
|
const dateFrom = document.getElementById('filter-date-from').value;
|
|
1160
1178
|
const dateTo = document.getElementById('filter-date-to').value;
|
|
1161
1179
|
const agentFilter = document.getElementById('filter-agent').value;
|
|
1162
1180
|
const outcomeFilter = document.getElementById('filter-outcome').value;
|
|
1181
|
+
const convoyFilter = document.getElementById('filter-convoy').value;
|
|
1163
1182
|
|
|
1164
1183
|
function matchDate(ts) {
|
|
1165
1184
|
const date = ts.slice(0, 10);
|
|
@@ -1168,27 +1187,43 @@ const base = import.meta.env.BASE_URL;
|
|
|
1168
1187
|
return true;
|
|
1169
1188
|
}
|
|
1170
1189
|
|
|
1171
|
-
|
|
1190
|
+
let sessions = rawSessions.filter((s) => {
|
|
1172
1191
|
if (!matchDate(s.timestamp)) return false;
|
|
1173
1192
|
if (agentFilter && s.agent !== agentFilter) return false;
|
|
1174
1193
|
if (outcomeFilter && s.outcome !== outcomeFilter) return false;
|
|
1175
1194
|
return true;
|
|
1176
1195
|
});
|
|
1177
1196
|
|
|
1178
|
-
|
|
1197
|
+
let delegations = rawDelegations.filter((d) => {
|
|
1179
1198
|
if (!matchDate(d.timestamp)) return false;
|
|
1180
1199
|
if (agentFilter && d.agent !== agentFilter) return false;
|
|
1181
1200
|
if (outcomeFilter && d.outcome !== outcomeFilter) return false;
|
|
1182
1201
|
return true;
|
|
1183
1202
|
});
|
|
1184
1203
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1204
|
+
let panels = rawPanels.filter((p) => matchDate(p.timestamp));
|
|
1205
|
+
let reviews = rawReviews.filter((r) => {
|
|
1187
1206
|
if (!matchDate(r.timestamp)) return false;
|
|
1188
1207
|
if (agentFilter && r.agent !== agentFilter) return false;
|
|
1189
1208
|
return true;
|
|
1190
1209
|
});
|
|
1191
1210
|
|
|
1211
|
+
if (convoyFilter) {
|
|
1212
|
+
sessions = sessions.filter((s) => s.convoy_id === convoyFilter);
|
|
1213
|
+
delegations = delegations.filter((d) => d.convoy_id === convoyFilter);
|
|
1214
|
+
panels = panels.filter((p) => p.convoy_id === convoyFilter);
|
|
1215
|
+
reviews = reviews.filter((r) => r.convoy_id === convoyFilter);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
const convoySection = document.getElementById('convoy-section');
|
|
1219
|
+
if (convoySection) {
|
|
1220
|
+
convoySection.style.display = convoyFilter ? '' : 'none';
|
|
1221
|
+
if (convoyFilter) {
|
|
1222
|
+
const convoy = rawConvoys.find((c) => c.id === convoyFilter);
|
|
1223
|
+
renderConvoyStatus(convoy);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1192
1227
|
renderAll(sessions, delegations, panels, reviews);
|
|
1193
1228
|
}
|
|
1194
1229
|
|
|
@@ -1300,8 +1335,80 @@ const base = import.meta.env.BASE_URL;
|
|
|
1300
1335
|
URL.revokeObjectURL(url);
|
|
1301
1336
|
}
|
|
1302
1337
|
|
|
1338
|
+
function populateConvoyFilter(convoys) {
|
|
1339
|
+
const select = document.getElementById('filter-convoy');
|
|
1340
|
+
if (!select) return;
|
|
1341
|
+
while (select.options.length > 1) select.remove(1);
|
|
1342
|
+
const sorted = convoys.slice().sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
1343
|
+
sorted.forEach((c) => {
|
|
1344
|
+
const opt = document.createElement('option');
|
|
1345
|
+
opt.value = c.id;
|
|
1346
|
+
opt.textContent = c.name + ' (' + c.status + ')';
|
|
1347
|
+
select.appendChild(opt);
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
function renderConvoyStatus(convoy) {
|
|
1352
|
+
const descEl = document.getElementById('convoy-desc');
|
|
1353
|
+
const bodyEl = document.getElementById('convoy-body');
|
|
1354
|
+
if (!descEl || !bodyEl) return;
|
|
1355
|
+
|
|
1356
|
+
if (!convoy) {
|
|
1357
|
+
bodyEl.innerHTML = emptyStateHtml('pipeline', 'Convoy not found', 'No matching convoy data available.');
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
descEl.textContent = convoy.name + ' — ' + (convoy.branch || 'no branch');
|
|
1362
|
+
|
|
1363
|
+
const s = convoy.summary || {};
|
|
1364
|
+
const total = s.total || (convoy.tasks ? convoy.tasks.length : 0);
|
|
1365
|
+
const done = s.done || 0;
|
|
1366
|
+
const pct = total > 0 ? Math.round((done / total) * 100) : 0;
|
|
1367
|
+
|
|
1368
|
+
const statusClass = convoy.status === 'done' ? 'success'
|
|
1369
|
+
: (convoy.status === 'failed' || convoy.status === 'gate-failed') ? 'failed'
|
|
1370
|
+
: convoy.status === 'running' ? 'partial' : '';
|
|
1371
|
+
|
|
1372
|
+
let html = '';
|
|
1373
|
+
html += '<div class="convoy-overview">';
|
|
1374
|
+
html += '<div class="convoy-stat"><span class="convoy-stat__label">Status</span><span class="outcome-badge outcome-badge--' + statusClass + '">' + escapeHtml(convoy.status) + '</span></div>';
|
|
1375
|
+
html += '<div class="convoy-stat"><span class="convoy-stat__label">Branch</span><span class="convoy-stat__value">' + escapeHtml(convoy.branch || '\u2014') + '</span></div>';
|
|
1376
|
+
html += '<div class="convoy-stat"><span class="convoy-stat__label">Tasks</span><span class="convoy-stat__value">' + done + '/' + total + '</span></div>';
|
|
1377
|
+
html += '<div class="convoy-stat"><span class="convoy-stat__label">Events</span><span class="convoy-stat__value">' + (convoy.events_count || 0) + '</span></div>';
|
|
1378
|
+
html += '<div class="convoy-stat"><span class="convoy-stat__label">Started</span><span class="convoy-stat__value">' + (convoy.started_at ? formatTime(convoy.started_at) : '\u2014') + '</span></div>';
|
|
1379
|
+
html += '</div>';
|
|
1380
|
+
|
|
1381
|
+
html += '<div class="convoy-progress">';
|
|
1382
|
+
html += '<div class="convoy-progress__bar"><div class="convoy-progress__fill" style="width:' + pct + '%"></div></div>';
|
|
1383
|
+
html += '<span class="convoy-progress__label">' + pct + '% complete</span>';
|
|
1384
|
+
html += '</div>';
|
|
1385
|
+
|
|
1386
|
+
if (convoy.tasks && convoy.tasks.length > 0) {
|
|
1387
|
+
html += '<table class="sessions-table convoy-tasks">';
|
|
1388
|
+
html += '<thead><tr><th>Task</th><th>Phase</th><th>Agent</th><th>Adapter</th><th>Status</th><th>Retries</th></tr></thead>';
|
|
1389
|
+
html += '<tbody>';
|
|
1390
|
+
convoy.tasks.forEach(function(t) {
|
|
1391
|
+
const tStatus = t.status === 'done' ? 'success'
|
|
1392
|
+
: (t.status === 'failed' || t.status === 'timed-out') ? 'failed'
|
|
1393
|
+
: t.status === 'running' ? 'partial' : '';
|
|
1394
|
+
html += '<tr>';
|
|
1395
|
+
html += '<td>' + escapeHtml(t.id) + '</td>';
|
|
1396
|
+
html += '<td class="td-num">' + t.phase + '</td>';
|
|
1397
|
+
html += '<td class="td-agent">' + escapeHtml(t.agent) + '</td>';
|
|
1398
|
+
html += '<td>' + escapeHtml(t.adapter || '\u2014') + '</td>';
|
|
1399
|
+
html += '<td><span class="outcome-badge outcome-badge--' + tStatus + '">' + escapeHtml(t.status) + '</span></td>';
|
|
1400
|
+
html += '<td class="td-num">' + (t.retries || 0) + '</td>';
|
|
1401
|
+
html += '</tr>';
|
|
1402
|
+
});
|
|
1403
|
+
html += '</tbody></table>';
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
bodyEl.innerHTML = html;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1303
1409
|
async function main() {
|
|
1304
1410
|
const events = await loadNdjson(base + 'data/events.ndjson');
|
|
1411
|
+
const convoys = await loadNdjson(base + 'data/convoys.ndjson');
|
|
1305
1412
|
|
|
1306
1413
|
const sessions = events.filter((e) => e.type === 'session');
|
|
1307
1414
|
const delegations = events.filter((e) => e.type === 'delegation');
|
|
@@ -1312,23 +1419,72 @@ const base = import.meta.env.BASE_URL;
|
|
|
1312
1419
|
rawDelegations = delegations;
|
|
1313
1420
|
rawPanels = panels;
|
|
1314
1421
|
rawReviews = reviews;
|
|
1422
|
+
rawConvoys = convoys;
|
|
1315
1423
|
|
|
1316
1424
|
populateAgentFilter(sessions, delegations, reviews);
|
|
1425
|
+
populateConvoyFilter(convoys);
|
|
1426
|
+
|
|
1427
|
+
// ── Read URL params ───────────────────────────────────
|
|
1428
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
1429
|
+
const convoyParam = urlParams.get('convoy');
|
|
1430
|
+
if (convoyParam === 'active') {
|
|
1431
|
+
const running = rawConvoys.find((c) => c.status === 'running');
|
|
1432
|
+
const latest = rawConvoys.slice().sort((a, b) => b.created_at.localeCompare(a.created_at))[0];
|
|
1433
|
+
const target = running || latest;
|
|
1434
|
+
if (target) {
|
|
1435
|
+
const sel = document.getElementById('filter-convoy');
|
|
1436
|
+
if (sel) sel.value = target.id;
|
|
1437
|
+
}
|
|
1438
|
+
} else if (convoyParam) {
|
|
1439
|
+
const sel = document.getElementById('filter-convoy');
|
|
1440
|
+
if (sel) sel.value = convoyParam;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1317
1443
|
renderAll(sessions, delegations, panels, reviews);
|
|
1318
1444
|
|
|
1445
|
+
// Apply convoy param after initial render (shows convoy section if needed)
|
|
1446
|
+
if (convoyParam) applyFilters();
|
|
1447
|
+
|
|
1319
1448
|
// ── Filter event listeners ────────────────────────────
|
|
1320
1449
|
document.getElementById('filter-date-from')?.addEventListener('change', applyFilters);
|
|
1321
1450
|
document.getElementById('filter-date-to')?.addEventListener('change', applyFilters);
|
|
1322
1451
|
document.getElementById('filter-agent')?.addEventListener('change', applyFilters);
|
|
1323
1452
|
document.getElementById('filter-outcome')?.addEventListener('change', applyFilters);
|
|
1453
|
+
document.getElementById('filter-convoy')?.addEventListener('change', applyFilters);
|
|
1324
1454
|
document.getElementById('filter-reset')?.addEventListener('click', () => {
|
|
1325
1455
|
document.getElementById('filter-date-from').value = '';
|
|
1326
1456
|
document.getElementById('filter-date-to').value = '';
|
|
1327
1457
|
document.getElementById('filter-agent').value = '';
|
|
1328
1458
|
document.getElementById('filter-outcome').value = '';
|
|
1459
|
+
document.getElementById('filter-convoy').value = '';
|
|
1329
1460
|
applyFilters();
|
|
1330
1461
|
});
|
|
1331
1462
|
|
|
1463
|
+
// ── Auto-refresh for live convoy monitoring ───────────
|
|
1464
|
+
let refreshInterval = null;
|
|
1465
|
+
function startAutoRefresh() {
|
|
1466
|
+
if (refreshInterval) return;
|
|
1467
|
+
refreshInterval = setInterval(async () => {
|
|
1468
|
+
const freshEvents = await loadNdjson(base + 'data/events.ndjson');
|
|
1469
|
+
const freshConvoys = await loadNdjson(base + 'data/convoys.ndjson');
|
|
1470
|
+
rawSessions = freshEvents.filter((e) => e.type === 'session');
|
|
1471
|
+
rawDelegations = freshEvents.filter((e) => e.type === 'delegation');
|
|
1472
|
+
rawPanels = freshEvents.filter((e) => e.type === 'panel');
|
|
1473
|
+
rawReviews = freshEvents.filter((e) => e.type === 'review');
|
|
1474
|
+
rawConvoys = freshConvoys;
|
|
1475
|
+
const currentValue = document.getElementById('filter-convoy')?.value;
|
|
1476
|
+
populateConvoyFilter(freshConvoys);
|
|
1477
|
+
const sel = document.getElementById('filter-convoy');
|
|
1478
|
+
if (sel && currentValue) sel.value = currentValue;
|
|
1479
|
+
applyFilters();
|
|
1480
|
+
}, 5000);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
const selectedConvoy = rawConvoys.find((c) => c.id === document.getElementById('filter-convoy')?.value);
|
|
1484
|
+
if (convoyParam === 'active' || (selectedConvoy && selectedConvoy.status === 'running')) {
|
|
1485
|
+
startAutoRefresh();
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1332
1488
|
// ── Export button ─────────────────────────────────────
|
|
1333
1489
|
document.getElementById('export-btn')?.addEventListener('click', exportData);
|
|
1334
1490
|
|
|
@@ -1533,3 +1533,63 @@ body {
|
|
|
1533
1533
|
padding: 8px 6px;
|
|
1534
1534
|
}
|
|
1535
1535
|
}
|
|
1536
|
+
|
|
1537
|
+
/* ---------- Convoy Section ---------- */
|
|
1538
|
+
.convoy-overview {
|
|
1539
|
+
display: flex;
|
|
1540
|
+
flex-wrap: wrap;
|
|
1541
|
+
gap: 24px;
|
|
1542
|
+
margin-bottom: 20px;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
.convoy-stat {
|
|
1546
|
+
display: flex;
|
|
1547
|
+
flex-direction: column;
|
|
1548
|
+
gap: 4px;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
.convoy-stat__label {
|
|
1552
|
+
font-size: 0.75rem;
|
|
1553
|
+
color: var(--text-tertiary);
|
|
1554
|
+
text-transform: uppercase;
|
|
1555
|
+
letter-spacing: 0.05em;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
.convoy-stat__value {
|
|
1559
|
+
font-size: 0.95rem;
|
|
1560
|
+
color: var(--text-primary);
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/* Progress bar */
|
|
1564
|
+
.convoy-progress {
|
|
1565
|
+
display: flex;
|
|
1566
|
+
align-items: center;
|
|
1567
|
+
gap: 12px;
|
|
1568
|
+
margin-bottom: 20px;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
.convoy-progress__bar {
|
|
1572
|
+
flex: 1;
|
|
1573
|
+
height: 8px;
|
|
1574
|
+
background: var(--bg-tertiary);
|
|
1575
|
+
border-radius: 4px;
|
|
1576
|
+
overflow: hidden;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
.convoy-progress__fill {
|
|
1580
|
+
height: 100%;
|
|
1581
|
+
background: var(--gradient-accent);
|
|
1582
|
+
border-radius: 4px;
|
|
1583
|
+
transition: width var(--transition-base);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
.convoy-progress__label {
|
|
1587
|
+
font-size: 0.8rem;
|
|
1588
|
+
color: var(--text-secondary);
|
|
1589
|
+
white-space: nowrap;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
/* Task table inherits .sessions-table styles */
|
|
1593
|
+
.convoy-tasks {
|
|
1594
|
+
margin-top: 8px;
|
|
1595
|
+
}
|
|
@@ -72,6 +72,20 @@ If the session summary mentions fast reviews or panel reviews, verify matching r
|
|
|
72
72
|
|
|
73
73
|
Only flag if the session produced code changes that should have been committed. Research-only or analysis sessions may not produce commits.
|
|
74
74
|
|
|
75
|
+
### 7. Convoy Observability (if convoy was executed)
|
|
76
|
+
|
|
77
|
+
If the session involved running a convoy (check for `.opencastle/convoy.db` or references to convoy execution in the session summary):
|
|
78
|
+
|
|
79
|
+
**Verify convoy NDJSON export:**
|
|
80
|
+
- `cat .opencastle/logs/convoys.ndjson | tail -1` should show the latest convoy record
|
|
81
|
+
- Record should have `status: done` or `status: failed` (not `running`)
|
|
82
|
+
|
|
83
|
+
**Verify convoy tasks logged:**
|
|
84
|
+
- Each completed convoy task should have a corresponding event in the NDJSON log
|
|
85
|
+
- Check: `grep '"type":"session"' .github/customizations/logs/events.ndjson | tail -10`
|
|
86
|
+
|
|
87
|
+
**Fix:** If convoy export is missing, the engine should have auto-exported. Manual export: run `opencastle run --status` to verify the convoy completed.
|
|
88
|
+
|
|
75
89
|
## Output
|
|
76
90
|
|
|
77
91
|
Return a structured report:
|
|
@@ -17,9 +17,12 @@ handoffs:
|
|
|
17
17
|
- label: Quick Refinement
|
|
18
18
|
agent: 'Team Lead (OpenCastle)'
|
|
19
19
|
prompt: 'Use the quick-refinement prompt to handle these follow-up refinements (UI tweaks, polish, adjustments):'
|
|
20
|
-
- label: Generate Convoy
|
|
20
|
+
- label: Generate Convoy
|
|
21
21
|
agent: 'Team Lead (OpenCastle)'
|
|
22
|
-
prompt: 'Use the generate-
|
|
22
|
+
prompt: 'Use the generate-convoy prompt to create a .convoy.yml spec for autonomous convoy execution based on:'
|
|
23
|
+
- label: Run Convoy
|
|
24
|
+
agent: 'Team Lead (OpenCastle)'
|
|
25
|
+
prompt: 'Run an existing .convoy.yml spec file. Parse the spec, validate the DAG, and execute via the convoy engine:'
|
|
23
26
|
- label: Resolve PR Comments
|
|
24
27
|
agent: 'Team Lead (OpenCastle)'
|
|
25
28
|
prompt: 'Use the resolve-pr-comments prompt to resolve the GitHub PR review comments on this PR:'
|
|
@@ -135,6 +138,46 @@ See the **team-lead-reference** skill for model tiers, token estimates, duration
|
|
|
135
138
|
|
|
136
139
|
Before EVERY delegation verify: (1) Tracker issue exists, (2) File partition is clean, (3) Dependencies verified Done, (4) Prompt includes file paths + acceptance criteria, (5) Self-improvement reminder included.
|
|
137
140
|
|
|
141
|
+
## Convoy Integration
|
|
142
|
+
|
|
143
|
+
The convoy engine is the preferred execution mechanism for multi-task work. Use it when a request decomposes into 3 or more subtasks.
|
|
144
|
+
|
|
145
|
+
### When to use convoy vs. direct delegation
|
|
146
|
+
|
|
147
|
+
| Task count | Approach |
|
|
148
|
+
|------------|----------|
|
|
149
|
+
| 1–2 subtasks | **Direct delegation** — sub-agents inline, standard workflow |
|
|
150
|
+
| 3+ subtasks | **Convoy execution** — generate spec, hand to user to run |
|
|
151
|
+
|
|
152
|
+
### How to generate a convoy spec
|
|
153
|
+
|
|
154
|
+
1. Decompose the request into tasks as normal (Steps 1–2)
|
|
155
|
+
2. Use the `generate-convoy` prompt with the decomposed task list as context
|
|
156
|
+
3. The `generate-convoy` prompt produces a valid `.convoy.yml` spec with DAG, agents, file scopes, and gates
|
|
157
|
+
|
|
158
|
+
### How to execute a convoy
|
|
159
|
+
|
|
160
|
+
Tell the user to run:
|
|
161
|
+
```
|
|
162
|
+
npx opencastle run -f <name>.convoy.yml
|
|
163
|
+
```
|
|
164
|
+
This gives the user control over when execution starts (preferred — supports overnight/unattended runs and manual review of the spec before execution).
|
|
165
|
+
|
|
166
|
+
### After convoy completes
|
|
167
|
+
|
|
168
|
+
1. Run all validation gates (lint, test, build) on the convoy's output branch
|
|
169
|
+
2. Open a PR from the convoy's configured `branch` — do NOT merge
|
|
170
|
+
3. Link the PR in the tracker issue
|
|
171
|
+
4. Log the session record as usual
|
|
172
|
+
|
|
173
|
+
### What the convoy engine handles automatically
|
|
174
|
+
|
|
175
|
+
- **Isolated git worktrees** per task — parallel agents never touch the same files
|
|
176
|
+
- **Parallel execution** with configurable concurrency
|
|
177
|
+
- **Merge queue ordering** — respects `depends_on` DAG when merging worktrees
|
|
178
|
+
- **Crash recovery** — `opencastle run --resume` continues from last checkpoint
|
|
179
|
+
- **Progress monitoring** — `opencastle run --status` shows live task state
|
|
180
|
+
|
|
138
181
|
## Workflow
|
|
139
182
|
|
|
140
183
|
### Step 1: Understand
|
|
@@ -270,7 +270,7 @@ Now that your project is configured, here's what you can do:
|
|
|
270
270
|
3. **Implement a feature** — Use the **"Implement Feature"** prompt to have the Team Lead orchestrate a full feature build with task tracking, delegation, and verification
|
|
271
271
|
4. **Fix a bug** — Use the **"Bug Fix"** prompt for structured triage, root cause analysis, and fix with tracker tracking
|
|
272
272
|
5. **Brainstorm first** — Not sure how to approach something? Use the **"Brainstorm"** prompt to explore requirements and trade-offs before committing to a plan
|
|
273
|
-
6. **
|
|
273
|
+
6. **Generate a convoy spec** — Use the **"Generate Convoy"** prompt to create a `.convoy.yml` spec for autonomous convoy execution with `npx opencastle run` CLI command.
|
|
274
274
|
|
|
275
275
|
## Guidelines
|
|
276
276
|
|
|
@@ -107,11 +107,12 @@ Not every task needs a brainstorm. Skip this prompt and go directly to `implemen
|
|
|
107
107
|
- The task is a simple config change or docs update
|
|
108
108
|
- The technical approach is obvious and unambiguous
|
|
109
109
|
- The scope is a single file or component with no design decisions
|
|
110
|
+
- The task is well-understood and can be expressed as a convoy spec directly → use `generate-convoy` instead
|
|
110
111
|
|
|
111
112
|
## After Brainstorming
|
|
112
113
|
|
|
113
114
|
Once the brainstorm is complete and the user confirms (or you're confident in the approach):
|
|
114
115
|
|
|
115
|
-
1. **Transition to planning** — use the brainstorm report as input for `implement-feature`
|
|
116
|
+
1. **Transition to planning** — use the brainstorm report as input for `implement-feature` (which will choose between direct delegation and convoy execution based on task count)
|
|
116
117
|
2. **Preserve context** — include the brainstorm report in delegation prompts so agents understand *why* an approach was chosen
|
|
117
118
|
3. **Reference in tracker** — link the brainstorm findings in the tracker issue description
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: 'Generate a
|
|
2
|
+
description: 'Generate a .convoy.yml spec file for autonomous convoy execution based on a high-level goal.'
|
|
3
3
|
agent: 'Team Lead (OpenCastle)'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<!-- ⚠️ This file is managed by OpenCastle. Edits will be overwritten on update. Customize in the .github/customizations/ directory instead. -->
|
|
7
7
|
|
|
8
|
-
# Generate
|
|
8
|
+
# Generate Convoy Spec
|
|
9
9
|
|
|
10
|
-
You are the Team Lead. The user wants to run `opencastle run` to execute a batch of tasks autonomously
|
|
10
|
+
You are the Team Lead. The user wants to run `opencastle run` to execute a batch of tasks autonomously via the convoy engine. Your job is to produce a valid `.convoy.yml` file they can feed to the CLI. Derive a short, descriptive, kebab-case filename from the user's goal (2–4 words max) and use it as the filename — for example `auth-refactor.convoy.yml` or `add-search.convoy.yml`. Always use the `.convoy.yml` extension.
|
|
11
11
|
|
|
12
12
|
## User Goal
|
|
13
13
|
|
|
@@ -174,4 +174,4 @@ Also provide:
|
|
|
174
174
|
2. An **estimated total duration** (sum of timeouts on the critical path).
|
|
175
175
|
3. A `--dry-run` command they can use to validate: `npx opencastle run --file <feature-name>.convoy.yml --dry-run`
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
|
|
@@ -45,10 +45,34 @@ Every subtask must be tracked. **No issue = no implementation.** This step produ
|
|
|
45
45
|
- **Acceptance criteria:** Verifiable checklist
|
|
46
46
|
- **Dependencies:** Links to prerequisite issues
|
|
47
47
|
5. **Link to roadmap** — Reference the roadmap section in the issue description so context is never lost
|
|
48
|
-
6. **Verify issues exist** — List all created issue IDs. If count is 0, do NOT proceed to Step
|
|
48
|
+
6. **Verify issues exist** — List all created issue IDs. If count is 0, do NOT proceed to Step 2.5
|
|
49
|
+
|
|
50
|
+
### 2.5 Choose Execution Path (BLOCKING — decides how Step 3 proceeds)
|
|
51
|
+
|
|
52
|
+
With the full task list in hand, decide the execution mechanism:
|
|
53
|
+
|
|
54
|
+
| Condition | Execution path |
|
|
55
|
+
|-----------|----------------|
|
|
56
|
+
| 1–2 subtasks | **Direct delegation** — delegate to sub-agents as today (proceed to Step 3 as-is) |
|
|
57
|
+
| 3+ subtasks | **Convoy execution** — generate a `.convoy.yml` spec using the `generate-convoy` prompt, then hand it to the user |
|
|
58
|
+
|
|
59
|
+
#### Direct delegation (1–2 subtasks)
|
|
60
|
+
|
|
61
|
+
Proceed with the normal Step 3 delegation workflow. Sub-agents handle each task inline.
|
|
62
|
+
|
|
63
|
+
#### Convoy execution (3+ subtasks)
|
|
64
|
+
|
|
65
|
+
1. **Generate the spec** — use the `generate-convoy` prompt with the decomposed task list as context. The spec IS the implementation plan — no manual per-task delegation is needed.
|
|
66
|
+
2. **Hand the spec to the user** — tell them to run: `npx opencastle run -f <name>.convoy.yml`
|
|
67
|
+
3. **The convoy engine handles** isolated git worktrees, parallel execution, merge queue ordering, and crash recovery automatically.
|
|
68
|
+
4. **After convoy completes** — proceed to Step 4 (validation) and Step 5 (delivery/PR). The convoy engine will have created its own commits on the configured branch.
|
|
69
|
+
|
|
70
|
+
> **Why convoy for 3+ tasks?** Parallel worktree isolation prevents file conflicts. The merge queue ensures safe ordering. Crash recovery means a failing task doesn't block others. Manual delegation of 3+ parallel tasks risks conflicts and is harder to monitor.
|
|
49
71
|
|
|
50
72
|
### 3. Implementation Rules
|
|
51
73
|
|
|
74
|
+
> **For convoy execution (3+ subtasks):** The convoy spec file IS the implementation plan — skip the manual delegation workflow below and jump to Step 4 after the user runs the convoy. The convoy engine delegates tasks internally using the agents and prompts defined in the spec.
|
|
75
|
+
|
|
52
76
|
#### Issue Traceability
|
|
53
77
|
|
|
54
78
|
- **Pass issue ID to every agent** — When delegating a subtask (sub-agent or background), include the tracker issue ID and title in the prompt so the agent knows which tracked task it is completing
|
|
@@ -93,6 +117,8 @@ Every subtask must pass ALL gates before being marked Done:
|
|
|
93
117
|
|
|
94
118
|
Follow the **Delivery Outcome** defined in the **git-workflow** skill — commit, push, open PR (not merged), and link to the tracker.
|
|
95
119
|
|
|
120
|
+
> **For convoy execution:** The convoy engine creates commits on the configured `branch` directly. After validation passes, open the PR from that branch. No additional commits from the Team Lead are needed unless gates failed and required manual fixes.
|
|
121
|
+
|
|
96
122
|
### 6. Documentation & Traceability
|
|
97
123
|
|
|
98
124
|
Keep documentation current so future sessions have full context:
|
|
@@ -127,6 +127,8 @@ Stop the follow-up workflow and switch to a full roadmap task if:
|
|
|
127
127
|
|
|
128
128
|
When escalating, explain to the user what you found and why it needs proper tracking.
|
|
129
129
|
|
|
130
|
+
- **Multi-task escalation** — If the refinement decomposes into 3+ subtasks, switch to `implement-feature` (which will use convoy execution for multi-task work)
|
|
131
|
+
|
|
130
132
|
### 8. Completion
|
|
131
133
|
|
|
132
134
|
The follow-up is complete when:
|