Files
resolutionflow/docs/archive/2026-02-03-draft-trees-feature.md
chihlasm 350c977eda feat: add procedural flows with intake forms, navigation, and seed templates
Adds a new "procedural" tree type for linear step-by-step project workflows
(domain controller setup, M365 onboarding, VPN config, etc). Includes intake
form builder, two-panel step navigation, variable resolution, procedural
exports, 3 seed templates, and UI rename from "Trees" to "Flows".

Also archives 19 implemented plan docs and creates deferred features backlog.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 04:13:52 -05:00

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 author
  • published - 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 errors
  • status='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 draftpublished: Run validation, reject if errors
  • Changing publisheddraft: 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=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:

// 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

  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 draftpublished: 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

# 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.


  • 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