refactor(issue): remove 'merged' queue status, use 'archived' instead

- Remove 'merged' from VALID_QUEUE_STATUSES constant
- Update mergeQueues() to set status to 'archived' instead of 'merged'
- Preserve merged_into/merged_at metadata for traceability
- Update frontend to use 'archived' for button visibility checks
- Fix queue --json returning fake queue ID when no active queue exists
This commit is contained in:
catlog22
2026-01-25 10:56:46 +08:00
parent fe2536d4cd
commit 9cff6f5f43
2 changed files with 16 additions and 16 deletions

View File

@@ -172,7 +172,7 @@ interface ExecutionGroup {
interface Queue { interface Queue {
id: string; // Queue unique ID: QUE-YYYYMMDD-HHMMSS (derived from filename) id: string; // Queue unique ID: QUE-YYYYMMDD-HHMMSS (derived from filename)
name?: string; // Optional queue name name?: string; // Optional queue name
status: 'active' | 'completed' | 'archived' | 'failed' | 'merged'; status: 'active' | 'completed' | 'archived' | 'failed';
issue_ids: string[]; // Issues in this queue issue_ids: string[]; // Issues in this queue
tasks: QueueItem[]; // Task items (task-level queue) tasks: QueueItem[]; // Task items (task-level queue)
solutions?: QueueItem[]; // Solution items (solution-level queue) solutions?: QueueItem[]; // Solution items (solution-level queue)
@@ -235,7 +235,7 @@ const ISSUES_DIR = '.workflow/issues';
// ============ Status Constants ============ // ============ Status Constants ============
const VALID_QUEUE_STATUSES = ['active', 'completed', 'archived', 'failed', 'merged'] as const; const VALID_QUEUE_STATUSES = ['active', 'completed', 'archived', 'failed'] as const;
const VALID_ITEM_STATUSES = ['pending', 'ready', 'executing', 'completed', 'failed', 'blocked'] as const; const VALID_ITEM_STATUSES = ['pending', 'ready', 'executing', 'completed', 'failed', 'blocked'] as const;
const VALID_ISSUE_STATUSES = ['registered', 'planning', 'planned', 'queued', 'executing', 'completed', 'failed', 'paused'] as const; const VALID_ISSUE_STATUSES = ['registered', 'planning', 'planned', 'queued', 'executing', 'completed', 'failed', 'paused'] as const;
@@ -777,7 +777,7 @@ interface MergeResult {
* Merge items from source queue into target queue * Merge items from source queue into target queue
* - Skips duplicate items (same issue_id + solution_id) * - Skips duplicate items (same issue_id + solution_id)
* - Re-generates item IDs for merged items * - Re-generates item IDs for merged items
* - Marks source queue as 'merged' with metadata (or deletes if deleteSource=true) * - Marks source queue as 'archived' with metadata (or deletes if deleteSource=true)
* - Updates queue index * - Updates queue index
*/ */
function mergeQueues(target: Queue, source: Queue, options?: { deleteSource?: boolean }): MergeResult { function mergeQueues(target: Queue, source: Queue, options?: { deleteSource?: boolean }): MergeResult {
@@ -832,7 +832,7 @@ function mergeQueues(target: Queue, source: Queue, options?: { deleteSource?: bo
// Write updated target queue // Write updated target queue
writeQueue(target); writeQueue(target);
// Handle source queue: delete or mark as merged // Handle source queue: delete or mark as archived
const index = readQueueIndex(); const index = readQueueIndex();
if (options?.deleteSource) { if (options?.deleteSource) {
@@ -843,8 +843,8 @@ function mergeQueues(target: Queue, source: Queue, options?: { deleteSource?: bo
} }
index.queues = index.queues.filter(q => q.id !== source.id); index.queues = index.queues.filter(q => q.id !== source.id);
} else { } else {
// Mark source queue as merged // Mark source queue as archived (was merged)
source.status = 'merged'; source.status = 'archived';
if (!source._metadata) { if (!source._metadata) {
source._metadata = { source._metadata = {
version: '2.1', version: '2.1',
@@ -862,7 +862,7 @@ function mergeQueues(target: Queue, source: Queue, options?: { deleteSource?: bo
const sourceEntry = index.queues.find(q => q.id === source.id); const sourceEntry = index.queues.find(q => q.id === source.id);
if (sourceEntry) { if (sourceEntry) {
sourceEntry.status = 'merged'; sourceEntry.status = 'archived';
} }
} }
@@ -2265,7 +2265,7 @@ async function queueAction(subAction: string | undefined, issueId: string | unde
process.exit(1); process.exit(1);
} }
// mergeQueues marks source as 'merged' and updates index // mergeQueues marks source as 'archived' and updates index
const result = mergeQueues(targetQueue, sourceQueue); const result = mergeQueues(targetQueue, sourceQueue);
if (options.json) { if (options.json) {
@@ -2285,7 +2285,7 @@ async function queueAction(subAction: string | undefined, issueId: string | unde
console.log(chalk.gray(` Skipped ${result.skippedDuplicates} duplicate items`)); console.log(chalk.gray(` Skipped ${result.skippedDuplicates} duplicate items`));
} }
console.log(chalk.gray(` Total items in target: ${result.totalItems}`)); console.log(chalk.gray(` Total items in target: ${result.totalItems}`));
console.log(chalk.gray(` Source queue ${sourceQueueId} marked as 'merged'`)); console.log(chalk.gray(` Source queue ${sourceQueueId} archived`));
} else { } else {
console.log(chalk.yellow(`⚠ Merge skipped: ${result.reason}`)); console.log(chalk.yellow(`⚠ Merge skipped: ${result.reason}`));
} }

View File

@@ -702,7 +702,7 @@ function renderQueueCard(queue, isActive) {
const completedCount = queue.completed_solutions || queue.completed_tasks || 0; const completedCount = queue.completed_solutions || queue.completed_tasks || 0;
const progressPercent = itemCount > 0 ? Math.round((completedCount / itemCount) * 100) : 0; const progressPercent = itemCount > 0 ? Math.round((completedCount / itemCount) * 100) : 0;
const issueCount = queue.issue_ids?.length || 0; const issueCount = queue.issue_ids?.length || 0;
const statusClass = queue.status === 'merged' ? 'merged' : queue.status || ''; const statusClass = queue.status || '';
const safeQueueId = escapeHtml(queue.id || ''); const safeQueueId = escapeHtml(queue.id || '');
return ` return `
@@ -740,17 +740,17 @@ function renderQueueCard(queue, isActive) {
<button class="btn-sm" onclick="toggleQueueExpand('${safeQueueId}')" title="View details"> <button class="btn-sm" onclick="toggleQueueExpand('${safeQueueId}')" title="View details">
<i data-lucide="eye" class="w-3 h-3"></i> <i data-lucide="eye" class="w-3 h-3"></i>
</button> </button>
${!isActive && queue.status !== 'merged' ? ` ${!isActive && queue.status !== 'archived' ? `
<button class="btn-sm btn-primary" onclick="activateQueue('${safeQueueId}')" title="Set as active"> <button class="btn-sm btn-primary" onclick="activateQueue('${safeQueueId}')" title="Set as active">
<i data-lucide="check-circle" class="w-3 h-3"></i> <i data-lucide="check-circle" class="w-3 h-3"></i>
</button> </button>
` : ''} ` : ''}
${queue.status !== 'merged' ? ` ${queue.status !== 'archived' ? `
<button class="btn-sm" onclick="showMergeQueueModal('${safeQueueId}')" title="Merge into another queue"> <button class="btn-sm" onclick="showMergeQueueModal('${safeQueueId}')" title="Merge into another queue">
<i data-lucide="git-merge" class="w-3 h-3"></i> <i data-lucide="git-merge" class="w-3 h-3"></i>
</button> </button>
` : ''} ` : ''}
${queue.status !== 'merged' && issueCount > 1 ? ` ${queue.status !== 'archived' && issueCount > 1 ? `
<button class="btn-sm" onclick="showSplitQueueModal('${safeQueueId}')" title="Split queue into multiple queues"> <button class="btn-sm" onclick="showSplitQueueModal('${safeQueueId}')" title="Split queue into multiple queues">
<i data-lucide="git-branch" class="w-3 h-3"></i> <i data-lucide="git-branch" class="w-3 h-3"></i>
</button> </button>
@@ -896,7 +896,7 @@ async function renderExpandedQueueView(queueId) {
</div> </div>
</div> </div>
<div class="queue-detail-actions"> <div class="queue-detail-actions">
${!isActive && queue.status !== 'merged' ? ` ${!isActive && queue.status !== 'archived' ? `
<button class="btn-primary" onclick="activateQueue('${safeQueueId}')"> <button class="btn-primary" onclick="activateQueue('${safeQueueId}')">
<i data-lucide="check-circle" class="w-4 h-4"></i> <i data-lucide="check-circle" class="w-4 h-4"></i>
<span>${t('issues.activate') || 'Activate'}</span> <span>${t('issues.activate') || 'Activate'}</span>
@@ -1141,7 +1141,7 @@ function showMergeQueueModal(sourceQueueId) {
} }
const otherQueues = queueData.queues.filter(q => const otherQueues = queueData.queues.filter(q =>
q.id !== sourceQueueId && q.status !== 'merged' q.id !== sourceQueueId && q.status !== 'archived'
); );
const safeSourceId = escapeHtml(sourceQueueId || ''); const safeSourceId = escapeHtml(sourceQueueId || '');
@@ -1170,7 +1170,7 @@ function showMergeQueueModal(sourceQueueId) {
</div> </div>
<p class="text-sm text-muted-foreground mt-2"> <p class="text-sm text-muted-foreground mt-2">
<i data-lucide="info" class="w-4 h-4 inline mr-1"></i> <i data-lucide="info" class="w-4 h-4 inline mr-1"></i>
Items from source queue will be appended to target queue. Source queue will be marked as "merged". Items from source queue will be appended to target queue. Source queue will be archived.
</p> </p>
`} `}
</div> </div>