Use native S-expression for recipe/plan display

- Display recipe's original S-expression when available (code is data)
- Fall back to generating S-expression from plan for legacy JSON
- Run service now prefers .sexp plan files over .json
- Add get_run_plan_sexp() for direct S-expression access

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-01-12 00:26:05 +00:00
parent f554122b07
commit 65a8170192
3 changed files with 35 additions and 9 deletions

View File

@@ -178,6 +178,7 @@ async def get_run(
# Try to load the recipe to show the plan
plan = None
plan_sexp = None # Native S-expression if available
recipe_id = run.get("recipe")
if recipe_id and len(recipe_id) == 64: # Looks like a hash
try:
@@ -185,7 +186,11 @@ async def get_run(
recipe_service = RecipeService(get_redis_client(), get_cache_manager())
recipe = await recipe_service.get_recipe(recipe_id)
if recipe:
# Use the new build_dag method if available
# Use native S-expression if available (code is data!)
if recipe.get("sexp"):
plan_sexp = recipe["sexp"]
# Build steps for DAG visualization
dag = recipe.get("dag", {})
nodes = dag.get("nodes", [])
@@ -311,8 +316,9 @@ async def get_run(
}
})
# Generate S-expression representation of the plan
plan_sexp = plan_to_sexp(plan, run.get("recipe_name"))
# Use native S-expression if available, otherwise generate from plan
if not plan_sexp and plan:
plan_sexp = plan_to_sexp(plan, run.get("recipe_name"))
templates = get_templates(request)
return render(templates, "runs/detail.html", request,