@yemi33/minions 0.1.2423 → 0.1.2425
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/bin/install-internal-minions.js +1209 -0
- package/bin/minions.js +75 -9
- package/dashboard/docs/typography.md +27 -12
- package/dashboard/js/command-center.js +13 -11
- package/dashboard/js/live-stream.js +1 -1
- package/dashboard/js/memory-search.js +388 -43
- package/dashboard/js/modal-qa.js +5 -5
- package/dashboard/js/qa.js +39 -26
- package/dashboard/js/refresh.js +37 -18
- package/dashboard/js/render-agents.js +14 -18
- package/dashboard/js/render-dispatch.js +14 -74
- package/dashboard/js/render-inbox.js +3 -3
- package/dashboard/js/render-meetings.js +8 -8
- package/dashboard/js/render-other.js +45 -20
- package/dashboard/js/render-pipelines.js +6 -6
- package/dashboard/js/render-plans.js +14 -14
- package/dashboard/js/render-prd.js +46 -46
- package/dashboard/js/render-prs.js +9 -65
- package/dashboard/js/render-schedules.js +5 -5
- package/dashboard/js/render-skills.js +6 -6
- package/dashboard/js/render-utils.js +4 -4
- package/dashboard/js/render-watches.js +4 -16
- package/dashboard/js/render-work-items.js +95 -107
- package/dashboard/js/settings.js +175 -74
- package/dashboard/layout.html +3 -2
- package/dashboard/pages/inbox.html +1 -1
- package/dashboard/pages/work.html +1 -1
- package/dashboard/shared/model-display.js +9 -0
- package/dashboard/shared/watches-source.js +43 -0
- package/dashboard/shared/welcome-popup.js +259 -0
- package/dashboard/slim/body.html +6 -6
- package/dashboard/slim/js/chat.js +4 -2
- package/dashboard/slim/js/history.js +10 -3
- package/dashboard/slim/js/modals-tiles.js +85 -16
- package/dashboard/slim/js/status.js +12 -16
- package/dashboard/slim/styles.css +24 -4
- package/dashboard/styles.css +62 -27
- package/dashboard-build.js +8 -2
- package/dashboard.js +3854 -2514
- package/docs/README.md +6 -4
- package/docs/api-errors.md +144 -0
- package/docs/auto-discovery.md +83 -61
- package/docs/capture-demos.js +173 -30
- package/docs/command-center.md +5 -1
- package/docs/completion-reports.md +127 -11
- package/docs/constants.md +15 -3
- package/docs/constellation-style-telemetry.md +2 -2
- package/docs/copilot-cli-schema.md +32 -22
- package/docs/cross-repo-plans.md +30 -30
- package/docs/demo/memory-system.html +1 -1
- package/docs/deprecated.json +132 -18
- package/docs/diagnostics-memory.md +8 -4
- package/docs/human-vs-automated.md +2 -2
- package/docs/internal-install.md +190 -0
- package/docs/kb-pr3223-cascade-archiving.md +16 -0
- package/docs/kb-pr696-merge-conflict-docs.md +23 -0
- package/docs/kb-sweep.md +2 -2
- package/docs/keep-processes.md +8 -1
- package/docs/live-checkout-mode.md +12 -7
- package/docs/managed-spawn.md +21 -3
- package/docs/named-agents.md +3 -2
- package/docs/plan-lifecycle.md +32 -32
- package/docs/pr-auto-fix-dispatch.md +3 -3
- package/docs/pr-review-fix-loop.md +6 -6
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
- package/docs/qa-runbook-lifecycle.md +67 -51
- package/docs/qa-runbooks.md +2 -2
- package/docs/runtime-adapters.md +101 -50
- package/docs/security.md +28 -0
- package/docs/self-improvement.md +47 -13
- package/docs/skills.md +29 -0
- package/docs/slim-ux/concepts.md +2 -2
- package/docs/specs/agent-configurability.md +43 -41
- package/docs/specs/agent-rename.md +13 -13
- package/docs/team-memory.md +283 -14
- package/docs/tutorials/03-plan-driven-feature.md +3 -3
- package/docs/tutorials/04-runtimes-and-harness.md +18 -21
- package/docs/tutorials/08-operations-and-recovery.md +3 -2
- package/docs/tutorials/README.md +4 -3
- package/docs/visual-evidence-ci.md +103 -0
- package/docs/watches.md +35 -30
- package/docs/worktree-lifecycle.md +151 -13
- package/engine/acp-transport.js +1 -0
- package/engine/ado.js +80 -33
- package/engine/agent-api-validation.js +571 -0
- package/engine/agent-worker-pool.js +7 -6
- package/engine/api-contracts/agent-content.js +761 -0
- package/engine/api-contracts/cc-ops.js +219 -0
- package/engine/api-contracts/config-runtime.js +419 -0
- package/engine/api-contracts/core.js +138 -0
- package/engine/api-contracts/index.js +506 -0
- package/engine/api-contracts/orchestration.js +602 -0
- package/engine/api-contracts/pull-requests.js +323 -0
- package/engine/api-contracts/qa-process.js +764 -0
- package/engine/api-contracts/work-plan-prd.js +494 -0
- package/engine/api-validation.js +543 -0
- package/engine/cc-api-validation.js +586 -0
- package/engine/cc-worker-pool.js +9 -1
- package/engine/cleanup.js +393 -271
- package/engine/cli.js +65 -7
- package/engine/comment-classifier.js +70 -5
- package/engine/consolidation.js +123 -5
- package/engine/content-api-validation.js +606 -0
- package/engine/create-pr-worktree.js +22 -1
- package/engine/db/migrations/027-review-learning-lifecycle.js +162 -0
- package/engine/dispatch.js +33 -0
- package/engine/distribution.js +189 -0
- package/engine/execution-model.js +59 -0
- package/engine/features.js +12 -0
- package/engine/github.js +67 -25
- package/engine/harness-context.js +313 -0
- package/engine/inbox-store.js +1 -1
- package/engine/kb-sweep.js +161 -38
- package/engine/keep-process-sweep.js +43 -3
- package/engine/lifecycle.js +1066 -165
- package/engine/llm.js +4 -0
- package/engine/managed-spawn.js +463 -48
- package/engine/meeting.js +197 -34
- package/engine/memory-retrieval.js +280 -0
- package/engine/memory-store.js +821 -38
- package/engine/model-discovery.js +17 -3
- package/engine/pipeline.js +802 -14
- package/engine/plan-prd-validation.js +745 -0
- package/engine/playbook.js +206 -17
- package/engine/pooled-agent-process.js +6 -1
- package/engine/pr-action.js +1 -1
- package/engine/pr-issue-validation.js +653 -0
- package/engine/pr-resolve.js +75 -13
- package/engine/prd-store.js +39 -6
- package/engine/preflight.js +7 -2
- package/engine/process-utils.js +79 -31
- package/engine/projects.js +198 -12
- package/engine/promotion.js +371 -0
- package/engine/qa-from-prd.js +3 -0
- package/engine/qa-process-validation.js +572 -0
- package/engine/qa-runbooks.js +41 -37
- package/engine/qa-runners/maestro.js +11 -7
- package/engine/qa-runners/playwright.js +37 -14
- package/engine/qa-runners.js +9 -10
- package/engine/qa-runs.js +124 -11
- package/engine/qa-sessions.js +139 -40
- package/engine/quarantine-refs.js +81 -11
- package/engine/queries.js +163 -9
- package/engine/review-learning-backfill.js +482 -0
- package/engine/review-learning.js +1236 -0
- package/engine/runtimes/claude.js +60 -2
- package/engine/runtimes/codex.js +57 -16
- package/engine/runtimes/contract.js +21 -0
- package/engine/runtimes/copilot.js +51 -3
- package/engine/runtimes/index.js +1 -0
- package/engine/scheduler.js +201 -32
- package/engine/settings-validation.js +909 -0
- package/engine/shared.js +705 -208
- package/engine/spawn-agent.js +48 -21
- package/engine/steering-constraints.js +31 -0
- package/engine/steering-store.js +10 -2
- package/engine/steering.js +14 -2
- package/engine/timeout.js +404 -111
- package/engine/untrusted-fence.js +24 -10
- package/engine/watch-actions.js +7 -2
- package/engine/watches.js +353 -51
- package/engine/work-item-validation.js +561 -0
- package/engine/work-items-store.js +61 -1
- package/engine/worktree-gc.js +656 -37
- package/engine/worktree-preflight.js +154 -0
- package/engine.js +2068 -342
- package/package.json +1 -1
- package/playbooks/_pr-description-audit.md +88 -41
- package/playbooks/build-fix-complex.md +3 -3
- package/playbooks/docs.md +2 -2
- package/playbooks/fix.md +18 -18
- package/playbooks/implement.md +18 -18
- package/playbooks/qa-session-draft.md +10 -12
- package/playbooks/qa-session-execute.md +16 -12
- package/playbooks/qa-validate.md +8 -7
- package/playbooks/shared-rules.md +45 -0
- package/playbooks/test.md +4 -0
- package/prompts/cc-system.md +1 -1
- package/skills/check-self-authored-review-comment/SKILL.md +2 -2
|
@@ -350,11 +350,11 @@ function renderPipelines(pipelines, opts) {
|
|
|
350
350
|
'</div>' +
|
|
351
351
|
'<div class="pipeline-card-badges">' +
|
|
352
352
|
'<span style="color:' + statusColor + ';font-size:var(--text-base);font-weight:600">' + escHtml(statusLabel) + '</span>' +
|
|
353
|
-
(p.stopWhen ? '<span style="font-size:var(--text-
|
|
354
|
-
(p.enabled === false ? '<span style="font-size:var(--text-
|
|
353
|
+
(p.stopWhen ? '<span style="font-size:var(--text-sm);color:var(--yellow)" title="Auto-stops when condition met: ' + escHtml(typeof p.stopWhen === 'string' ? p.stopWhen : (p.stopWhen.check || 'condition')) + '">STOP-WHEN</span>' : '') +
|
|
354
|
+
(p.enabled === false ? '<span style="font-size:var(--text-sm);color:var(--red)"' + (p._stopReason ? ' title="' + escHtml(p._stopReason) + '"' : '') + '>' + (p._stoppedBy ? 'AUTO-STOPPED' : 'DISABLED') + '</span>' : '') +
|
|
355
355
|
(p.enabled !== false
|
|
356
356
|
? (activeRun
|
|
357
|
-
? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
357
|
+
? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--yellow);border-color:var(--yellow)" onclick="event.stopPropagation();_retriggerPipeline(\'' + escHtml(p.id) + '\',this)">Retrigger</button>'
|
|
358
358
|
: '<button class="btn-action btn-action-md" onclick="event.stopPropagation();_triggerPipeline(\'' + escHtml(p.id) + '\',this)">Run Now</button>')
|
|
359
359
|
: '') +
|
|
360
360
|
'</div>' +
|
|
@@ -411,11 +411,11 @@ function openPipelineDetail(id) {
|
|
|
411
411
|
(activeRun ? '<button class="btn-destructive btn-destructive-md" onclick="_abortPipeline(\'' + escHtml(id) + '\',this)">Abort</button>' : '') +
|
|
412
412
|
(p.enabled !== false
|
|
413
413
|
? (activeRun
|
|
414
|
-
? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
414
|
+
? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--yellow);border-color:var(--yellow)" onclick="_retriggerPipeline(\'' + escHtml(id) + '\',this)">Retrigger</button>'
|
|
415
415
|
: '<button class="btn-action btn-action-md" onclick="_triggerPipeline(\'' + escHtml(id) + '\',this)">Run Now</button>')
|
|
416
416
|
: '') +
|
|
417
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
418
|
-
'<button class="pr-pager-btn' + (_pipelineToggleInFlight.has(id) ? ' disabled' : '') + '" style="font-size:var(--text-
|
|
417
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--blue);border-color:var(--blue)" onclick="openEditPipelineModal(\'' + escHtml(id) + '\')">Edit</button>' +
|
|
418
|
+
'<button class="pr-pager-btn' + (_pipelineToggleInFlight.has(id) ? ' disabled' : '') + '" style="font-size:var(--text-sm);padding:2px 8px" onclick="_togglePipelineEnabled(\'' + escHtml(id) + '\',' + !p.enabled + ',this)">' + (p.enabled !== false ? 'Disable' : 'Enable') + '</button>' +
|
|
419
419
|
'<button class="btn-destructive btn-destructive-md" onclick="_deletePipelineConfirm(\'' + escHtml(id) + '\')">Delete</button>' +
|
|
420
420
|
'</div>' +
|
|
421
421
|
'</div>';
|
|
@@ -343,7 +343,7 @@ function renderPlans(plans) {
|
|
|
343
343
|
actions = '<div class="plan-card-meta" style="margin-top:6px;color:var(--purple,#a855f7)">Revision in progress: ' + escapeHtml((p.revisionFeedback || '').slice(0, 100)) + '</div>';
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
-
const executeBtn = isDraft && (effectiveStatus === 'active' || effectiveStatus === 'draft') && !isArchived && !prdFile ? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
346
|
+
const executeBtn = isDraft && (effectiveStatus === 'active' || effectiveStatus === 'draft') && !isArchived && !prdFile ? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--green);font-weight:600" ' +
|
|
347
347
|
'onclick="event.stopPropagation();planExecute(\'' + escapeHtml(p.file) + '\',\'' + escapeHtml(p.project) + '\',this)">Execute</button>' : '';
|
|
348
348
|
const showPause = effectiveStatus === 'dispatched' && prdFile && !isArchived;
|
|
349
349
|
// Resume pill not needed — paused state is handled by the actions block above
|
|
@@ -351,26 +351,26 @@ function renderPlans(plans) {
|
|
|
351
351
|
const verifyWis = allWi.filter(w => w.itemType === 'verify' && w.sourcePlan === prdFile);
|
|
352
352
|
const hasVerifyWi = verifyWis.length > 0;
|
|
353
353
|
const showVerify = effectiveStatus === 'completed' && prdFile && !isArchived && !hasVerifyWi;
|
|
354
|
-
const pauseBtn = showPause ? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
354
|
+
const pauseBtn = showPause ? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--yellow)" ' +
|
|
355
355
|
'onclick="event.stopPropagation();planPause(\'' + escapeHtml(prdFile) + '\',this)">Pause</button>' : '';
|
|
356
356
|
const resumeBtn = showResume
|
|
357
|
-
? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
357
|
+
? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--green)" ' +
|
|
358
358
|
'onclick="event.stopPropagation();planApprove(\'' + escapeHtml(prdFile) + '\',this)">Resume</button>'
|
|
359
359
|
: '';
|
|
360
|
-
const verifyBtn = showVerify ? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
360
|
+
const verifyBtn = showVerify ? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--green)" ' +
|
|
361
361
|
'onclick="event.stopPropagation();triggerVerify(\'' + escapeHtml(prdFile) + '\',this)">Verify</button>' : '';
|
|
362
362
|
const showArchive = !isArchived;
|
|
363
363
|
const archiveFile = prdFile || p.file;
|
|
364
364
|
const archiveReady = p.archiveReady && !isArchived;
|
|
365
|
-
const archiveBtn = showArchive ? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
365
|
+
const archiveBtn = showArchive ? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px' +
|
|
366
366
|
(archiveReady ? ';color:var(--green);font-weight:600;border:1px solid var(--green)' : '') + '" ' +
|
|
367
367
|
'onclick="event.stopPropagation();planArchive(\'' + escapeHtml(archiveFile) + '\',this)">' +
|
|
368
368
|
(archiveReady ? '✓ Archive' : 'Archive') + '</button>' : '';
|
|
369
|
-
const archiveReadyBadge = archiveReady ? '<span style="font-size:var(--text-
|
|
370
|
-
const deleteBtn = !isArchived ? '<button class="pr-pager-btn" style="font-size:var(--text-
|
|
369
|
+
const archiveReadyBadge = archiveReady ? '<span style="font-size:var(--text-sm);font-weight:600;padding:1px 6px;border-radius:3px;background:rgba(63,185,80,0.15);color:var(--green);vertical-align:middle" title="Verification passed — ready to archive">Ready to archive</span>' : '';
|
|
370
|
+
const deleteBtn = !isArchived ? '<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--red)" ' +
|
|
371
371
|
'onclick="event.stopPropagation();planDelete(\'' + escapeHtml(p.file) + '\')">Delete</button>' : '';
|
|
372
372
|
|
|
373
|
-
const versionBadge = p.version ? ' <span style="font-size:var(--text-
|
|
373
|
+
const versionBadge = p.version ? ' <span style="font-size:var(--text-sm);font-weight:700;padding:1px 5px;border-radius:3px;background:rgba(56,139,253,0.15);color:var(--blue);vertical-align:middle">v' + p.version + '</span>' : '';
|
|
374
374
|
const statusColors = { 'completed': 'var(--green)', 'dispatched': 'var(--blue)', 'converting': 'var(--yellow)', 'paused': 'var(--muted)', 'awaiting-approval': 'var(--yellow)', 'approved': 'var(--purple,#a855f7)', 'rejected': 'var(--red)', 'has-failures': 'var(--red)', 'revision-requested': 'var(--purple,#a855f7)', 'active': 'var(--muted)', 'draft': 'var(--muted)' };
|
|
375
375
|
const cardClass = effectiveStatus === 'dispatched' || effectiveStatus === 'converting' ? 'working' : effectiveStatus === 'awaiting-approval' || effectiveStatus === 'paused' ? 'awaiting' : effectiveStatus;
|
|
376
376
|
// P-e8d49105 — cross-repo plans surface every touched project as its
|
|
@@ -399,7 +399,7 @@ function renderPlans(plans) {
|
|
|
399
399
|
const color = isComplete ? 'var(--green)' : 'var(--muted)';
|
|
400
400
|
return '<span title="' + escapeHtml(pn) + ': ' + complete + '/' + total +
|
|
401
401
|
(isComplete ? ' complete' : ' in progress') + '" ' +
|
|
402
|
-
'style="font-size:var(--text-
|
|
402
|
+
'style="font-size:var(--text-sm);font-weight:600;padding:1px 6px;border-radius:3px;background:rgba(110,118,129,0.15);color:' + color + '">' +
|
|
403
403
|
escapeHtml(pn) + ' ' + complete + '/' + total + tail + '</span>';
|
|
404
404
|
}).join(' ')
|
|
405
405
|
: '';
|
|
@@ -407,7 +407,7 @@ function renderPlans(plans) {
|
|
|
407
407
|
'<div class="plan-card-header">' +
|
|
408
408
|
'<div><div class="plan-card-title">' + escapeHtml(p.summary || p.file) + versionBadge + '</div>' +
|
|
409
409
|
'<div class="plan-card-meta">' +
|
|
410
|
-
'<span style="font-size:var(--text-
|
|
410
|
+
'<span style="font-size:var(--text-sm);font-weight:600;padding:1px 8px;border-radius:10px;background:color-mix(in srgb, ' + (statusColors[effectiveStatus] || 'var(--muted)') + ' 15%, transparent);color:' + (statusColors[effectiveStatus] || 'var(--muted)') + ';border:1px solid color-mix(in srgb, ' + (statusColors[effectiveStatus] || 'var(--muted)') + ' 40%, transparent)">' + escapeHtml(label) + '</span>' +
|
|
411
411
|
projectMeta +
|
|
412
412
|
'<span>' + p.itemCount + ' items</span>' +
|
|
413
413
|
perProjectPills +
|
|
@@ -1074,13 +1074,13 @@ function _renderVerifyBadge(verifyWi, opts) {
|
|
|
1074
1074
|
const planFile = verifyWi.sourcePlan || '';
|
|
1075
1075
|
const planSlug = planFile.replace('.json', '');
|
|
1076
1076
|
const verifyPr = allPrs.find(pr => (pr.prdItems || []).includes(verifyWi.id) || (pr.branch && pr.branch.includes(planSlug) && (pr.title || '').includes('[E2E]')));
|
|
1077
|
-
const prLink = verifyPr?.url ? ' <a href="' + escapeHtml(verifyPr.url) + '" target="_blank" onclick="event.stopPropagation()" style="color:var(--blue);text-decoration:underline;font-size:var(--text-
|
|
1077
|
+
const prLink = verifyPr?.url ? ' <a href="' + escapeHtml(verifyPr.url) + '" target="_blank" onclick="event.stopPropagation()" style="color:var(--blue);text-decoration:underline;font-size:var(--text-sm)">' + escapeHtml(verifyPr.id || 'E2E PR') + '</a>' : '';
|
|
1078
1078
|
// Testing guide. verifyGuides moved to /api/verify-guides
|
|
1079
1079
|
// (window._lastVerifyGuides set by refresh.js).
|
|
1080
1080
|
const guides = window._lastVerifyGuides || [];
|
|
1081
1081
|
const guide = guides.find(g => g.planFile === planFile);
|
|
1082
|
-
const guideLink = guide ? ' <span onclick="event.stopPropagation();openVerifyGuide(\'' + escapeHtml(guide.file) + '\')" style="color:var(--green);cursor:pointer;text-decoration:underline;font-size:var(--text-
|
|
1083
|
-
return '<span style="font-size:var(--text-
|
|
1082
|
+
const guideLink = guide ? ' <span onclick="event.stopPropagation();openVerifyGuide(\'' + escapeHtml(guide.file) + '\')" style="color:var(--green);cursor:pointer;text-decoration:underline;font-size:var(--text-sm)">Testing Guide</span>' : '';
|
|
1083
|
+
return '<span style="font-size:var(--text-sm);font-weight:600;color:' + color + ';padding:0 4px">' + label + '</span>' + prLink + guideLink;
|
|
1084
1084
|
}
|
|
1085
1085
|
|
|
1086
1086
|
async function openVerifyGuide(file) {
|
|
@@ -1088,7 +1088,7 @@ async function openVerifyGuide(file) {
|
|
|
1088
1088
|
const normalizedFile = normalizePlanFile(file);
|
|
1089
1089
|
const content = await fetch('/api/plans/' + encodeURIComponent(normalizedFile)).then(r => r.text());
|
|
1090
1090
|
document.getElementById('modal-title').innerHTML = 'Manual Testing Guide' +
|
|
1091
|
-
' <button class="pr-pager-btn" style="font-size:var(--text-
|
|
1091
|
+
' <button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;margin-left:8px;vertical-align:middle" onclick="openArchivedPrdModal()">Back</button>';
|
|
1092
1092
|
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd() escapes all user-controlled fields before assembling HTML (see dashboard/js/utils.js)
|
|
1093
1093
|
document.getElementById('modal-body').innerHTML = renderMd(content);
|
|
1094
1094
|
_modalDocContext = { title: 'Manual Testing Guide', content, selection: '' };
|
|
@@ -110,12 +110,12 @@ function renderPrd(prd, prog) {
|
|
|
110
110
|
const hasVerifyWi = allWi.some(w => w.itemType === 'verify' && w.sourcePlan === prdFile);
|
|
111
111
|
actions = (hasVerifyWi ? '' : ' <button class="btn-action" style="margin-left:4px" onclick="triggerVerify(\'' + escHtml(prdFile) + '\',this)">Verify</button>') +
|
|
112
112
|
' <button class="btn-action" style="margin-left:4px" onclick="qaThisPrd(\'' + escHtml(prdFile) + '\',this)">QA this PRD</button>' +
|
|
113
|
-
' <button class="pr-pager-btn" style="font-size:var(--text-
|
|
113
|
+
' <button class="pr-pager-btn" style="font-size:var(--text-sm);padding:1px 6px;margin-left:4px" onclick="planArchive(\'' + escHtml(prdFile) + '\',this)">Archive</button>';
|
|
114
114
|
} else if (effectiveStatus === 'dispatched') {
|
|
115
|
-
actions = ' <button class="pr-pager-btn" style="font-size:var(--text-
|
|
115
|
+
actions = ' <button class="pr-pager-btn" style="font-size:var(--text-sm);padding:1px 6px;color:var(--yellow);border-color:var(--yellow);margin-left:4px" onclick="planPause(\'' + escHtml(prdFile) + '\',this)">Pause</button>';
|
|
116
116
|
} else if (effectiveStatus === 'paused') {
|
|
117
117
|
actions = ' <button class="btn-action" style="margin-left:4px" onclick="planApprove(\'' + escHtml(prdFile) + '\',this)">Resume</button>' +
|
|
118
|
-
' <button class="pr-pager-btn" style="font-size:var(--text-
|
|
118
|
+
' <button class="pr-pager-btn" style="font-size:var(--text-sm);padding:1px 6px;margin-left:4px" onclick="planArchive(\'' + escHtml(prdFile) + '\',this)">Archive</button>';
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: PRD file; status label/color and age text are derived UI state)
|
|
@@ -242,7 +242,7 @@ function renderPrdProgress(prog) {
|
|
|
242
242
|
const labels = { 'done': 'DONE', 'in-progress': 'WIP', 'failed': 'FAIL', 'paused': 'PAUSED', 'missing': '\u2014', 'updated': 'REDO' };
|
|
243
243
|
const style = styles[s] || 'background:var(--surface);color:var(--muted)';
|
|
244
244
|
const label = labels[s] || '—';
|
|
245
|
-
return '<span style="font-size:var(--text-
|
|
245
|
+
return '<span style="font-size:var(--text-sm);font-weight:700;padding:2px 6px;border-radius:3px;letter-spacing:0.5px;white-space:nowrap;' + style + '">' + label + '</span>';
|
|
246
246
|
};
|
|
247
247
|
|
|
248
248
|
// Build work item lookup by actual work item ID.
|
|
@@ -276,13 +276,13 @@ function renderPrdProgress(prog) {
|
|
|
276
276
|
const wi = wiById[workItemId] || wiById[i.id];
|
|
277
277
|
const wiAgent = wi?.dispatched_to ? (agentData.find(a => a.id === wi.dispatched_to) || {}) : null;
|
|
278
278
|
const wiLabel = _renderPrdWorkItemIdBadge(wi ? wi.id : workItemId, i.id);
|
|
279
|
-
const agentLabel = wiAgent ? '<span style="font-size:var(--text-
|
|
279
|
+
const agentLabel = wiAgent ? '<span style="font-size:var(--text-sm);color:var(--muted)" title="' + escHtml(wiAgent.name || wi.dispatched_to) + '">' +
|
|
280
280
|
(wiAgent.emoji || '') + ' ' + escHtml(wiAgent.name || wi.dispatched_to) + '</span>' : '';
|
|
281
281
|
|
|
282
282
|
// Branch label — show the target branch for this item
|
|
283
283
|
const wiBranch = wi ? (wi.branch || wi.featureBranch || (wi._artifacts && wi._artifacts.branch) || '') : '';
|
|
284
284
|
const branchLabel = wiBranch
|
|
285
|
-
? '<span style="font-size:var(--text-
|
|
285
|
+
? '<span style="font-size:var(--text-sm);color:var(--muted);background:var(--surface);padding:1px 5px;border-radius:3px;border:1px solid var(--border);font-family:monospace;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block;vertical-align:middle" title="Branch: ' + escHtml(wiBranch) + '">🌿 ' + escHtml(wiBranch) + '</span>'
|
|
286
286
|
: '';
|
|
287
287
|
|
|
288
288
|
// Requeue button for failed items or PRD items with no work item (orphaned/deleted)
|
|
@@ -291,19 +291,19 @@ function renderPrdProgress(prog) {
|
|
|
291
291
|
const requeueState = getPrdRequeueState(wi ? wi.id : i.id);
|
|
292
292
|
let requeueBtn = '';
|
|
293
293
|
if (requeueState && requeueState.status === 'pending') {
|
|
294
|
-
requeueBtn = '<span style="color:var(--yellow);cursor:wait;font-size:var(--text-
|
|
294
|
+
requeueBtn = '<span style="color:var(--yellow);cursor:wait;font-size:var(--text-sm);padding:1px 5px;background:rgba(210,153,34,0.1);border:1px solid rgba(210,153,34,0.35);border-radius:3px" title="Retry request in progress">requeuing…</span>';
|
|
295
295
|
} else if (requeueState && requeueState.status === 'queued') {
|
|
296
|
-
requeueBtn = '<span style="color:var(--green);cursor:default;font-size:var(--text-
|
|
296
|
+
requeueBtn = '<span style="color:var(--green);cursor:default;font-size:var(--text-sm);padding:1px 5px;background:rgba(63,185,80,0.1);border:1px solid rgba(63,185,80,0.35);border-radius:3px" title="Successfully requeued">requeued</span>';
|
|
297
297
|
} else if (requeueState && requeueState.status === 'error') {
|
|
298
|
-
requeueBtn = '<span style="color:var(--red);cursor:default;font-size:var(--text-
|
|
298
|
+
requeueBtn = '<span style="color:var(--red);cursor:default;font-size:var(--text-sm);padding:1px 5px;background:rgba(248,81,73,0.1);border:1px solid rgba(248,81,73,0.35);border-radius:3px" title="' + escHtml(requeueState.message || 'Retry failed') + '">retry failed</span>';
|
|
299
299
|
} else if (canRequeue) {
|
|
300
|
-
requeueBtn = '<span onclick="event.stopPropagation();prdItemRequeue(\'' + escHtml(wi ? wi.id : i.id) + '\',\'' + escHtml(wi ? (wi._source || '') : '') + '\',\'' + escHtml(i.source || '') + '\')" style="color:var(--green);cursor:pointer;font-size:var(--text-
|
|
300
|
+
requeueBtn = '<span onclick="event.stopPropagation();prdItemRequeue(\'' + escHtml(wi ? wi.id : i.id) + '\',\'' + escHtml(wi ? (wi._source || '') : '') + '\',\'' + escHtml(i.source || '') + '\')" style="color:var(--green);cursor:pointer;font-size:var(--text-sm);padding:1px 5px;background:rgba(63,185,80,0.1);border:1px solid rgba(63,185,80,0.3);border-radius:3px" title="Requeue this work item">retry</span>';
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
// Re-open button for done items — sets PRD item to "updated" so engine re-dispatches
|
|
304
304
|
const isDone = i.status === 'done' || (wi && wi.status === 'done');
|
|
305
305
|
const reopenBtn = isDone
|
|
306
|
-
? '<span onclick="event.stopPropagation();prdItemReopen(\'' + escHtml(i.source || '') + '\',\'' + escHtml(i.id) + '\')" style="color:var(--blue);cursor:pointer;font-size:var(--text-
|
|
306
|
+
? '<span onclick="event.stopPropagation();prdItemReopen(\'' + escHtml(i.source || '') + '\',\'' + escHtml(i.id) + '\')" style="color:var(--blue);cursor:pointer;font-size:var(--text-sm);padding:1px 5px;background:rgba(56,139,253,0.1);border:1px solid rgba(56,139,253,0.3);border-radius:3px" title="Re-open: set to updated so engine re-dispatches on existing branch">re-open</span>'
|
|
307
307
|
: '';
|
|
308
308
|
|
|
309
309
|
// P-79b47b0c — replace the bare prd-item-id span with a renderArtifactLink
|
|
@@ -339,9 +339,9 @@ function renderPrdProgress(prog) {
|
|
|
339
339
|
const childAgent = c.dispatched_to ? (agentData.find(a => a.id === c.dispatched_to) || {}) : null;
|
|
340
340
|
return '<div style="font-size:var(--text-sm);display:flex;align-items:center;gap:6px;color:var(--text);padding:2px 6px;background:var(--surface);border-radius:4px;border:1px solid var(--border)">' +
|
|
341
341
|
statusBadge(c.status) +
|
|
342
|
-
'<span style="color:var(--muted);font-size:var(--text-
|
|
342
|
+
'<span style="color:var(--muted);font-size:var(--text-sm)">' + escHtml(c.id) + '</span>' +
|
|
343
343
|
'<span style="flex:1">' + escHtml((c.title || '').replace('Implement: ', '').slice(0, 60)) + '</span>' +
|
|
344
|
-
(childAgent ? '<span style="font-size:var(--text-
|
|
344
|
+
(childAgent ? '<span style="font-size:var(--text-sm);color:var(--muted)">' + (childAgent.emoji || '') + ' ' + escHtml(childAgent.name || c.dispatched_to) + '</span>' : '') +
|
|
345
345
|
'</div>';
|
|
346
346
|
}).join('') +
|
|
347
347
|
'</div>';
|
|
@@ -404,24 +404,24 @@ function renderPrdProgress(prog) {
|
|
|
404
404
|
// Hide regular action buttons when stale banner is showing — stale banner has its own actions
|
|
405
405
|
const isStale = showStale;
|
|
406
406
|
const pauseResumeBtn = isStale ? '' : isAwaitingApproval
|
|
407
|
-
? '<span onclick="event.stopPropagation();planApprove(\'' + escHtml(g.file) + '\',this)" style="color:var(--green);cursor:pointer;font-size:var(--text-
|
|
407
|
+
? '<span onclick="event.stopPropagation();planApprove(\'' + escHtml(g.file) + '\',this)" style="color:var(--green);cursor:pointer;font-size:var(--text-sm);padding:1px 6px;background:rgba(63,185,80,0.1);border:1px solid rgba(63,185,80,0.3);border-radius:3px">Approve</span>'
|
|
408
408
|
: isPaused
|
|
409
|
-
? '<span onclick="event.stopPropagation();planApprove(\'' + escHtml(g.file) + '\',this)" style="color:var(--green);cursor:pointer;font-size:var(--text-
|
|
409
|
+
? '<span onclick="event.stopPropagation();planApprove(\'' + escHtml(g.file) + '\',this)" style="color:var(--green);cursor:pointer;font-size:var(--text-sm);padding:1px 6px;background:rgba(63,185,80,0.1);border:1px solid rgba(63,185,80,0.3);border-radius:3px">Resume</span>'
|
|
410
410
|
: isCompleted && !(window._lastWorkItems || []).some(w => w.itemType === 'verify' && w.sourcePlan === g.file)
|
|
411
|
-
? '<span onclick="event.stopPropagation();triggerVerify(\'' + escHtml(g.file) + '\',this)" style="color:var(--green);cursor:pointer;font-size:var(--text-
|
|
412
|
-
: isCompleted ? '' : '<span onclick="event.stopPropagation();planPause(\'' + escHtml(g.file) + '\',this)" style="color:var(--yellow);cursor:pointer;font-size:var(--text-
|
|
413
|
-
const archiveBtn = (!isStale && (isCompleted || isPaused)) ? '<span onclick="event.stopPropagation();planArchive(\'' + escHtml(g.file) + '\',this)" style="color:var(--muted);cursor:pointer;font-size:var(--text-
|
|
414
|
-
const deleteBtn = '<span onclick="event.stopPropagation();planDelete(\'' + escHtml(g.file) + '\')" style="color:var(--red);cursor:pointer;font-size:var(--text-
|
|
411
|
+
? '<span onclick="event.stopPropagation();triggerVerify(\'' + escHtml(g.file) + '\',this)" style="color:var(--green);cursor:pointer;font-size:var(--text-sm);padding:1px 6px;background:rgba(63,185,80,0.1);border:1px solid rgba(63,185,80,0.3);border-radius:3px">Verify</span>'
|
|
412
|
+
: isCompleted ? '' : '<span onclick="event.stopPropagation();planPause(\'' + escHtml(g.file) + '\',this)" style="color:var(--yellow);cursor:pointer;font-size:var(--text-sm);padding:1px 6px;background:rgba(210,153,34,0.1);border:1px solid rgba(210,153,34,0.3);border-radius:3px">Pause</span>';
|
|
413
|
+
const archiveBtn = (!isStale && (isCompleted || isPaused)) ? '<span onclick="event.stopPropagation();planArchive(\'' + escHtml(g.file) + '\',this)" style="color:var(--muted);cursor:pointer;font-size:var(--text-sm);padding:1px 6px;background:rgba(139,148,158,0.1);border:1px solid rgba(139,148,158,0.3);border-radius:3px">Archive</span>' : '';
|
|
414
|
+
const deleteBtn = '<span onclick="event.stopPropagation();planDelete(\'' + escHtml(g.file) + '\')" style="color:var(--red);cursor:pointer;font-size:var(--text-sm);padding:1px 6px;background:rgba(248,81,73,0.1);border:1px solid rgba(248,81,73,0.3);border-radius:3px">Delete</span>';
|
|
415
415
|
const sourcePlanLink = g.sourcePlan
|
|
416
|
-
? '<span onclick="event.stopPropagation();planView(\'' + escHtml(g.sourcePlan) + '\')" style="color:var(--blue);cursor:pointer;font-size:var(--text-
|
|
416
|
+
? '<span onclick="event.stopPropagation();planView(\'' + escHtml(g.sourcePlan) + '\')" style="color:var(--blue);cursor:pointer;font-size:var(--text-sm);padding:1px 6px;background:rgba(56,139,253,0.1);border:1px solid rgba(56,139,253,0.3);border-radius:3px" title="View source plan">📄 Plan</span>'
|
|
417
417
|
: '';
|
|
418
418
|
|
|
419
419
|
return '<div style="font-size:var(--text-base);font-weight:600;color:var(--blue);margin-bottom:4px;padding:6px 8px;background:var(--surface2);border-radius:4px;display:flex;align-items:center;gap:8px;flex-wrap:wrap">' +
|
|
420
420
|
(g._projects.length > 0 ? g._projects.map(function(p) { return '<span class="prd-project-badge">' + escHtml(p) + '</span>'; }).join(' ') : '') +
|
|
421
421
|
'<span style="color:var(--text)">' + escHtml(summary || g.file) + '</span>' +
|
|
422
422
|
(g.branchStrategy === 'shared-branch'
|
|
423
|
-
? '<span style="font-size:var(--text-
|
|
424
|
-
: '<span style="font-size:var(--text-
|
|
423
|
+
? '<span style="font-size:var(--text-sm);padding:1px 5px;border-radius:3px;background:rgba(210,153,34,0.15);color:var(--yellow);font-weight:400" title="All items commit to a single shared feature branch">🌳 shared branch</span>'
|
|
424
|
+
: '<span style="font-size:var(--text-sm);padding:1px 5px;border-radius:3px;background:rgba(56,139,253,0.12);color:var(--blue);font-weight:400" title="Each item gets its own branch (work/P-xxx) and PR">🔀 parallel branches</span>') +
|
|
425
425
|
pausedLabel +
|
|
426
426
|
staleLabel +
|
|
427
427
|
'<span style="font-weight:700;font-size:var(--text-base);color:' + (done === g.items.length && g.items.length > 0 ? 'var(--green)' : 'var(--text)') + '">' + (g.items.length > 0 ? Math.round((done / g.items.length) * 100) : 0) + '%</span>' +
|
|
@@ -498,7 +498,7 @@ function renderPrdProgress(prog) {
|
|
|
498
498
|
const col = columns[d] || [];
|
|
499
499
|
const colLabel = d === 0 ? 'Root' : 'Wave ' + d;
|
|
500
500
|
html += '<div style="flex:1;min-width:0">' +
|
|
501
|
-
'<div style="font-size:var(--text-
|
|
501
|
+
'<div style="font-size:var(--text-sm);color:var(--muted);text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px;text-align:center">' + colLabel + '</div>';
|
|
502
502
|
for (const i of col) {
|
|
503
503
|
const borderColor = statusColor(i.status, i.id);
|
|
504
504
|
const src = escHtml(i.source || '');
|
|
@@ -513,7 +513,7 @@ function renderPrdProgress(prog) {
|
|
|
513
513
|
const wiIdBadge = _renderPrdWorkItemIdBadge(wiForCard ? wiForCard.id : workItemId, i.id, '8px', '1px 4px');
|
|
514
514
|
const isDoneCard = i.status === 'done' || (wiForCard && wiForCard.status === 'done');
|
|
515
515
|
const reopenBtnGraph = isDoneCard
|
|
516
|
-
? '<span onclick="event.stopPropagation();prdItemReopen(\'' + src + '\',\'' + iid + '\')" style="font-size:var(--text-
|
|
516
|
+
? '<span onclick="event.stopPropagation();prdItemReopen(\'' + src + '\',\'' + iid + '\')" style="font-size:var(--text-sm);color:var(--blue);cursor:pointer;padding:1px 4px;background:rgba(56,139,253,0.1);border:1px solid rgba(56,139,253,0.3);border-radius:3px" title="Re-open: set to updated so engine re-dispatches on existing branch">re-open</span>'
|
|
517
517
|
: '';
|
|
518
518
|
const cardTitle = i.description ? escHtml(i.name + ' — ' + i.description) : escHtml(i.name || '');
|
|
519
519
|
html += '<div onclick="prdItemEdit(\'' + src + '\',\'' + iid + '\')" ' +
|
|
@@ -528,34 +528,34 @@ function renderPrdProgress(prog) {
|
|
|
528
528
|
'</div>' +
|
|
529
529
|
'<div style="color:var(--text);font-size:var(--text-base);line-height:1.3;margin-bottom:3px;overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical">' + escHtml(i.name) + '</div>' +
|
|
530
530
|
'<div style="display:flex;gap:4px;flex-wrap:wrap;align-items:center">' +
|
|
531
|
-
'<span class="prd-item-priority ' + (i.priority || '') + '" style="font-size:var(--text-
|
|
532
|
-
(agentDisplay ? '<span style="font-size:var(--text-
|
|
531
|
+
'<span class="prd-item-priority ' + (i.priority || '') + '" style="font-size:var(--text-sm);padding:1px 4px">' + escHtml(i.priority || '') + '</span>' +
|
|
532
|
+
(agentDisplay ? '<span style="font-size:var(--text-sm);color:var(--muted)">' + agentDisplay + '</span>' : '') +
|
|
533
533
|
(function() {
|
|
534
534
|
const w = wi[i.id];
|
|
535
535
|
const rqId = w ? w.id : i.id;
|
|
536
536
|
const rq = getPrdRequeueState(rqId);
|
|
537
537
|
if (rq && rq.status === 'pending') {
|
|
538
|
-
return '<span style="font-size:var(--text-
|
|
538
|
+
return '<span style="font-size:var(--text-sm);color:var(--yellow);cursor:wait;padding:1px 4px;background:rgba(210,153,34,0.1);border:1px solid rgba(210,153,34,0.35);border-radius:3px">requeuing…</span>';
|
|
539
539
|
}
|
|
540
540
|
if (rq && rq.status === 'queued') {
|
|
541
|
-
return '<span style="font-size:var(--text-
|
|
541
|
+
return '<span style="font-size:var(--text-sm);color:var(--green);cursor:default;padding:1px 4px;background:rgba(63,185,80,0.1);border:1px solid rgba(63,185,80,0.35);border-radius:3px">requeued</span>';
|
|
542
542
|
}
|
|
543
543
|
if (rq && rq.status === 'error') {
|
|
544
|
-
return '<span style="font-size:var(--text-
|
|
544
|
+
return '<span style="font-size:var(--text-sm);color:var(--red);cursor:default;padding:1px 4px;background:rgba(248,81,73,0.1);border:1px solid rgba(248,81,73,0.35);border-radius:3px" title="' + escHtml(rq.message || 'Retry failed') + '">failed</span>';
|
|
545
545
|
}
|
|
546
546
|
// Show retry for failed items, or PRD items with no work item (orphaned/deleted)
|
|
547
547
|
const canRetry = (w && i.status === 'failed') ||
|
|
548
548
|
(!w && i.status && i.status !== 'missing' && i.status !== 'done' && i.status !== 'planned');
|
|
549
549
|
if (!canRetry) return '';
|
|
550
|
-
return '<span onclick="event.stopPropagation();prdItemRequeue(\'' + escHtml(rqId) + '\',\'' + escHtml(w ? (w._source || '') : '') + '\',\'' + escHtml(i.source || '') + '\')" style="font-size:var(--text-
|
|
550
|
+
return '<span onclick="event.stopPropagation();prdItemRequeue(\'' + escHtml(rqId) + '\',\'' + escHtml(w ? (w._source || '') : '') + '\',\'' + escHtml(i.source || '') + '\')" style="font-size:var(--text-sm);color:var(--green);cursor:pointer;padding:1px 4px;background:rgba(63,185,80,0.1);border:1px solid rgba(63,185,80,0.3);border-radius:3px">retry</span>';
|
|
551
551
|
})() +
|
|
552
552
|
reopenBtnGraph +
|
|
553
|
-
(deps ? '<span style="font-size:var(--text-
|
|
553
|
+
(deps ? '<span style="font-size:var(--text-sm);color:var(--muted)" title="Depends on: ' + escHtml(deps) + '">deps: ' + escHtml(deps) + '</span>' : '') +
|
|
554
554
|
(function() {
|
|
555
555
|
var w = wi[i.id];
|
|
556
556
|
var b = w ? (w.branch || w.featureBranch || (w._artifacts && w._artifacts.branch) || '') : '';
|
|
557
557
|
if (!b) return '';
|
|
558
|
-
return '<span style="font-size:var(--text-
|
|
558
|
+
return '<span style="font-size:var(--text-sm);color:var(--muted);font-family:monospace;max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block;vertical-align:middle" title="Branch: ' + escHtml(b) + '">🌿 ' + escHtml(b) + '</span>';
|
|
559
559
|
})() +
|
|
560
560
|
'</div>' +
|
|
561
561
|
((i.prs || []).length ? '<div style="margin-top:3px">' + (i.prs || []).map(function(pr) { return _renderPrLink(pr, { size: '9px' }); }).join(' ') + '</div>' : '') +
|
|
@@ -565,10 +565,10 @@ function renderPrdProgress(prog) {
|
|
|
565
565
|
return '<div style="margin-top:4px;border-top:1px solid var(--border);padding-top:4px">' +
|
|
566
566
|
children.map(function(c) {
|
|
567
567
|
var cAgent = c.dispatched_to ? (agentData.find(function(a) { return a.id === c.dispatched_to; }) || {}) : null;
|
|
568
|
-
return '<div style="font-size:var(--text-
|
|
568
|
+
return '<div style="font-size:var(--text-sm);display:flex;align-items:center;gap:4px;padding:1px 0">' +
|
|
569
569
|
statusBadge(c.status) +
|
|
570
570
|
'<span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + escHtml((c.title || '').replace('Implement: ', '').slice(0, 40)) + '</span>' +
|
|
571
|
-
(cAgent ? '<span style="font-size:var(--text-
|
|
571
|
+
(cAgent ? '<span style="font-size:var(--text-sm);color:var(--muted)">' + (cAgent.emoji || '') + '</span>' : '') +
|
|
572
572
|
'</div>';
|
|
573
573
|
}).join('') +
|
|
574
574
|
'</div>';
|
|
@@ -588,8 +588,8 @@ function renderPrdProgress(prog) {
|
|
|
588
588
|
const renderViewToggle = () => {
|
|
589
589
|
const isGraph = window._prdViewMode === 'graph';
|
|
590
590
|
return '<div style="display:flex;gap:4px;margin-bottom:8px;padding:0 8px">' +
|
|
591
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
592
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
591
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;' + (isGraph ? 'background:var(--blue);color:#fff;border-color:var(--blue)' : '') + '" onclick="window._prdViewMode=\'graph\';rerenderPrdFromCache()">Graph</button>' +
|
|
592
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;' + (!isGraph ? 'background:var(--blue);color:#fff;border-color:var(--blue)' : '') + '" onclick="window._prdViewMode=\'list\';rerenderPrdFromCache()">List</button>' +
|
|
593
593
|
'</div>';
|
|
594
594
|
};
|
|
595
595
|
|
|
@@ -654,18 +654,18 @@ function renderPrdProgress(prog) {
|
|
|
654
654
|
? renderArtifactLink({ type: 'pr', id: pr.id, label: pr.id, title: pr.title || pr.id })
|
|
655
655
|
: '<code>' + escHtml(pr.id) + '</code>';
|
|
656
656
|
return '<div style="display:flex;align-items:center;gap:6px;padding:2px 0;font-size:var(--text-base);opacity:' + rowOpacity + '">' +
|
|
657
|
-
'<span style="color:' + statusColor + ';font-size:var(--text-
|
|
657
|
+
'<span style="color:' + statusColor + ';font-size:var(--text-sm);font-weight:600;padding:1px 4px;border:1px solid;border-radius:3px">' + escHtml(pr.status || 'active') + '</span>' +
|
|
658
658
|
prChip +
|
|
659
659
|
'<span style="color:var(--text);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + escHtml(pr.title || '') + '</span>' +
|
|
660
|
-
'<span style="color:var(--muted);font-size:var(--text-
|
|
660
|
+
'<span style="color:var(--muted);font-size:var(--text-sm)">' + escHtml(pr._project || '') + '</span>' +
|
|
661
661
|
'</div>';
|
|
662
662
|
}).join('');
|
|
663
663
|
}
|
|
664
664
|
if (guide) {
|
|
665
665
|
html += '<div style="display:flex;align-items:center;gap:6px;padding:' + (prs.length ? '4px' : '0') + ' 0 0;font-size:var(--text-base)">' +
|
|
666
|
-
'<span style="font-size:var(--text-
|
|
666
|
+
'<span style="font-size:var(--text-sm);color:var(--green);font-weight:600;padding:1px 4px;border:1px solid var(--green);border-radius:3px">GUIDE</span>' +
|
|
667
667
|
'<span onclick="openVerifyGuide(\'' + escHtml(guide.file) + '\')" style="color:var(--blue);cursor:pointer;text-decoration:underline;font-weight:500">Manual Testing Guide</span>' +
|
|
668
|
-
'<span style="color:var(--muted);font-size:var(--text-
|
|
668
|
+
'<span style="color:var(--muted);font-size:var(--text-sm)">Build instructions, test steps, known issues</span>' +
|
|
669
669
|
'</div>';
|
|
670
670
|
}
|
|
671
671
|
html += '</div>';
|
|
@@ -766,11 +766,11 @@ function showArchivedPrdDetail(idxOrFile) {
|
|
|
766
766
|
// View mode toggle for archived
|
|
767
767
|
const isGraph = window._archivedPrdViewMode !== 'list';
|
|
768
768
|
const toggleHtml = '<div style="display:flex;gap:4px;margin-bottom:8px">' +
|
|
769
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
770
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
771
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
772
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
773
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
769
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;' + (isGraph ? 'background:var(--blue);color:#fff;border-color:var(--blue)' : '') + '" onclick="window._archivedPrdViewMode=\'graph\';showArchivedPrdDetail(\'' + escHtml(g.file) + '\')">Graph</button>' +
|
|
770
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;' + (!isGraph ? 'background:var(--blue);color:#fff;border-color:var(--blue)' : '') + '" onclick="window._archivedPrdViewMode=\'list\';showArchivedPrdDetail(\'' + escHtml(g.file) + '\')">List</button>' +
|
|
771
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;color:var(--green);margin-left:auto" onclick="triggerVerify(\'' + escHtml(g.file) + '\')">Trigger Verify</button>' +
|
|
772
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px" onclick="planUnarchive(\'' + escHtml(g.file) + '\',this)">Unarchive</button>' +
|
|
773
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px" onclick="openArchivedPrdModal()">Back</button>' +
|
|
774
774
|
'</div>';
|
|
775
775
|
|
|
776
776
|
// Render directly from the archived group data (not from stale renderGroup closure)
|
|
@@ -1072,7 +1072,7 @@ function openArchive(i) {
|
|
|
1072
1072
|
html += '<div class="archive-feature">' +
|
|
1073
1073
|
'<span class="feat-id">' + escHtml(f.id) + '</span> ' +
|
|
1074
1074
|
'<span class="prd-item-priority ' + pClass + '">' + escHtml(f.priority || '') + '</span>' +
|
|
1075
|
-
(f.status ? ' <span class="pr-badge ' + (f.status === 'done' ? 'approved' : 'draft') + '" style="font-size:var(--text-
|
|
1075
|
+
(f.status ? ' <span class="pr-badge ' + (f.status === 'done' ? 'approved' : 'draft') + '" style="font-size:var(--text-sm)">' + escHtml(f.status) + '</span>' : '') +
|
|
1076
1076
|
'<div class="feat-name">' + escHtml(f.name) + '</div>' +
|
|
1077
1077
|
'<div class="feat-desc">' + escHtml(f.description || '') + '</div>' +
|
|
1078
1078
|
(f.rationale ? '<div class="feat-desc" style="margin-top:4px;color:var(--yellow)">Rationale: ' + escHtml(f.rationale) + '</div>' : '') +
|
|
@@ -20,66 +20,10 @@ const PR_FILTER_ALL_LABELS = {
|
|
|
20
20
|
build: 'All build statuses',
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// on /state/ guarantees a fresh read on the next 4 s poll regardless of
|
|
28
|
-
// whether the /api/status outer cache has busted.
|
|
29
|
-
//
|
|
30
|
-
// Engine-side enrichment is minimal: stamping `_project` from the project
|
|
31
|
-
// name (the filename context) and back-filling `url` from `prUrlBase` when
|
|
32
|
-
// missing. Both are applied here to keep parity with the legacy slice.
|
|
33
|
-
// Other engine-derived fields (build status from CI poll, reviewStatus,
|
|
34
|
-
// _buildStatusStale) are PERSISTED to disk by the engine's PR pollers, so
|
|
35
|
-
// they survive the round-trip naturally.
|
|
36
|
-
async function fetchPullRequestsFromDisk(projects) {
|
|
37
|
-
if (!Array.isArray(projects) || !projects.length) return null;
|
|
38
|
-
const results = await Promise.all(projects.map(async (p) => {
|
|
39
|
-
const name = p && (p.name || p.path);
|
|
40
|
-
if (!name) return [];
|
|
41
|
-
const urlBase = (p && p.prUrlBase) || '';
|
|
42
|
-
try {
|
|
43
|
-
const r = await fetch('/state/projects/' + encodeURIComponent(name) + '/pull-requests.json');
|
|
44
|
-
if (!r.ok) return [];
|
|
45
|
-
const arr = await r.json();
|
|
46
|
-
if (!Array.isArray(arr)) return [];
|
|
47
|
-
return arr.map((pr) => {
|
|
48
|
-
if (!pr || typeof pr !== 'object' || !pr.id) return null;
|
|
49
|
-
const stamped = Object.assign({ _project: name }, pr);
|
|
50
|
-
if (!stamped.url && urlBase && typeof stamped.number === 'number') {
|
|
51
|
-
stamped.url = urlBase + stamped.number;
|
|
52
|
-
}
|
|
53
|
-
return stamped;
|
|
54
|
-
}).filter(Boolean);
|
|
55
|
-
} catch {
|
|
56
|
-
return [];
|
|
57
|
-
}
|
|
58
|
-
}));
|
|
59
|
-
const out = [];
|
|
60
|
-
const seen = new Set();
|
|
61
|
-
for (const arr of results) {
|
|
62
|
-
for (const pr of arr) {
|
|
63
|
-
if (seen.has(pr.id)) continue;
|
|
64
|
-
seen.add(pr.id);
|
|
65
|
-
out.push(pr);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
// Same sort the engine applies (engine/queries.js#getPullRequests): group
|
|
69
|
-
// terminal (merged/abandoned/closed) PRs to the bottom, then most recent
|
|
70
|
-
// created first within each group, falling back to id.
|
|
71
|
-
const _isTerminalPr = (pr) => pr.status === 'merged' || pr.status === 'abandoned' || pr.status === 'closed';
|
|
72
|
-
out.sort((a, b) => {
|
|
73
|
-
const at = _isTerminalPr(a) ? 1 : 0;
|
|
74
|
-
const bt = _isTerminalPr(b) ? 1 : 0;
|
|
75
|
-
if (at !== bt) return at - bt;
|
|
76
|
-
const ac = a.created || '';
|
|
77
|
-
const bc = b.created || '';
|
|
78
|
-
if (ac && bc) return bc.localeCompare(ac);
|
|
79
|
-
return (b.id || '').localeCompare(a.id || '');
|
|
80
|
-
});
|
|
81
|
-
return out;
|
|
82
|
-
}
|
|
23
|
+
// Pull requests are served fresh by GET /api/pull-requests (see refresh.js),
|
|
24
|
+
// which runs queries.getPullRequests() server-side. The per-project
|
|
25
|
+
// pull-requests.json mirrors it used to read were retired by the SQL-only
|
|
26
|
+
// cutover.
|
|
83
27
|
|
|
84
28
|
// Terminal statuses — follow-up WIs in these states are historical, not active.
|
|
85
29
|
var _WI_TERMINAL = new Set(['done', 'cancelled', 'failed', 'decomposed', 'in-pr', 'implemented', 'complete']);
|
|
@@ -185,11 +129,11 @@ function prRow(pr) {
|
|
|
185
129
|
const prId = pr.id || '—';
|
|
186
130
|
const pendingReason = pr._pendingReason ? String(pr._pendingReason) : '';
|
|
187
131
|
const pendingReasonHtml = pendingReason
|
|
188
|
-
? '<div style="font-size:var(--text-
|
|
132
|
+
? '<div style="font-size:var(--text-sm);color:var(--muted);margin-top:2px" title="Pending reason: ' + escapeHtml(pendingReason) + '">' + escapeHtml(pendingReason.replace(/_/g, ' ')) + '</div>'
|
|
189
133
|
: '';
|
|
190
134
|
var followupCount = _countPrFollowups(pr);
|
|
191
135
|
var followupChip = followupCount > 0
|
|
192
|
-
? ' <span class="pr-badge draft" style="font-size:var(--text-
|
|
136
|
+
? ' <span class="pr-badge draft" style="font-size:var(--text-sm)" title="' + followupCount + ' active follow-up work item(s) in progress from comments on this PR">+' + followupCount + ' follow-up' + (followupCount === 1 ? '' : 's') + '</span>'
|
|
193
137
|
: '';
|
|
194
138
|
// Issue #2969 — paused-cause chip(s). Prefer the API-enriched _pausedCauses
|
|
195
139
|
// field; fall back to deriving from _noOpFixes for the /state/ disk-fetch
|
|
@@ -210,7 +154,7 @@ function prRow(pr) {
|
|
|
210
154
|
+ (rec.reason || 'unknown') + '. Click to resume (clears _noOpFixes['
|
|
211
155
|
+ cause + '] so the next discovery pass can re-dispatch).';
|
|
212
156
|
return ' <button class="pr-badge rejected pr-paused-chip" '
|
|
213
|
-
+ 'style="font-size:var(--text-
|
|
157
|
+
+ 'style="font-size:var(--text-sm);cursor:pointer;border:none" '
|
|
214
158
|
+ 'title="' + escapeHtml(titleAttr) + '" '
|
|
215
159
|
+ 'data-pr-id="' + escapeHtml(String(pr.id || '')) + '" '
|
|
216
160
|
+ 'data-pr-cause="' + escapeHtml(cause) + '" '
|
|
@@ -243,7 +187,7 @@ function prRow(pr) {
|
|
|
243
187
|
+ (buildFixIneffective.reason || 'unknown')
|
|
244
188
|
+ '. A new push to this branch will reset this pause automatically, or click to resume now.';
|
|
245
189
|
pausedChips += ' <button class="pr-badge rejected pr-build-fix-ineffective-chip" '
|
|
246
|
-
+ 'style="font-size:var(--text-
|
|
190
|
+
+ 'style="font-size:var(--text-sm);cursor:pointer;border:none" '
|
|
247
191
|
+ 'title="' + escapeHtml(bfiTitle) + '" '
|
|
248
192
|
+ 'data-pr-id="' + escapeHtml(String(pr.id || '')) + '" '
|
|
249
193
|
+ 'data-pr-head-sha="' + escapeHtml(String(buildFixIneffective.headSha || '')) + '" '
|
|
@@ -264,7 +208,7 @@ function prRow(pr) {
|
|
|
264
208
|
var whpTitle = (worktreeHeldPaused.reason || 'Branch is held externally; PR automation paused')
|
|
265
209
|
+ '. Auto-clears once the holder releases the branch, or click to resume now.';
|
|
266
210
|
pausedChips += ' <button class="pr-badge rejected pr-worktree-held-chip" '
|
|
267
|
-
+ 'style="font-size:var(--text-
|
|
211
|
+
+ 'style="font-size:var(--text-sm);cursor:pointer;border:none" '
|
|
268
212
|
+ 'title="' + escapeHtml(whpTitle) + '" '
|
|
269
213
|
+ 'data-pr-id="' + escapeHtml(String(pr.id || '')) + '" '
|
|
270
214
|
+ 'onclick="event.stopPropagation();resumeWorktreeHeldPause(this)">'
|
|
@@ -240,8 +240,8 @@ const _SLOT_COLORS = {
|
|
|
240
240
|
function _renderViewToggle() {
|
|
241
241
|
const isList = _schedViewMode === 'list';
|
|
242
242
|
return '<div style="display:flex;gap:4px;margin-bottom:8px">' +
|
|
243
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
244
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
243
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;' + (isList ? 'background:var(--blue);color:#fff;border-color:var(--blue)' : '') + '" onclick="_schedSetView(\'list\')">List</button>' +
|
|
244
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:2px 8px;' + (!isList ? 'background:var(--blue);color:#fff;border-color:var(--blue)' : '') + '" onclick="_schedSetView(\'calendar\')">Calendar</button>' +
|
|
245
245
|
'</div>';
|
|
246
246
|
}
|
|
247
247
|
|
|
@@ -299,7 +299,7 @@ function _renderScheduleCalendar(schedules) {
|
|
|
299
299
|
html += '<div class="sched-cal-slot" style="background:' + colors.bg + ';border-left-color:' + colors.border + ';color:' + colors.text + ';opacity:' + opacity + '" ' +
|
|
300
300
|
'onclick="if(shouldIgnoreSelectionClick(event))return;openScheduleDetail(\'' + escHtml(s.id) + '\')" title="' + escHtml(s.title + ' — ' + timeLabel + ' — ' + (s.type || 'implement')) + '">' +
|
|
301
301
|
'<span style="font-weight:600;' + strikeStyle + '">' + escHtml((s.title || s.id).slice(0, 25)) + '</span>' +
|
|
302
|
-
'<span style="font-size:var(--text-
|
|
302
|
+
'<span style="font-size:var(--text-sm);opacity:0.7"> ' + timeLabel + '</span>' +
|
|
303
303
|
'</div>';
|
|
304
304
|
}
|
|
305
305
|
if (cellSlots.length === 0) html += ' ';
|
|
@@ -353,8 +353,8 @@ function renderSchedules(schedules, opts) {
|
|
|
353
353
|
'<td><span class="pr-date">' + escHtml(lastRun) + '</span></td>' +
|
|
354
354
|
'<td style="white-space:nowrap">' +
|
|
355
355
|
'<button class="btn-action" style="margin-right:4px" onclick="event.stopPropagation();runScheduleNow(\'' + escHtml(s.id) + '\',this)" title="Run now">Run now</button>' +
|
|
356
|
-
'<button class="pr-pager-btn' + (_schedToggleInFlight.has(s.id) ? ' disabled' : '') + '" style="font-size:var(--text-
|
|
357
|
-
'<button class="pr-pager-btn" style="font-size:var(--text-
|
|
356
|
+
'<button class="pr-pager-btn' + (_schedToggleInFlight.has(s.id) ? ' disabled' : '') + '" style="font-size:var(--text-sm);padding:1px 6px;color:' + (s.enabled ? 'var(--yellow)' : 'var(--green)') + ';border-color:' + (s.enabled ? 'var(--yellow)' : 'var(--green)') + ';margin-right:4px" onclick="event.stopPropagation();toggleScheduleEnabled(\'' + escHtml(s.id) + '\',' + !s.enabled + ')" title="' + (s.enabled ? 'Disable' : 'Enable') + '">' + (s.enabled ? '⏸' : '▶') + '</button>' +
|
|
357
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-sm);padding:1px 6px;color:var(--blue);border-color:var(--blue);margin-right:4px" onclick="event.stopPropagation();openEditScheduleModal(\'' + escHtml(s.id) + '\')" title="Edit">✎</button>' +
|
|
358
358
|
'<button class="btn-destructive" onclick="event.stopPropagation();deleteSchedule(\'' + escHtml(s.id) + '\')" title="Delete">✕</button>' +
|
|
359
359
|
'</td>' +
|
|
360
360
|
'</tr>';
|