Skip to content

Instantly share code, notes, and snippets.

@gcrsaldanha
Created December 19, 2025 19:08
Show Gist options
  • Select an option

  • Save gcrsaldanha/a5356773f033341bb398340c229a2b55 to your computer and use it in GitHub Desktop.

Select an option

Save gcrsaldanha/a5356773f033341bb398340c229a2b55 to your computer and use it in GitHub Desktop.
Speckit Workflow Demo: Edit Company Recipients Feature

Speckit Demo: Edit Company Recipients

PR: carta/carta-frontend-platform#18584


TL;DR: From Prompt to PR

# 1. Initial prompt
> "Implement the front-end components and flow for Edit company recipients"
> Figma: https://www.figma.com/design/.../RFI-for-PE-LLCs?node-id=3181-126265

# 2. Generate specification with user stories
> /speckit.specify

# 3. Research technical context (existing patterns, API types)
> /speckit.plan    # includes research phase

# 4. Generate dependency-ordered tasks
> /speckit.tasks

# 5. Implement following TDD (tests first)
> /speckit.implement

Output: 32 tasks, 4 components, 2 hooks, 15 stories, 56 tests


1. Research Phase Highlights (research.md)

Speckit analyzed the codebase to resolve technical decisions:

### API Types (RESOLVED)

**Decision**: Use existing `PointOfContact` type from `@carta/api-rfi-data-collection`

**Rationale**: The schema already defines:
  PointOfContact: { email, name, title, primary, is_company_designated }

**Alternatives considered**:
- Create new type β†’ Rejected: duplicates existing well-defined schema

**Location**: `apis/investor-relations/rfi-data-collection/client/schema.d.ts`

---

### Modal Pattern (RESOLVED)

**Decision**: Follow existing `EditRequestEmailModal` pattern

**Rationale**: Established pattern in codebase:
- `Modal.Header`, `Modal.Body`, `Modal.Footer` with `enableNewStyles`
- `VStack`, `HStack` for layout
- `Field` wrapper for form inputs

**Reference**: `src/core/components/PeriodView/modals/EditRequestEmailModal.tsx`

---

### API Mutation Pattern (RESOLVED)

**Decision**: Use `useCallback` with `callApi` (not useMutation)

**Rationale**: Existing pattern in `useExportRfiData`:
  const exportData = useCallback(async () => {
    await callApi(apiRequestBuilder, { firmId });
  }, [firmId]);

**Reference**: `src/core/hooks/mutations/useExportRfiData.ts`

2. Specification (spec.md)

Three user stories with acceptance criteria:

### User Story 1 - Carta Designated Contacts (P1) 🎯 MVP

**Given** a Carta company with designated contacts is selected,
**When** I click "Edit contacts" from the row actions menu,
**Then** I see a modal with:
- "Send request to:" section with designated contact
- "Primary" and "Designated" badges
- Message: "This contact is designated by the company and cannot be changed."
- "Add recipient" link for CC recipients

### User Story 2 - Investor Managed Contacts (P2)
- Primary recipient is editable via email search
- Can add/remove CC recipients

### User Story 3 - Missing Contact (P3)
- Empty state with inline form to add new contact

3. Tasks with Parallelization (tasks.md)

32 tasks organized by phase. Tasks marked [P] can run simultaneously:

## Phase 2: Foundational
- [x] T005 [P] Create useUpdateRecipients hook      ──┬── Parallel
- [x] T006 [P] Create useGetEntityRecipients hook   β”€β”€β”˜

## Phase 3: User Story 1 - MVP
- [x] T007 [US1] Write tests for RecipientListItem    ◄── TDD: tests first
- [x] T009 [P] Create RecipientListItem.tsx           ──┬── Parallel
- [x] T010 [P] Create RecipientListItem.stories.tsx   β”€β”€β”˜

## Phase 4: User Story 2
- [x] T016 [P] Create EmailAutocomplete.tsx           ──┬── Parallel
- [x] T017 [P] Create EmailAutocomplete.stories.tsx   β”€β”€β”˜

## Phase 5: User Story 3
- [x] T023 [P] Create AddRecipientForm.tsx            ──┬── Parallel
- [x] T024 [P] Create AddRecipientForm.stories.tsx    β”€β”€β”˜

12 parallel opportunities across 32 tasks.


4. Generated Storybook Stories

Modal variants for each contact type scenario:

// CartaDesignated - read-only primary contact
export const CartaDesignated: Story = {
    args: {
        entityName: 'Acme Corp',
        contactType: 'carta_designated',
        recipients: [
            { name: 'Jane Smith', isDesignated: true, isPrimary: true },
        ],
    },
};

// InvestorManaged - editable primary
export const InvestorManaged: Story = {
    args: {
        entityName: 'Beta Inc',
        contactType: 'investor_managed',
        recipients: [{ name: 'Bob Johnson', isDesignated: false, isPrimary: true }],
    },
};

// MissingContact - empty state with add form
export const MissingContact: Story = {
    args: {
        entityName: 'New Company',
        contactType: 'missing',
        recipients: [],
    },
};

Results

Artifact Count
Tasks completed 26/32
Components 4 (EditRecipientsModal, RecipientListItem, AddRecipientForm, EmailAutocomplete)
Hooks 2 (useUpdateRecipients, useGetEntityRecipients)
Storybook stories 15
Tests 56 passing
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment