Skip to content

Visual scenario representation: render the inject DAG, with a path-taken overlay for debriefs #206

Description

@richardmhope

Summary

A scenario is a branching DAG, but nowhere in the app can you see it as one. scenarios/detail.html renders a depth-first text tree and the editor edits nodes one at a time; a reviewer has to rebuild the shape of the exercise in their head. Render the inject graph visually — and, post-exercise, overlay the path each team actually took.

Two views, one renderer

  1. Authoring/review (scenario detail + editor): nodes = injects, edges = per-option next_inject_id branches plus node-level next_inject_id linear chains (both edge kinds already live in _check_no_cycles's adjacency graph — reuse that traversal, don't re-derive it). Show option labels on edges, team targets as the existing team-scent pills, and validation errors on the offending node — the validation sidebar already computes these.
  2. Debrief overlay (report/review pages): dim the full graph and highlight the edges each team traversed, per the response records and ExerciseStateTransition timeline. "The comms team never saw the containment branch" becomes visible at a glance — this is the piece that upgrades the report page into a hotwash tool.

Rendering constraints (the hard part)

  • Strict CSP (script-src 'self', no unsafe-eval) rules out CDN-loaded graph libraries, and most off-the-shelf ones (mermaid, cytoscape) are heavyweight for this. _check_no_cycles guarantees the graph is a DAG, so a hand-rolled layered layout (longest-path layering + simple barycenter ordering) rendered as inline SVG is feasible and keeps us dependency-free. If a library is genuinely needed, it must be vendored and version-pinned like the Alpine CSP build.
  • The renderer must be a CSP-safe Alpine component (Alpine.data registry file under static/js/pages/), server data passed as |tojson factory args — no optional chaining or arrow functions in directive attributes.
  • Sample scenarios are small (7 injects) but don't assume that: cap node label length, scroll/pan inside an overflow container rather than growing the page.

Not in scope

  • Graph-based editing (drag to rewire). Read-only first; the tree editor remains the write path.
  • Replay animation. A static path overlay is the deliverable; time-scrubbing can build on it later if wanted.

Prior art in-repo

  • scenarios/detail.html depth-first tree + validation sidebar (data source for view 1)
  • exercises/report.html / review.html + build_report (data source for view 2)
  • _check_no_cycles in app/schemas/scenario_json.py (canonical edge set)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions