- Updated IMPLEMENTATION-PLAN-STEP-LIBRARY-FRONTEND.md with design decisions: - Custom steps persistence: separate `custom_steps` field in sessions - Custom step navigation: full step type support (decision/action/solution) - Validation warnings: inline dismissible, no confirmation modal - Added backend migration task (B.10) for custom_steps field - Updated file count: 10 new, 8 modified, 1 migration - Clarified acceptance criteria for validation behavior - Created docs/plans/2026-02-03-draft-trees-feature.md: - Comprehensive design for draft trees and custom steps - Database schema, API changes, frontend UX patterns - Implementation phases and success metrics - Related to Issue #25 (planned for Phase 3) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
16 KiB
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:
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 authorpublished- Complete, passes validation, visible per sharing settings
Step Library Table
Add status column to step_library table:
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:
@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
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 errorsstatus='published': Run full validation, reject if errors exist
PUT /api/v1/trees/{id}
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
@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=truePOST /api/v1/stepswithstatusfieldPUT /api/v1/steps/{id}with status change validationGET /api/v1/steps/{id}/can-publish
Frontend Changes
Tree Library Page
Visual Changes:
// Draft badge on tree cards
{tree.status === 'draft' && (
<span className="rounded-full bg-yellow-100 px-2 py-1 text-xs font-medium text-yellow-800 dark:bg-yellow-900 dark:text-yellow-100">
Draft
</span>
)}
// Filter toggle
<label className="flex items-center gap-2">
<input
type="checkbox"
checked={includeDrafts}
onChange={(e) => setIncludeDrafts(e.target.checked)}
/>
Show my drafts
</label>
Default: Only show published trees With "Show my drafts" enabled: Show published + user's drafts
Tree Editor Page
Save Button Logic:
const { canSave, validationErrors, validationWarnings } = useValidation()
const isDraft = tree.status === 'draft'
// Two-button layout when draft has errors
{isDraft && validationErrors.length > 0 ? (
<>
<button onClick={handleSaveDraft}>
Save Draft
</button>
<button
onClick={handlePublish}
disabled={validationErrors.length > 0}
title={validationErrors.length > 0 ? "Fix errors to publish" : ""}
>
Publish
</button>
</>
) : (
<button onClick={handleSave}>
{isDraft ? 'Save Draft' : 'Save'}
</button>
)}
Validation Display:
// Show validation summary for drafts
{isDraft && (
<ValidationSummary
errors={validationErrors}
warnings={validationWarnings}
mode="draft" // Shows "Fix these to publish" message
/>
)}
// Show validation summary for published (blocks save)
{!isDraft && validationErrors.length > 0 && (
<ValidationSummary
errors={validationErrors}
warnings={validationWarnings}
mode="published" // Shows "Cannot save" message
/>
)}
Status Badge in Editor:
<div className="flex items-center gap-2">
<h1>{tree.name}</h1>
{tree.status === 'draft' && (
<span className="rounded bg-yellow-100 px-2 py-1 text-sm font-medium text-yellow-800">
Draft
</span>
)}
</div>
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:
// In CustomStepModal, add checkbox:
<label className="flex items-center gap-2">
<input
type="checkbox"
checked={saveAsDraft}
onChange={(e) => setSaveAsDraft(e.target.checked)}
/>
Save as draft (you can refine it later)
</label>
// In StepLibraryBrowser, filter control:
<label className="flex items-center gap-2">
<input
type="checkbox"
checked={showDrafts}
onChange={(e) => setShowDrafts(e.target.checked)}
/>
Show my draft steps
</label>
User Flows
Flow 1: Save Draft Tree
- User creates new tree, clicks "Create Tree"
- Tree Editor opens, user adds nodes
- User clicks "Save Draft" (or just "Save" if creating as draft from start)
- Validation runs but doesn't block—tree saved with
status='draft' - Success message: "Draft saved. Publish when ready."
Flow 2: Publish Draft Tree
- User opens draft tree in editor
- ValidationSummary shows errors/warnings
- User fixes all errors
- "Publish" button becomes enabled
- User clicks "Publish"
- Tree status changes to
published - Success message: "Tree published and available to team"
Flow 3: Unpublish Tree
- User opens published tree
- Clicks "Convert to Draft" (in dropdown menu)
- Confirmation modal: "This will hide the tree from others. Continue?"
- Tree status changes to
draft - Tree removed from other users' tree library view
Flow 4: Save Draft Custom Step
- User adds custom step during navigation
- In CustomStepModal, checks "Save as draft"
- Step saved to personal library with
status='draft' - Step inserted into current session (works like published step)
- 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
statuscolumn totreestable - Update Trees API endpoints (list, create, update)
- Add
can_publishendpoint - 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
statuscolumn tostep_librarytable - Update Step Library API endpoints
- Add
can_publishendpoint 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
-
Auto-save for drafts?
- Should drafts auto-save every N seconds like Google Docs?
- Recommendation: Phase 5 enhancement, manual save for now
-
Draft expiration?
- Should drafts older than 30 days be auto-deleted?
- Recommendation: No expiration for now, add later if storage becomes issue
-
Version history for drafts?
- Should we track versions of draft edits?
- Recommendation: Out of scope, add with general version control feature later
-
Team drafts?
- Should teams be able to collaborate on draft trees?
- Recommendation: Phase 6 - "shared drafts" with permissions
Migration Plan
Database Migration
# 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