From bfd24f0d578aed5259767b2fdf7d90bc6cd30fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sondre=20Engebr=C3=A5ten?= Date: Thu, 12 Feb 2026 21:16:46 +0100 Subject: [PATCH] fix(issues): GAP-06 wire CreateSpecButton in IssueDetail - Add onCreateSpec prop to IssueDetailProps - Import and conditionally render CreateSpecButton after actions section - Derive hasActiveAgent and hasEnrichment from enrichment data - 3 new integration tests (visible, hidden, disabled when agent active) - All 23 IssueDetail integration tests pass, lint clean Co-Authored-By: Claude Opus 4.6 --- .../github-issues/components/IssueDetail.tsx | 13 +++++++ .../IssueDetail.integration.test.tsx | 38 +++++++++++++++++++ .../components/github-issues/types/index.ts | 1 + docs/gap-tracker.md | 15 ++++---- 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/apps/frontend/src/renderer/components/github-issues/components/IssueDetail.tsx b/apps/frontend/src/renderer/components/github-issues/components/IssueDetail.tsx index 9a114067..078fc7f4 100644 --- a/apps/frontend/src/renderer/components/github-issues/components/IssueDetail.tsx +++ b/apps/frontend/src/renderer/components/github-issues/components/IssueDetail.tsx @@ -20,6 +20,7 @@ import { CommentForm } from './CommentForm'; import { InlineEditor } from './InlineEditor'; import { LabelManager } from './LabelManager'; import { AssigneeManager } from './AssigneeManager'; +import { CreateSpecButton } from './CreateSpecButton'; import type { IssueDetailProps } from '../types'; export function IssueDetail({ @@ -45,6 +46,7 @@ export function IssueDetail({ onAddAssignees, onRemoveAssignees, collaborators, + onCreateSpec, onClose, onReopen, onComment, @@ -214,6 +216,17 @@ export function IssueDetail({ )} + {/* Create Spec */} + {onCreateSpec && ( + l.status === 'active')} + hasEnrichment={!!enrichment?.enrichment} + onCreateSpec={onCreateSpec} + /> + )} + {/* Task Linked Info */} {hasLinkedTask && ( diff --git a/apps/frontend/src/renderer/components/github-issues/components/__tests__/IssueDetail.integration.test.tsx b/apps/frontend/src/renderer/components/github-issues/components/__tests__/IssueDetail.integration.test.tsx index c56ecf7b..da046a95 100644 --- a/apps/frontend/src/renderer/components/github-issues/components/__tests__/IssueDetail.integration.test.tsx +++ b/apps/frontend/src/renderer/components/github-issues/components/__tests__/IssueDetail.integration.test.tsx @@ -260,4 +260,42 @@ describe('IssueDetail integration', () => { fireEvent.click(screen.getByLabelText('Remove assignee dev1')); expect(onRemoveAssignees).toHaveBeenCalledWith(['dev1']); }); + + // GAP-06: CreateSpecButton integration + it('renders CreateSpecButton when onCreateSpec is provided', () => { + render( + , + ); + // CreateSpecButton renders a section with aria-label "Create spec from issue" + expect(screen.getByLabelText('Create spec from issue')).toBeDefined(); + expect(screen.getByText('Create Spec')).toBeDefined(); + }); + + it('does not render CreateSpecButton when onCreateSpec is not provided', () => { + render(); + expect(screen.queryByLabelText('Create spec from issue')).toBeNull(); + }); + + it('CreateSpecButton is disabled when agent is active', () => { + const enrichment = { + issueNumber: 42, + triageState: 'triage' as const, + completenessScore: 50, + enrichment: { problem: 'p', goal: 'g', scopeIn: [], scopeOut: [], acceptanceCriteria: [], technicalContext: '' }, + agentLinks: [{ agentId: 'a1', status: 'active' as const, specNumber: '001' }], + }; + render( + , + ); + const createBtn = screen.getByText('Create Spec'); + expect(createBtn.hasAttribute('disabled')).toBe(true); + }); }); diff --git a/apps/frontend/src/renderer/components/github-issues/types/index.ts b/apps/frontend/src/renderer/components/github-issues/types/index.ts index 7c597851..d25e6d75 100644 --- a/apps/frontend/src/renderer/components/github-issues/types/index.ts +++ b/apps/frontend/src/renderer/components/github-issues/types/index.ts @@ -52,6 +52,7 @@ export interface IssueDetailProps { onAddAssignees?: (logins: string[]) => Promise; onRemoveAssignees?: (logins: string[]) => Promise; collaborators?: string[]; + onCreateSpec?: () => Promise<{ specNumber: string } | null>; onClose?: (comment?: string) => Promise; onReopen?: () => Promise; onComment?: (body: string) => Promise; diff --git a/docs/gap-tracker.md b/docs/gap-tracker.md index 21ab9401..e1f020d8 100644 --- a/docs/gap-tracker.md +++ b/docs/gap-tracker.md @@ -3,7 +3,7 @@ **Branch:** `terminal/enhancement-issues-tab` **Created:** 2026-02-12 **Total Gaps:** 41 confirmed (from triple-verified audit) -**Status:** 10 / 41 complete +**Status:** 11 / 41 complete --- @@ -95,17 +95,17 @@ Each gap has: ID, description, status, files to modify, doc reference, test stat - **Commit:** (combined with GAP-04) ### GAP-06: CreateSpecButton not used in IssueDetail.tsx -- **Status:** `PENDING` +- **Status:** `DONE` - **Priority:** MUST-FIX - **Scope:** Medium - **Doc ref:** Phase 2 PRD > US-8 > AC8.1; Phase 2 PRD Section 4.2 -- **Files to modify:** `renderer/components/github-issues/components/IssueDetail.tsx` +- **Files to modify:** `renderer/components/github-issues/components/IssueDetail.tsx`, `types/index.ts` - **Source component:** `CreateSpecButton.tsx` — accepts issueNumber, issueClosed, hasActiveAgent, activeSpecNumber, hasEnrichment, onCreateSpec -- **Fix:** Import CreateSpecButton. Add inside actions div (after Investigate button, ~line 163). Wire props from enrichment data. -- **Tests:** Render with onCreateSpec → button visible; check disabled when hasActiveAgent -- **Test status:** `PENDING` +- **Fix:** Added onCreateSpec to IssueDetailProps, imported CreateSpecButton, conditionally renders after actions when onCreateSpec provided, derives hasActiveAgent and hasEnrichment from enrichment data +- **Tests:** 3 new tests: button visible with onCreateSpec, hidden without, disabled when agent active +- **Test status:** `PASS` (23/23) - **Depends on:** None (standalone component) -- **Commit:** — +- **Commit:** GAP-06 ### GAP-07: CompletenessBreakdown not used in EnrichmentPanel.tsx - **Status:** `PENDING` @@ -554,6 +554,7 @@ Each gap has: ID, description, status, files to modify, doc reference, test stat | 2026-02-12 | GAP-21 | DONE — optimistic store updates in useMutations, 21 tests pass | GAP-21 | | 2026-02-12 | GAP-02+03 | DONE — InlineEditor wired for title (required) and body (multiline) in IssueDetail, 7 new tests, 14 pass | GAP-02+03 | | 2026-02-12 | GAP-04+05 | DONE — LabelManager+AssigneeManager wired in IssueDetail, repoLabels/collaborators fetched via IPC, 6 new tests, 20 pass | GAP-04+05 | +| 2026-02-12 | GAP-06 | DONE — CreateSpecButton wired in IssueDetail with onCreateSpec prop, 3 new tests, 23 pass | GAP-06 | ---