Files
resolutionflow/docs/plans/2026-03-10-flexible-intake-design.md
chihlasm 4727106141 fix: race condition hardening across auth, counters, and data fetching (#102)
* fix: prevent race conditions in token operations and auth flows

Backend:
- Refresh token rotation: use atomic UPDATE...WHERE revoked_at IS NULL
  to prevent concurrent refresh requests from both succeeding
- Account invite codes: SELECT FOR UPDATE to prevent double-spend
- Platform invite codes: SELECT FOR UPDATE to prevent double-spend
- Password reset tokens: SELECT FOR UPDATE to prevent double-use
- Email verification tokens: SELECT FOR UPDATE to prevent double-use

Frontend:
- Token refresh subscriber arrays: swap before iterating so a throwing
  callback doesn't leave the queue in a dirty state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: atomic counters, plan limit re-check, and double-submit guard

Backend:
- Tree usage_count: use SQL-level UPDATE (Tree.usage_count + 1) instead
  of Python-level increment to prevent lost updates under concurrency
- Tag usage_count: same SQL-level atomic increment/decrement in both
  create_tree and update_tree (delete_tree already used this pattern)
- Plan tree limit: re-check count after db.flush() to close the TOCTOU
  window where two concurrent creates could both pass the pre-check

Frontend:
- TreeEditorPage: add isSaving early-return guard inside handleSaveDraft
  and handlePublish callbacks so Ctrl+S can't bypass the button disabled
  prop and fire duplicate save requests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: prevent stale API responses from overwriting newer data

- SessionHistoryPage: move loadSessions into effect with cancelled flag
  so rapid filter/tab changes discard outdated responses
- TreeLibraryPage: add request ID ref to loadTrees so stale responses
  from previous filter selections are discarded
- QuickStartPage: add request ID ref to debounced search so out-of-order
  responses don't overwrite newer search results

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add flexible intake design — deferred variables + prepared sessions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 01:57:22 -04:00

7.6 KiB

Plan: Flexible Intake — Deferred Variables + Prepared Sessions

Context

The current intake form on procedural flows is a blocking modal that forces engineers to enter all variables before the flow starts. This creates friction because:

  • Engineers don't always have all the information upfront
  • Information often lives in PSA tickets, RMM tools, or was communicated verbally
  • Sometimes a lead/PM has the info and wants to set up the session for an engineer to execute later

Goal: Replace the blocking intake modal with two complementary workflows:

  1. Deferred Variables — start the flow immediately, fill variables inline as you encounter them
  2. Prepared Sessions — pre-fill variables ahead of time, optionally assign to an engineer, execute later

Design

Workflow 1: Deferred Variables (Start Now, Fill Later)

  • "Start Flow" launches the session immediately — no intake modal
  • Variables begin empty
  • When a step references [VAR:server_name] and it's unfilled, an inline input prompt renders in place — visually prominent with the field's label, help text, and styling that stands out (cyan border, slight glow)
  • Once filled, the value persists and resolves everywhere in the session
  • Engineers can also open a "Session Variables" side panel at any time to see/edit all variables
  • At session completion, if required variables are still empty → soft warning with a prompt to fill them (for complete export documentation), but not a hard block

Workflow 2: Prepared Sessions (Set Up Ahead, Execute Later)

  • From a flow's detail page: "Prepare Session" action opens a form to fill variables + assign an engineer
  • Creates a session in prepared state — started_at is null, variables populated, no steps executed
  • Assignment: Preparer can assign to a specific engineer on their team (or leave unassigned)
  • Personal queue: Engineers see prepared sessions assigned to them in a dedicated section (Quick Start page or Session History tab)
  • Clicking a prepared session opens the flow with variables pre-populated; execution begins normally
  • Unassigned prepared sessions are visible to all team members

Data Model Changes

Session model additions:

  • prepared_by_id — UUID FK to users, nullable. Who created the prepared session.
  • assigned_to_id — UUID FK to users, nullable. Who should execute it.
  • Use existing convention: started_at IS NULL = prepared, started_at IS NOT NULL, completed_at IS NULL = active, completed_at IS NOT NULL = completed

Session variables become mutable:

  • session_variables is currently write-once at session creation
  • New endpoint: PATCH /sessions/{id}/variables — updates individual variables during an active session
  • Only the session owner (or assigned engineer) can update variables

Migration: One migration adding prepared_by_id and assigned_to_id columns with FK constraints.

Variable Resolution Changes

Backend:

  • No changes to export pipeline — it already resolves variables from session_variables
  • New PATCH /sessions/{id}/variables endpoint accepts partial variable updates
  • Session creation no longer validates required intake fields (they can be filled later)

Frontend — resolveVariables() in lib/variableResolver.ts:

  • Currently returns a plain string with [VAR:x] replaced
  • New behavior: also identify unresolved variables so StepDetail can render inline prompts

Frontend — StepDetail.tsx:

  • When rendering step content, unresolved [VAR:x] references render as inline input components
  • Inline prompt design: input field with the field's label as placeholder, cyan border, subtle glow background to make them visually prominent and easy to spot
  • On blur/enter: calls PATCH /sessions/{id}/variables → re-renders step with resolved value
  • Lookup field metadata (label, field_type, help_text, options) from the intake form definition in the tree snapshot

Frontend — Session Variables Panel:

  • Existing "View Parameters" button becomes "Session Variables" — now editable
  • Shows all intake form fields with filled/unfilled status
  • Unfilled required fields highlighted
  • Editing a field here updates the session and re-resolves all visible steps

API Changes

Method Endpoint Description
PATCH /sessions/{id}/variables Update one or more session variables (partial dict merge)
POST /sessions Remove required-field validation for intake forms (allow empty start)
GET /sessions Add assigned_to_id and status=prepared filter params
POST /sessions/prepare New endpoint: create a prepared session with variables + optional assignee

UI Changes

Location Change
Flow detail page "Start Flow" no longer shows intake modal. Add "Prepare Session" option (dropdown or secondary button)
ProceduralNavigationPage Remove IntakeFormModal gating. Add "Session Variables" panel button. Inline prompts on steps with unfilled variables
StepDetail Render inline input prompts for unresolved [VAR:x] references
Quick Start page New "Prepared for You" section showing assigned prepared sessions
Session History New "Prepared" tab/filter showing prepared sessions
Prepare Session form New modal/page: select flow, fill variables, assign engineer, save
Session completion Soft warning if required variables still empty

What Gets Removed

  • IntakeFormModal.tsx — no longer used as a blocking gate (may repurpose as the "Prepare Session" form)
  • Required-field validation in POST /sessions for intake form fields
  • The showIntakeForm / intake modal state in ProceduralNavigationPage

Implementation Phases

Phase 1: Mutable Variables + Inline Prompts

Files: sessions.py, variableResolver.ts, StepDetail.tsx, ProceduralNavigationPage.tsx

  1. Add PATCH /sessions/{id}/variables endpoint
  2. Remove intake form required-field blocking from POST /sessions
  3. Update resolveVariables() to identify unresolved variables
  4. Build inline variable prompt component for StepDetail
  5. Make "View Parameters" panel editable
  6. Remove IntakeFormModal gating from ProceduralNavigationPage

Phase 2: Prepared Sessions

Files: sessions.py, session.py (schemas), migration, PrepareSessionModal.tsx, QuickStartPage.tsx, SessionHistoryPage.tsx

  1. Migration: add prepared_by_id, assigned_to_id to sessions table
  2. POST /sessions/prepare endpoint
  3. GET /sessions filter support for assigned_to_id and prepared status
  4. Prepare Session modal/form (reuse IntakeFormModal field rendering)
  5. "Prepared for You" section on Quick Start
  6. "Prepared" filter on Session History

Phase 3: Polish

  1. Soft completion warning for unfilled required variables
  2. Prepared session staleness indicator (optional)
  3. Notification when a session is prepared/assigned to you (optional, future)

Verification

  • Start a procedural flow without filling any variables → flow starts immediately, no modal
  • Navigate to a step with [VAR:server_name] → see inline input prompt
  • Fill the variable inline → value resolves across all steps
  • Open Session Variables panel → see all fields, edit one → reflected in steps
  • Prepare a session from flow detail page → assign to another engineer
  • Log in as assigned engineer → see prepared session in Quick Start queue
  • Click prepared session → flow opens with variables pre-filled, execute normally
  • Complete a session with one unfilled required variable → see soft warning
  • Export session → variables resolved in output, unfilled ones show as [VAR:x] or blank