# Implementation Plan: Step Library Frontend + Tree Editor Validation > **Date:** February 3, 2026 (Updated after refinement session) > **Scope:** Issues #1, #8, #9, #10 > **Estimated Components:** 10 new files, 8 modified files, 1 migration > **Parallel Workstreams:** 2 > **Related Planning:** Issue #25 (Draft Trees - Phase 3) --- ## Plan Refinements (Feb 3, 2026) This plan was refined through collaborative design discussion. Key changes from initial draft: 1. **Custom Steps Persistence** - Changed from storing in `decisions` array to separate `custom_steps` JSONB field - Added: Backend migration task (B.10) - Added: Session schema updates - Enables: Session resuming with custom steps 2. **Custom Step Navigation** - Expanded to support all step types (decision/action/solution) - Added: Detailed rendering logic for each type in B.11 - Enables: Custom branching decisions during troubleshooting 3. **Validation Warnings** - Clarified as inline/dismissible (no confirmation modal) - Updated: Acceptance criteria to specify warning behavior - Deferred: "Save as Draft" feature to Issue #25 (Phase 3) --- ## Overview This plan covers two parallel workstreams: | Workstream | Issues | Description | |------------|--------|-------------| | **A** | #1 | Tree Editor Validation UI | | **B** | #10 → #9 → #8 | Step Library Frontend (sequential) | Both workstreams are independent and can be developed simultaneously. --- ## Workstream A: Tree Editor Validation (Issue #1) ### Current State The validation logic **already exists** in `treeEditorStore.ts:519-658`. It checks: - ✅ Tree name required - ✅ Decision nodes need question + options - ✅ Option labels required - ✅ Action/Solution nodes need titles - ✅ At least one solution node - ✅ Orphan node detection - ✅ Invalid node reference detection ### What's Missing 1. **UI to display validation errors** - No visual feedback shown to user 2. **Prevent save on errors** - Save button should be disabled or show confirmation 3. **Circular reference detection** - Not implemented in current validation 4. **Real-time validation** - Currently only validates on explicit call ### Tasks #### A.1: Add Circular Reference Detection **File:** `frontend/src/store/treeEditorStore.ts` Add to the `validate()` function after line 624: ```typescript // Check for circular references in next_node_id chains const detectCircularRefs = (startId: string, visited: Set = new Set()): boolean => { if (visited.has(startId)) return true visited.add(startId) const node = findNodeInTree(startId, state.treeStructure) if (!node) return false // Check options if (node.options) { for (const opt of node.options) { if (opt.next_node_id && detectCircularRefs(opt.next_node_id, new Set(visited))) { errors.push({ nodeId: node.id, message: `Circular reference detected: "${opt.label}" creates a loop`, severity: 'error' }) return true } } } // Check next_node_id if (node.next_node_id && detectCircularRefs(node.next_node_id, new Set(visited))) { errors.push({ nodeId: node.id, message: `Circular reference detected in node "${node.title || node.id}"`, severity: 'error' }) return true } return false } // Run from root detectCircularRefs('root') ``` #### A.2: Create Validation Summary Component **File:** `frontend/src/components/tree-editor/ValidationSummary.tsx` (NEW) ```typescript interface ValidationSummaryProps { errors: ValidationError[] onSelectNode: (nodeId: string) => void } ``` Features: - Collapsible panel showing error/warning count - Click error to select the problematic node - Color-coded: red for errors, yellow for warnings - Icon indicators (AlertCircle, AlertTriangle from lucide-react) #### A.3: Integrate Validation UI into TreeEditorPage **File:** `frontend/src/pages/TreeEditorPage.tsx` Modifications: 1. Call `validate()` before save attempt 2. Show ValidationSummary when errors or warnings exist 3. Block save if any severity='error' exists (disable save button) 4. Warnings are **informational only** - do not block save, no confirmation modal 5. Add "Validate" button in toolbar for manual check 6. Auto-validate on blur from form fields (debounced) #### A.4: Visual Node Error Indicators **File:** `frontend/src/components/tree-editor/NodeList.tsx` Modifications: - Add red border/highlight to nodes with validation errors - Show error icon badge on problem nodes - Tooltip on hover showing the specific error ### Acceptance Criteria - Workstream A - [ ] Cannot save tree without a name (error blocks save) - [ ] Cannot save tree without at least one solution node (error blocks save) - [ ] Validation errors display in a clear, clickable list - [ ] Clicking an error selects the problematic node - [ ] Circular references are detected and blocked (error) - [ ] Orphan nodes show as warnings (informational, do not block save) - [ ] Save button disabled when errors exist - [ ] Warnings are dismissible/ignorable - user can save with warnings present --- ## Workstream B: Step Library Frontend (Issues #10, #9, #8) ### Dependencies - Backend API complete ✅ (`/api/v1/steps/*`, `/api/v1/step-categories/*`) - No frontend API client exists yet ### Execution Order ``` #10 Step Library Browser Component ↓ #9 Custom Step Creation Modal (embeds browser) ↓ #8 Add Custom Step Button (triggers modal) ``` --- ### Issue #10: Step Library Browser Component #### B.1: Create Steps API Client **File:** `frontend/src/api/steps.ts` (NEW) ```typescript // API client for step library endpoints export const stepsApi = { list: (params?: StepListParams) => Promise get: (id: string) => Promise create: (data: StepCreate) => Promise update: (id: string, data: StepUpdate) => Promise delete: (id: string) => Promise search: (query: string) => Promise getPopularTags: () => Promise rate: (id: string, data: RatingCreate) => Promise updateRating: (id: string, data: RatingUpdate) => Promise deleteRating: (id: string) => Promise getReviews: (id: string) => Promise } ``` #### B.2: Create Step Categories API Client **File:** `frontend/src/api/stepCategories.ts` (NEW) ```typescript export const stepCategoriesApi = { list: () => Promise get: (id: string) => Promise } ``` #### B.3: Add TypeScript Types **File:** `frontend/src/types/step.ts` (NEW) ```typescript export interface StepCommand { label: string command: string command_type?: string } export interface StepContent { instructions: string help_text?: string commands?: StepCommand[] } export interface Step { id: string title: string step_type: 'decision' | 'action' | 'solution' content: StepContent visibility: 'private' | 'team' | 'public' category_id?: string category_name?: string tags: string[] usage_count: number rating_average: number rating_count: number helpful_yes: number helpful_no: number is_featured: boolean is_verified: boolean created_by: string author_name?: string created_at: string updated_at: string } export interface StepListItem { id: string title: string step_type: string visibility: string category_id?: string category_name?: string tags: string[] usage_count: number rating_average: number rating_count: number is_featured: boolean created_by: string author_name?: string created_at: string } export interface StepCategory { id: string name: string description?: string display_order: number team_id?: string is_active: boolean } export interface StepListParams { visibility?: 'private' | 'team' | 'public' category_id?: string tags?: string[] min_rating?: number step_type?: 'decision' | 'action' | 'solution' sort_by?: 'recent' | 'popular' | 'highest_rated' | 'most_used' limit?: number offset?: number } export interface PopularTag { tag: string count: number } ``` #### B.4: Create Step Card Component **File:** `frontend/src/components/step-library/StepCard.tsx` (NEW) A card displaying: - Step title and type badge (decision/action/solution) - Category name - Star rating (if rated) or "Not rated" placeholder - Tags as chips (max 3 visible, "+N more" overflow) - Author name and created date - Usage count - [Preview] and [Insert] buttons #### B.5: Create Step Detail Modal **File:** `frontend/src/components/step-library/StepDetailModal.tsx` (NEW) Full step preview showing: - Title, type, category, tags - Full rating breakdown (star distribution chart) - Instructions (markdown rendered) - Commands (copyable code blocks) - Help text - Top reviews (2-3 shown, "See all" link) - [Cancel] and [Insert Into Session] buttons #### B.6: Create Step Library Browser Component **File:** `frontend/src/components/step-library/StepLibraryBrowser.tsx` (NEW) Main browser component with: **Header:** - Search input (full-text, debounced 300ms) - Filter dropdowns: Category, Type, Min Rating, Sort By - Popular tags as clickable chips **Body:** - Grouped sections: "My Steps", "Team Steps", "Community" - Each section collapsible - Virtualized list for performance (if >50 items) - Loading skeletons while fetching - Empty state: "No steps found. Create your first step!" **Footer:** - [+ Create New Step] button (optional, for standalone use) **Props:** ```typescript interface StepLibraryBrowserProps { onInsert: (step: Step) => void onCreateNew?: () => void showCreateButton?: boolean } ``` #### B.7: Update API Index **File:** `frontend/src/api/index.ts` Add exports for `stepsApi` and `stepCategoriesApi`. ### Acceptance Criteria - Issue #10 - [ ] Can search steps with full-text query - [ ] Can filter by category, type, minimum rating - [ ] Can sort by recent, popular, highest rated, most used - [ ] Steps grouped by visibility (My Steps, Team, Community) - [ ] Can click step to see full preview modal - [ ] Can copy commands from preview - [ ] Can click "Insert" to select a step - [ ] Popular tags shown and clickable as quick filters --- ### Issue #9: Custom Step Creation Modal #### B.8: Create Step Form Component **File:** `frontend/src/components/step-library/StepForm.tsx` (NEW) Form fields: - Step Type: Radio group (Decision / Action / Solution) - Title: Text input (required) - Instructions: Textarea with markdown support (required) - Help Text: Textarea (optional) - Commands: Dynamic array field (optional) - Each command: label + command + type dropdown - Category: Dropdown (optional) - Tags: Tag input with autocomplete (optional) - Visibility: Dropdown (Private / Team / Public) - Checkbox: "Save to My Step Library for reuse" #### B.9: Create Custom Step Modal **File:** `frontend/src/components/step-library/CustomStepModal.tsx` (NEW) Tabbed modal with: - **Tab 1: "Type My Own"** - StepForm component - **Tab 2: "Browse Library"** - StepLibraryBrowser component **Props:** ```typescript interface CustomStepModalProps { isOpen: boolean onClose: () => void onInsertStep: (step: Step | CustomStepDraft) => void } ``` **Behavior:** - Tab 1: User fills form, clicks [Insert Step] - If "Save to library" checked, POST to `/api/v1/steps` first - Returns step data to parent - Tab 2: User browses, clicks Insert on a step - Returns selected step to parent ### Acceptance Criteria - Issue #9 - [ ] Modal has two tabs: "Type My Own" and "Browse Library" - [ ] Can create custom step with type, title, instructions - [ ] Can optionally add commands, help text, category, tags - [ ] Can optionally save step to personal library - [ ] Can switch to Browse tab and select existing step - [ ] Insert button returns step data to parent component --- ### Issue #8: Add Custom Step Button in Tree Navigation #### B.10: Add Custom Steps Backend Support **Files:** - `backend/alembic/versions/XXXX_add_custom_steps_to_sessions.py` (NEW - migration) - `backend/app/schemas/session.py` (MODIFIED) - `backend/app/api/endpoints/sessions.py` (MODIFIED) **Migration:** ```python def upgrade(): # Add custom_steps JSONB column to sessions table op.add_column('sessions', sa.Column('custom_steps', JSONB, nullable=False, server_default='[]') ) def downgrade(): op.drop_column('sessions', 'custom_steps') ``` **Schema Updates:** ```python # In DecisionRecord - no changes needed (still uses node_id) # In SessionResponse - add custom_steps field class SessionResponse(BaseModel): # ... existing fields custom_steps: list[dict[str, Any]] = Field(default_factory=list) # NEW # In SessionUpdate - add custom_steps field class SessionUpdate(BaseModel): # ... existing fields custom_steps: Optional[list[dict[str, Any]]] = None # NEW ``` **Custom Step Structure in Database:** ```python # Each item in custom_steps array: { "id": "uuid-string", "inserted_after_node_id": "parent-node-id", "step_data": { "title": "Check Additional Logs", "step_type": "action", # decision | action | solution "content": { "instructions": "Check /var/log/...", "help_text": "Optional help", "commands": [...] # optional }, "category_id": "optional-uuid", "tags": ["optional"] }, "timestamp": "2026-02-03T10:30:00Z" } ``` **API Logic:** - `PUT /api/v1/sessions/{id}` - Accept `custom_steps` in request body - Validation: Ensure `step_data.step_type` is valid enum value - No cascade delete needed - custom steps are session-scoped #### B.11: Modify TreeNavigationPage **File:** `frontend/src/pages/TreeNavigationPage.tsx` Add state: ```typescript const [showCustomStepModal, setShowCustomStepModal] = useState(false) const [customSteps, setCustomSteps] = useState([]) ``` **UI Changes:** 1. After each decision node's options, add: ```tsx ``` 2. Add CustomStepModal at bottom of component 3. Handle insert: ```typescript const handleInsertCustomStep = (step: Step | CustomStepDraft) => { // Create custom step object const customStep: CustomStep = { id: crypto.randomUUID(), inserted_after_node_id: currentNodeId, step_data: step, timestamp: new Date().toISOString() } // Add to local state const newCustomSteps = [...customSteps, customStep] setCustomSteps(newCustomSteps) // Navigate to custom step (becomes current) setCurrentNodeId(customStep.id) setShowCustomStepModal(false) // Save to backend if (session) { sessionsApi.update(session.id, { custom_steps: newCustomSteps }).catch(err => console.error('Failed to save custom step:', err)) } } ``` 4. Render custom steps based on type: ```tsx {/* Render custom step in navigation flow */} {currentNode?.id.startsWith('custom-') && (
{/* Custom Step Badge */} Custom Step {/* Render based on step type */} {customStepNode.step_data.step_type === 'decision' && ( <>

{customStepNode.step_data.title}

{/* Render decision options - user creates them inline or uses predefined */}
)} {customStepNode.step_data.step_type === 'action' && ( <>

{customStepNode.step_data.title}

{customStepNode.step_data.content.commands?.map(cmd => (
{cmd.command}
))} )} {customStepNode.step_data.step_type === 'solution' && ( <>

{customStepNode.step_data.title}

)}
)} ``` **Navigation Logic:** - Custom decision: User selects option → continues to next tree node or another custom step - Custom action: User clicks Continue → returns to tree flow (next_node_id of original tree node) - Custom solution: User clicks Complete → ends session #### B.12: Update Session Export **File:** `frontend/src/components/session/ExportPreviewModal.tsx` or `backend/app/api/endpoints/sessions.py` Ensure custom steps are included in export with clear marking: ```markdown ## Step 5: [CUSTOM] Check Additional Logs *Custom step added by user* Instructions: ... ``` #### B.13: Update Session Types **File:** `frontend/src/types/index.ts` Add: ```typescript export interface CustomStep { id: string inserted_after_node_id: string step_data: Step | CustomStepDraft timestamp: string } export interface CustomStepDraft { title: string step_type: 'decision' | 'action' | 'solution' content: StepContent category_id?: string tags?: string[] } ``` ### Acceptance Criteria - Issue #8 - [ ] "+ Add Custom Step" button visible at each decision point - [ ] Button opens CustomStepModal - [ ] Can insert step from "Type My Own" tab - [ ] Can insert step from "Browse Library" tab - [ ] Custom steps appear in session flow with visual indicator - [ ] Custom steps included in session export - [ ] Session continues after custom step --- ## File Summary ### New Files (10) | File | Issue | Description | |------|-------|-------------| | `frontend/components/tree-editor/ValidationSummary.tsx` | #1 | Error/warning display | | `frontend/api/steps.ts` | #10 | Steps API client | | `frontend/api/stepCategories.ts` | #10 | Categories API client | | `frontend/types/step.ts` | #10 | Step TypeScript types | | `frontend/components/step-library/StepCard.tsx` | #10 | Step list item card | | `frontend/components/step-library/StepDetailModal.tsx` | #10 | Step preview modal | | `frontend/components/step-library/StepLibraryBrowser.tsx` | #10 | Main browser component | | `frontend/components/step-library/StepForm.tsx` | #9 | Step creation form | | `frontend/components/step-library/CustomStepModal.tsx` | #9 | Tabbed modal wrapper | | `backend/alembic/versions/XXXX_add_custom_steps.py` | #8 | Add custom_steps to sessions | ### Modified Files (6) | File | Issue | Changes | |------|-------|---------| | `frontend/store/treeEditorStore.ts` | #1 | Add circular reference detection | | `frontend/pages/TreeEditorPage.tsx` | #1 | Integrate validation UI | | `frontend/components/tree-editor/NodeList.tsx` | #1 | Node error indicators | | `frontend/pages/TreeNavigationPage.tsx` | #8 | Add custom step button + rendering | | `frontend/api/index.ts` | #10 | Export new API clients | | `frontend/types/index.ts` | #8 | Add CustomStep types | | `backend/app/schemas/session.py` | #8 | Add custom_steps field | | `backend/app/api/endpoints/sessions.py` | #8 | Handle custom_steps in update | --- ## Execution Plan ### Phase 1: Foundation (Do First) ``` Workstream A Workstream B ───────────── ───────────── A.1 Circular ref detection B.1 Steps API client B.2 Categories API client B.3 TypeScript types ``` ### Phase 2: Core Components ``` Workstream A Workstream B ───────────── ───────────── A.2 ValidationSummary B.4 StepCard A.3 TreeEditorPage integration B.5 StepDetailModal A.4 NodeList indicators B.6 StepLibraryBrowser ``` ### Phase 3: Integration ``` Workstream A Workstream B ───────────── ───────────── Testing & polish B.8 StepForm B.9 CustomStepModal B.10 Backend custom steps support (migration + schemas) B.11 TreeNavigationPage integration B.12-13 Export & types updates ``` --- ## Testing Checklist ### Workstream A - Validation - [ ] Create tree without name → Error shown, save blocked - [ ] Create tree without solution → Error shown, save blocked - [ ] Create decision without options → Error shown - [ ] Create circular reference (A → B → A) → Error detected - [ ] Create orphan node → Warning shown (can still save) - [ ] Click error → Node selected - [ ] Fix all errors → Save enabled ### Workstream B - Step Library - [ ] Open browser → Steps load, grouped by visibility - [ ] Search "citrix" → Filtered results appear - [ ] Filter by category → Results filtered - [ ] Click step → Preview modal opens - [ ] Click copy command → Copied to clipboard - [ ] Click Insert → Modal closes, step returned - [ ] Create custom step → Form validates, step inserted - [ ] Save to library checked → Step saved to API - [ ] Add custom step in navigation → Step appears with badge - [ ] Complete session → Export includes custom steps --- ## Notes for Implementation 1. **Use existing patterns**: Follow Modal.tsx, TagInput.tsx, and existing page structures 2. **Dark mode**: All components must support light/dark themes via Tailwind classes 3. **Keyboard navigation**: Support Escape to close modals, Enter to submit 4. **Loading states**: Show skeletons or spinners during API calls 5. **Error handling**: Display user-friendly error messages, log details to console 6. **Accessibility**: Use proper ARIA labels, maintain focus management in modals --- ## Design Decisions (Resolved) ### 1. Custom Steps Persistence ✅ **Decision:** Separate `custom_steps` JSONB field in sessions table **Rationale:** - Clean separation of concerns (decisions track actions taken, custom_steps store step content) - Enables session resuming with custom steps intact - Easier to query and export custom steps separately - Follows normalized data structure patterns **Implementation:** Task B.10 adds migration + schema updates ### 2. Custom Step Navigation Flow ✅ **Decision:** Full step type support (decision/action/solution) **Rationale:** - Real-world troubleshooting requires branching after custom actions - Example: "Try clearing cache" (action) → "Did it work?" (decision) - Consistent with tree structure mental model - Maximum flexibility for engineers **Implementation:** Task B.11 renders custom steps based on `step_type` ### 3. Validation Warning UX ✅ **Decision:** Inline dismissible warnings, no confirmation modal **Rationale:** - Warnings are informational, not critical - Orphan nodes might be intentional (work in progress) - Trust users to manage their own tree quality - Simpler UX, fewer clicks - Draft feature (Issue #25) handles "save incomplete work" use case **Implementation:** Task A.2-A.3 show warnings in ValidationSummary, don't block save --- ## Future Enhancements (Out of Scope) 1. **Draft Trees Feature** - Documented in Issue #25 - Save incomplete trees without validation errors blocking - Planned for Phase 3 after Step Library is complete 2. **Standalone Step Library Page** - `/steps` route - Browse/manage personal step library outside of active sessions - Add after Issue #10 is complete and usage patterns are understood 3. **Rate Limiting** - Backend concern - Prevent step library spam - Add if abuse is observed in production