# Feature Design: Draft Trees & Custom Steps > **Date:** February 3, 2026 > **Status:** Planned for Phase 3 > **Related Issues:** TBD > **Dependencies:** Tree Editor Validation UI (Issue #1) --- ## Overview Enable users to save incomplete trees and custom steps as drafts, allowing them to return later to finish editing without validation errors blocking their work. **Use Cases:** - Building a complex tree over multiple sessions - Starting a tree without all solution nodes defined - Experimenting with tree structures before publishing - Saving custom steps for later refinement --- ## Motivation Currently, validation errors block saving trees. This creates friction when: - User wants to save progress on a complex tree (10+ nodes) - User is interrupted mid-editing and needs to save incomplete work - User wants to experiment without committing to a "valid" structure - User creates a custom step during troubleshooting but wants to refine it later **Goal:** Allow users to save work-in-progress without bypassing quality checks for published trees. --- ## Design ### Database Changes #### Trees Table Add `status` column to `trees` table: ```sql ALTER TABLE trees ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT 'published'; ALTER TABLE trees ADD CONSTRAINT trees_status_check CHECK (status IN ('draft', 'published')); CREATE INDEX idx_trees_status ON trees(status); ``` **Statuses:** - `draft` - Incomplete, may have validation errors, only visible to author - `published` - Complete, passes validation, visible per sharing settings #### Step Library Table Add `status` column to `step_library` table: ```sql ALTER TABLE step_library ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT 'published'; ALTER TABLE step_library ADD CONSTRAINT step_library_status_check CHECK (status IN ('draft', 'published')); CREATE INDEX idx_step_library_status ON step_library(status); ``` --- ## API Changes ### Trees Endpoints #### GET /api/v1/trees Add query parameter: ```python @router.get("/") async def list_trees( include_drafts: bool = False, # NEW category_id: Optional[UUID] = None, tags: Optional[str] = None, # ... existing params ): """ List trees. By default, only returns published trees. Set include_drafts=true to include user's own draft trees. """ ``` **Logic:** - Default: Only return `status='published'` trees - `include_drafts=true`: Return published trees + current user's drafts - Never show other users' drafts #### POST /api/v1/trees ```python class TreeCreate(BaseModel): name: str description: Optional[str] = None tree_structure: dict status: str = "published" # NEW: default to published # ... existing fields ``` **Validation:** - `status='draft'`: Skip validation, allow saving with errors - `status='published'`: Run full validation, reject if errors exist #### PUT /api/v1/trees/{id} ```python class TreeUpdate(BaseModel): name: Optional[str] = None tree_structure: Optional[dict] = None status: Optional[str] = None # NEW: allow status change # ... existing fields ``` **Validation:** - Changing `draft` → `published`: Run validation, reject if errors - Changing `published` → `draft`: Allow without validation - Updating draft: Skip validation - Updating published: Run validation #### GET /api/v1/trees/{id}/can-publish ```python @router.get("/{id}/can-publish") async def can_publish_tree(id: UUID) -> dict: """ Check if a draft tree can be published. Returns: { "can_publish": bool, "errors": ValidationError[], "warnings": ValidationError[] } """ ``` **Use case:** Frontend calls this before showing "Publish" button to preview errors. ### Step Library Endpoints Same pattern as trees: - `GET /api/v1/steps?include_drafts=true` - `POST /api/v1/steps` with `status` field - `PUT /api/v1/steps/{id}` with status change validation - `GET /api/v1/steps/{id}/can-publish` --- ## Frontend Changes ### Tree Library Page **Visual Changes:** ```tsx // Draft badge on tree cards {tree.status === 'draft' && ( Draft )} // Filter toggle ``` **Default:** Only show published trees **With "Show my drafts" enabled:** Show published + user's drafts ### Tree Editor Page **Save Button Logic:** ```tsx const { canSave, validationErrors, validationWarnings } = useValidation() const isDraft = tree.status === 'draft' // Two-button layout when draft has errors {isDraft && validationErrors.length > 0 ? ( <> ) : ( )} ``` **Validation Display:** ```tsx // Show validation summary for drafts {isDraft && ( )} // Show validation summary for published (blocks save) {!isDraft && validationErrors.length > 0 && ( )} ``` **Status Badge in Editor:** ```tsx

{tree.name}

{tree.status === 'draft' && ( Draft )}
``` ### Tree Navigation Page **Draft trees behavior:** - Can be selected and used for navigation - Show warning banner: "⚠️ This is a draft tree and may be incomplete" - Allow session creation (useful for testing draft trees) ### Step Library Browser **Draft custom steps:** ```tsx // In CustomStepModal, add checkbox: // In StepLibraryBrowser, filter control: ``` --- ## User Flows ### Flow 1: Save Draft Tree 1. User creates new tree, clicks "Create Tree" 2. Tree Editor opens, user adds nodes 3. User clicks "Save Draft" (or just "Save" if creating as draft from start) 4. Validation runs but doesn't block—tree saved with `status='draft'` 5. Success message: "Draft saved. Publish when ready." ### Flow 2: Publish Draft Tree 1. User opens draft tree in editor 2. ValidationSummary shows errors/warnings 3. User fixes all errors 4. "Publish" button becomes enabled 5. User clicks "Publish" 6. Tree status changes to `published` 7. Success message: "Tree published and available to team" ### Flow 3: Unpublish Tree 1. User opens published tree 2. Clicks "Convert to Draft" (in dropdown menu) 3. Confirmation modal: "This will hide the tree from others. Continue?" 4. Tree status changes to `draft` 5. Tree removed from other users' tree library view ### Flow 4: Save Draft Custom Step 1. User adds custom step during navigation 2. In CustomStepModal, checks "Save as draft" 3. Step saved to personal library with `status='draft'` 4. Step inserted into current session (works like published step) 5. Later, user opens "My Steps" page, refines draft, publishes --- ## Validation Rules ### Draft Trees - ✅ Can save with missing required fields - ✅ Can save with orphan nodes - ✅ Can save with circular references - ✅ Can save without solution nodes - ❌ Still validate JSONB structure (prevent corrupted data) ### Published Trees - ❌ Cannot save with any validation errors - ⚠️ Can save with warnings (orphan nodes, etc.) - ✅ Must have at least one solution node - ✅ Must have valid tree_structure ### Publishing Transition - When `draft` → `published`: Run full validation, reject if errors - Show clear error message: "Cannot publish: 3 errors found. [View Details]" --- ## UI Mockup Descriptions ### Tree Library Page ``` ┌─────────────────────────────────────────────────┐ │ Tree Library │ │ │ │ [Search...] [Category ▼] [+ New Tree] │ │ │ │ ☑ Show my drafts │ │ │ │ ┌─────────────────────────────────┐ │ │ │ Citrix Connection Issues [DRAFT]│ │ │ │ Last edited: 2 hours ago │ │ │ │ 5 nodes · 2 errors │ │ │ │ [Continue Editing] │ │ │ └─────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────┐ │ │ │ Outlook Won't Start │ │ │ │ Last used: Yesterday │ │ │ │ 12 nodes · Published │ │ │ │ [Start Session] │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────────────────┘ ``` ### Tree Editor - Draft Mode ``` ┌─────────────────────────────────────────────────┐ │ Citrix Connection Issues [DRAFT] │ │ │ │ ⚠ Validation (2 errors, 1 warning) │ │ ├─ ❌ Tree must have at least one solution node│ │ ├─ ❌ Node "Check firewall" is orphaned │ │ └─ ⚠ Node "Reboot" has no help text │ │ │ │ [Node editing area...] │ │ │ │ [Cancel] [Save Draft] [Publish] ← disabled │ └─────────────────────────────────────────────────┘ ``` ### Tree Editor - Ready to Publish ``` ┌─────────────────────────────────────────────────┐ │ Citrix Connection Issues [DRAFT] │ │ │ │ ✅ No validation errors │ │ ⚠ 1 warning: Node "Reboot" has no help text │ │ │ │ [Node editing area...] │ │ │ │ [Cancel] [Save Draft] [Publish] ← enabled │ └─────────────────────────────────────────────────┘ ``` --- ## Implementation Phases ### Phase 1: Backend Foundation - [ ] Add `status` column to `trees` table - [ ] Update Trees API endpoints (list, create, update) - [ ] Add `can_publish` endpoint - [ ] Update validation logic to respect status - [ ] Write tests for draft/publish transitions ### Phase 2: Frontend - Trees - [ ] Update Tree Library to filter by status - [ ] Add "Show my drafts" toggle - [ ] Update Tree Editor save button logic - [ ] Add "Publish" button for drafts - [ ] Add status badge to tree cards and editor - [ ] Add confirmation modal for unpublishing ### Phase 3: Backend - Step Library - [ ] Add `status` column to `step_library` table - [ ] Update Step Library API endpoints - [ ] Add `can_publish` endpoint for steps - [ ] Write tests ### Phase 4: Frontend - Step Library - [ ] Update CustomStepModal with draft option - [ ] Update StepLibraryBrowser to filter drafts - [ ] Add "Publish" action to step detail modal - [ ] Add status badge to step cards --- ## Testing Checklist ### Trees - [ ] Create draft tree with validation errors → saves successfully - [ ] Try to publish draft with errors → rejected with clear message - [ ] Fix errors, publish draft → becomes published - [ ] Edit published tree, introduce error → cannot save - [ ] Convert published tree to draft → hidden from others - [ ] Other users cannot see my draft trees - [ ] Draft trees show in "My Trees" when filter enabled ### Step Library - [ ] Save custom step as draft → appears in "My Steps" with badge - [ ] Draft steps not shown in team/public views - [ ] Publish draft step → validation runs - [ ] Draft step can be inserted into session (works like published) - [ ] Edit draft step, publish when ready ### Edge Cases - [ ] Create draft → close browser → reopen → draft still there - [ ] Two users editing same tree: User A drafts, User B can't see draft - [ ] Published tree with 100 uses → convert to draft → sessions still work - [ ] Delete draft tree → no orphaned sessions --- ## Open Questions 1. **Auto-save for drafts?** - Should drafts auto-save every N seconds like Google Docs? - Recommendation: Phase 5 enhancement, manual save for now 2. **Draft expiration?** - Should drafts older than 30 days be auto-deleted? - Recommendation: No expiration for now, add later if storage becomes issue 3. **Version history for drafts?** - Should we track versions of draft edits? - Recommendation: Out of scope, add with general version control feature later 4. **Team drafts?** - Should teams be able to collaborate on draft trees? - Recommendation: Phase 6 - "shared drafts" with permissions --- ## Migration Plan ### Database Migration ```python # Migration: add_tree_status_column def upgrade(): # Add column with default 'published' for existing trees op.add_column('trees', sa.Column('status', sa.String(20), nullable=False, server_default='published')) op.create_check_constraint('trees_status_check', 'trees', "status IN ('draft', 'published')") op.create_index('idx_trees_status', 'trees', ['status']) # Same for step_library op.add_column('step_library', sa.Column('status', sa.String(20), nullable=False, server_default='published')) op.create_check_constraint('step_library_status_check', 'step_library', "status IN ('draft', 'published')") op.create_index('idx_step_library_status', 'step_library', ['status']) ``` **Rollback safety:** All existing trees default to `published`, no data loss. --- ## Success Metrics - **Adoption:** % of users who create at least one draft tree per month - **Completion:** % of drafts that get published (vs abandoned) - **Time savings:** Avg time to create complex trees (before/after draft feature) - **Error reduction:** % reduction in "cannot save" frustration incidents **Target:** 60% of users with 5+ trees use draft feature within 2 months of launch. --- ## Related Features - **Tree Editor Validation** (Issue #1) - Prerequisite - **Step Library Browser** (Issue #10) - Will benefit from draft steps - **Tree Forking** (Issue #13) - Forked trees could start as drafts - **Tree Sharing** (Issue #16) - Published status required to share --- ## Notes - Draft feature inspired by Gmail drafts, Google Docs, Notion page publishing - Key principle: **Never lose work** - always allow saving, validate on publish - This feature enables iterative tree building, which is critical for complex MSP workflows