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 <[email protected]>
This commit is contained in:
Sondre Engebråten
2026-02-12 21:16:46 +01:00
co-authored by Claude Opus 4.6
parent c2798a9991
commit bfd24f0d57
4 changed files with 60 additions and 7 deletions
@@ -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({
)}
</div>
{/* Create Spec */}
{onCreateSpec && (
<CreateSpecButton
issueNumber={issue.number}
issueClosed={issue.state === 'closed'}
hasActiveAgent={!!enrichment?.agentLinks?.some(l => l.status === 'active')}
hasEnrichment={!!enrichment?.enrichment}
onCreateSpec={onCreateSpec}
/>
)}
{/* Task Linked Info */}
{hasLinkedTask && (
<Card className="bg-success/5 border-success/30">
@@ -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(
<IssueDetail
{...baseProps}
onCreateSpec={vi.fn()}
/>,
);
// 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(<IssueDetail {...baseProps} />);
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(
<IssueDetail
{...baseProps}
enrichment={enrichment as never}
onTransition={vi.fn()}
onCreateSpec={vi.fn()}
/>,
);
const createBtn = screen.getByText('Create Spec');
expect(createBtn.hasAttribute('disabled')).toBe(true);
});
});
@@ -52,6 +52,7 @@ export interface IssueDetailProps {
onAddAssignees?: (logins: string[]) => Promise<void>;
onRemoveAssignees?: (logins: string[]) => Promise<void>;
collaborators?: string[];
onCreateSpec?: () => Promise<{ specNumber: string } | null>;
onClose?: (comment?: string) => Promise<void>;
onReopen?: () => Promise<void>;
onComment?: (body: string) => Promise<void>;
+8 -7
View File
@@ -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 |
---