docs: Refine implementation plan and document draft feature

- 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>
This commit is contained in:
Michael Chihlas
2026-02-03 18:51:27 -05:00
parent 67a98bc25c
commit 4378ec4b20
2 changed files with 744 additions and 46 deletions

View File

@@ -1,9 +1,29 @@
# Implementation Plan: Step Library Frontend + Tree Editor Validation
> **Date:** February 3, 2026
> **Date:** February 3, 2026 (Updated after refinement session)
> **Scope:** Issues #1, #8, #9, #10
> **Estimated Components:** 8 new files, 4 modified files
> **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)
---
@@ -108,10 +128,11 @@ Features:
Modifications:
1. Call `validate()` before save attempt
2. Show ValidationSummary when errors exist
3. Block save if any severity='error' exists (warnings allow save)
4. Add "Validate" button in toolbar for manual check
5. Auto-validate on blur from form fields (debounced)
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`
@@ -123,13 +144,14 @@ Modifications:
### Acceptance Criteria - Workstream A
- [ ] Cannot save tree without a name
- [ ] Cannot save tree without at least one solution node
- [ ] 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
- [ ] Orphan nodes show as warnings (allow save with confirmation)
- [ ] 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
---
@@ -387,7 +409,66 @@ interface CustomStepModalProps {
### Issue #8: Add Custom Step Button in Tree Navigation
#### B.10: Modify TreeNavigationPage
#### 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:
@@ -413,31 +494,83 @@ const [customSteps, setCustomSteps] = useState<CustomStep[]>([])
3. Handle insert:
```typescript
const handleInsertCustomStep = (step: Step | CustomStepDraft) => {
// Insert after current node in session
// Create custom step object
const customStep: CustomStep = {
id: crypto.randomUUID(),
inserted_after_node_id: currentNodeId,
step_data: step,
timestamp: new Date().toISOString()
}
setCustomSteps([...customSteps, customStep])
// 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 in the navigation flow with visual indicator:
4. Render custom steps based on type:
```tsx
{/* Custom Step Badge */}
<span className="rounded-full bg-purple-100 px-2 py-1 text-xs font-medium text-purple-800">
Custom Step
</span>
{/* Render custom step in navigation flow */}
{currentNode?.id.startsWith('custom-') && (
<div className="rounded-lg border border-purple-200 bg-purple-50 p-4 dark:border-purple-800 dark:bg-purple-900/20">
{/* Custom Step Badge */}
<span className="mb-2 inline-block rounded-full bg-purple-100 px-2 py-1 text-xs font-medium text-purple-800 dark:bg-purple-900 dark:text-purple-100">
Custom Step
</span>
{/* Render based on step type */}
{customStepNode.step_data.step_type === 'decision' && (
<>
<h2>{customStepNode.step_data.title}</h2>
<MarkdownContent content={customStepNode.step_data.content.instructions} />
{/* Render decision options - user creates them inline or uses predefined */}
<div className="mt-4 space-y-2">
<button onClick={() => handleCustomDecisionOption('yes')}>Yes</button>
<button onClick={() => handleCustomDecisionOption('no')}>No</button>
</div>
</>
)}
{customStepNode.step_data.step_type === 'action' && (
<>
<h2>{customStepNode.step_data.title}</h2>
<MarkdownContent content={customStepNode.step_data.content.instructions} />
{customStepNode.step_data.content.commands?.map(cmd => (
<pre key={cmd.label}>{cmd.command}</pre>
))}
<button onClick={handleContinueFromCustomAction}>Continue</button>
</>
)}
{customStepNode.step_data.step_type === 'solution' && (
<>
<h2>{customStepNode.step_data.title}</h2>
<MarkdownContent content={customStepNode.step_data.content.instructions} />
<button onClick={handleComplete}>Complete Session</button>
</>
)}
</div>
)}
```
#### B.11: Update Session Export
**File:** `frontend/src/components/session/ExportPreviewModal.tsx`
**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:
@@ -448,7 +581,7 @@ Ensure custom steps are included in export with clear marking:
Instructions: ...
```
#### B.12: Update Session Types
#### B.13: Update Session Types
**File:** `frontend/src/types/index.ts`
Add:
@@ -487,26 +620,29 @@ export interface CustomStepDraft {
| File | Issue | Description |
|------|-------|-------------|
| `components/tree-editor/ValidationSummary.tsx` | #1 | Error/warning display |
| `api/steps.ts` | #10 | Steps API client |
| `api/stepCategories.ts` | #10 | Categories API client |
| `types/step.ts` | #10 | Step TypeScript types |
| `components/step-library/StepCard.tsx` | #10 | Step list item card |
| `components/step-library/StepDetailModal.tsx` | #10 | Step preview modal |
| `components/step-library/StepLibraryBrowser.tsx` | #10 | Main browser component |
| `components/step-library/StepForm.tsx` | #9 | Step creation form |
| `components/step-library/CustomStepModal.tsx` | #9 | Tabbed modal wrapper |
| `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 (5)
### Modified Files (6)
| File | Issue | Changes |
|------|-------|---------|
| `store/treeEditorStore.ts` | #1 | Add circular reference detection |
| `pages/TreeEditorPage.tsx` | #1 | Integrate validation UI |
| `components/tree-editor/NodeList.tsx` | #1 | Node error indicators |
| `pages/TreeNavigationPage.tsx` | #8 | Add custom step button + modal |
| `api/index.ts` | #10 | Export new API clients |
| `types/index.ts` | #8 | Add CustomStep types |
| `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 |
---
@@ -536,8 +672,9 @@ Workstream A Workstream B
───────────── ─────────────
Testing & polish B.8 StepForm
B.9 CustomStepModal
B.10 TreeNavigationPage integration
B.11-12 Export & types updates
B.10 Backend custom steps support (migration + schemas)
B.11 TreeNavigationPage integration
B.12-13 Export & types updates
```
---
@@ -578,13 +715,54 @@ Testing & polish B.8 StepForm
---
## Questions to Resolve Before Starting
## Design Decisions (Resolved)
1. **Custom steps persistence**: Should custom steps be saved to the session in the database, or only exist in frontend state until export?
- *Recommendation*: Save to session.decisions with a `is_custom_step: true` flag
### 1. Custom Steps Persistence
**Decision:** Separate `custom_steps` JSONB field in sessions table
2. **Step library page**: Should there be a standalone `/steps` page for browsing the library outside of navigation?
- *Recommendation*: Yes, add this as a future enhancement after Issue #10
**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
3. **Rate limiting**: Should step creation have rate limiting to prevent spam?
- *Recommendation*: Backend concern, out of scope for this plan
**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

View File

@@ -0,0 +1,520 @@
# 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' && (
<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:**
```tsx
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:**
```tsx
// 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:**
```tsx
<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:**
```tsx
// 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 `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