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>
This commit is contained in:
chihlasm
2026-02-14 04:13:52 -05:00
parent 303570ca2c
commit 350c977eda
58 changed files with 11686 additions and 167 deletions

View File

@@ -136,13 +136,27 @@ async def start_session(
detail="You don't have access to this tree"
)
# For procedural trees with intake forms, validate required fields
session_variables = session_data.session_variables or {}
if tree.tree_type == 'procedural' and tree.intake_form:
missing_fields = []
for field in tree.intake_form:
if field.get("required") and not session_variables.get(field["variable_name"]):
missing_fields.append(field["label"])
if missing_fields:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=f"Missing required intake form fields: {', '.join(missing_fields)}"
)
# Create session with tree snapshot (includes tree metadata for filtering/export)
tree_snapshot = {
**tree.tree_structure,
"name": tree.name,
"description": tree.description,
"category": tree.category,
"version": tree.version
"version": tree.version,
"tree_type": tree.tree_type,
}
new_session = Session(
@@ -152,7 +166,8 @@ async def start_session(
path_taken=[],
decisions=[],
ticket_number=session_data.ticket_number,
client_name=session_data.client_name
client_name=session_data.client_name,
session_variables=session_variables,
)
# Increment tree usage count
@@ -421,7 +436,9 @@ async def save_session_as_tree(
can_publish, validation_errors = can_publish_tree(
tree_structure,
tree_name,
request_data.description
request_data.description,
tree_type=original_tree.tree_type,
intake_form=original_tree.intake_form,
)
if not can_publish:
raise HTTPException(