Skip to content

UPDATING.md

Operating manual for docs/models/americanfootball/nfl-flow/. Read this before editing the flowchart.

What this directory is

A vendored, readable visualization of the NFL game simulator state machine in nfl_model_flow_diagram.htm. The HTML is the source of truth (interactive explorer with ~40 states); the flowchart (nfl_sim_flowchart.md) is its derivative — a Mermaid-based diagram set organized into 3 phases (Initialization / Play Execution / Play Aftermath).

The HTML drives; the flowchart follows. When the HTML changes, the flowchart needs to be updated to match.

Directory layout

docs/models/americanfootball/nfl-flow/
├── README.md                       entry point and pointers
├── UPDATING.md                     this file — operating manual
├── CONTEXT.md                      glossary of terms
├── nfl_model_flow_diagram.htm      the source of truth (interactive explorer)
└── rendered/
    ├── nfl_sim_flowchart.md        the flowchart (primary deliverable)
    └── nfl_sim_flowchart.html      interactive companion (pan/zoom single canvas)

Flowchart conventions

These are the rules every sub-diagram in nfl_sim_flowchart.md follows. Internalize them before editing the flowchart.

  1. Strict tree per sub-diagram. No back-edges. No node has multiple inbound edges from within the same diagram (except shared-helper nodes that are themselves trees). The one exception is the Phase Map in section 1, which intentionally shows cycles to communicate orchestration.

Why: Earlier drafts drew the source's back-edges directly (v1: every loop back to Pre-Snap created a hairball in the Drive section), then extracted subroutines but kept back-edges (v2: reduced clutter, didn't eliminate it), then used shared terminal exit nodes (v3: convergence still created visible crossings under Mermaid's auto-layout). Strict trees with duplicated terminal exits are the first approach that renders without crossings. The Phase Map is the single deliberate exception — it restores the cyclic mental model before readers encounter the strict-tree sub-diagrams.

  1. Terminal exit nodes name root destinations. Where flow would loop or jump to another diagram, the diagram ends in a terminal node labeled → DestinationName (e.g., → Drive Cycle). The named destination is always the root of the target sub-diagram — never an internal state within it. When the upstream source transitions directly to a non-root internal state (e.g., a blocked-kick defense recovery entering mid-way through the Punt sub-diagram), expand that state's sub-tree inline at the exit point rather than naming the internal state as the destination.

  2. Terminal nodes are duplicated per leaf; edge labels are never merged. When several branches all exit to the same destination, each branch gets its own copy of the terminal node rather than converging arrows. The same principle applies to edge labels: when a node has multiple distinct outcomes that route to the same destination, keep them as separate labeled edges — do not collapse them into a single edge with a combined label. Duplication is cheap; convergence creates crossings and erases the distinction between transitions.

Why (rules 2–3): The same destination (e.g., → Drive Cycle) may appear five or more times in one sub-diagram — this is intentional. Readability beats DRY for visual diagrams. If an update would naturally introduce a back-edge, express it as a terminal exit instead; this sometimes requires extracting the destination into its own sub-diagram.

  1. Phase context comes from color, not from label. Exit labels are diagram names only (→ Kickoff, not → Kickoff phase). The exit node's fill color signals the destination's phase: green = Play Execution, yellow-bordered = Play Aftermath.

Why: The flowchart uses three causal phases (Initialization / Play Execution / Play Aftermath) rather than the HTML source's five category values (game_setup, kickoff, drive, scoring, game_end). The source's grouping is by game area; the flowchart's grouping is by causal role — what triggers what. The loop structure (cause produces effects, effects trigger new causes) is the more useful mental model for understanding the simulator at a glance. The source's drive category in particular splits across Play Execution (Drive Cycle + 5 play types) and Play Aftermath (Yards Selector, Post-Play Pipeline, Loose-Ball Recovery) — mirroring it would produce a Drive section that reads as a dumping ground.

  1. Elision rule. States from the HTML with exactly one outgoing transition and no distinct routing logic are elided from the diagrams — the source edge label carries the event name and the diagram jumps directly to the terminal exit. States with two or more outgoing transitions are always shown explicitly, even when all routes lead to the same next state — the distinction between outcomes carries routing information.

Why: Single-outcome pass-through states add no routing information to a diagram — they are transit nodes whose only contribution is a label, which the incoming edge already carries. Showing them would inflate the diagram without aiding comprehension. States with multiple outcomes are always shown because the choice between those outcomes is itself the routing information; collapsing them into an edge label would erase a decision point. A separate Scoring phase, for example, would contain only single-outcome transit states — no decisions, no branching, no resolution logic — so those states are expressed as exit labels on the play diagrams that precede them. PAT, which has distinct branching, is its own sub-diagram.

  1. Source category appears as italic metadata. Each sub-section heading shows the source's category field — e.g., #### 3b.1. Pass — *source category:drive*. This lets a reader cross-reference the simulator's code. Source category is for orientation, not for organizing the doc.

When to regenerate

Run the /regen-nfl-flowchart Claude Code slash command whenever nfl_model_flow_diagram.htm changes. The command reads the conventions from this file and from CONTEXT.md, parses the upstream STATES object, and rewrites the two downstream artefacts minimally where the HTML has not changed:

  • rendered/nfl_sim_flowchart.md — the Mermaid markdown flowchart.
  • The STATES inline data block inside rendered/nfl_sim_flowchart.html — the interactive companion.

If you have a new HTML version on disk, pass its path as an argument and the command will copy it into place before regenerating:

/regen-nfl-flowchart path/to/new_nfl_model_flow_diagram.htm

After regenerating, the command warns about any state IDs present in the upstream HTML that are not yet assigned to a SUB_DIAGRAMS.states list in rendered/nfl_sim_flowchart.html. New states must be placed manually — add the state to STATES (slimmed shape: title, category, outcomes with label + next only) and append its id to the correct sub-diagram's states list. All node coordinates are computed automatically at render time; there is no hand-authored layout object to maintain.

The manual steps in the sections below remain the reference for understanding what the command does and for structural changes (new phases, back-edges, new sub-diagrams) that require a judgment call before editing.

Updating when the HTML source changes

If nfl_model_flow_diagram.htm is updated (new states, changed transitions, etc.):

  1. Diff the state machine. Look at the STATES object in the HTML and identify new/removed/changed states.
  2. Classify each change by phase. Initialization, Play Execution, or Play Aftermath? See CONTEXT.md for definitions; convention 4 above explains the classification framework.
  3. Update the relevant sub-diagram in nfl_sim_flowchart.md. Follow all flowchart conventions above.
  4. Update the Phase Map (section 1) if a new sub-diagram is added or removed.
  5. If a fundamental structural change happens (e.g., a new top-level phase, a back-edge that breaks the strict-tree convention), document the decision here in UPDATING.md as a new section before changing the diagrams.

Updating the interactive flowchart

nfl_sim_flowchart.html is a self-contained companion to the Mermaid flowchart. It renders every sub-diagram on a single pan/zoom canvas, with the same 3-phase left-to-right structure and color palette as the Mermaid version. Two inline JS objects drive it:

  • STATES — mirrors the upstream STATES shape from nfl_model_flow_diagram.htm, slimmed to title, category, and outcomes (label + next). No desc or effect. Single-outcome pass-through states (per convention 5 above) are inlined into their source's outcome — their next pointer is used directly as the next of the source outcome, so the elided state never appears in STATES or SUB_DIAGRAMS.
  • SUB_DIAGRAMS — per-sub-diagram metadata: phase, label, rootState, and states (the membership list). The layout algorithm walks STATES from the root and computes every node's (x, y) automatically — there are no hand-placed coordinates. Adding a state means adding it to STATES and appending its id to the correct sub-diagram's states list.

Shared-state duplication. When the upstream HTML defines a single state that multiple sub-diagrams transition into, duplicate that state in the rendered STATES object — one copy per sub-diagram, each with a unique id. Each copy has identical slimmed outcomes but lives inside only one sub-diagram's states list. This preserves strict-tree isolation: no state belongs to two sub-diagrams simultaneously. When regenerating, detect upstream states whose outcomes are referenced from roots in different sub-diagrams and split them accordingly.

The layout algorithm grows trees left-to-right (each depth level is one column to the right of its parent; siblings share a column and stack vertically). Phase bands and sub-diagram origins are computed from the content at runtime — there are no hand-placed coordinates — with one exception described below.

Diamond layout pattern. When a branching node routes to two children that both converge on a common successor, the automatic tree walk places the successor below one of the two branches rather than centred between them. To produce the intended diamond shape (branch-node left, two children top and bottom, successor right, with a direct horizontal bypass arrow from the branch-node to the successor), a post-process vertical shift is applied after the tree walk:

  1. Shift the successor node (and its subtree) so it centres exactly between the two branch children.
  2. Re-centre the branch-node itself on the same midpoint row.
  3. Recompute the sub-diagram bounding box so subsequent sub-diagrams are placed correctly.

Two outcome orderings are load-bearing for any sub-diagram that uses this pattern:

  • Branch-node bypass outcome must be at the middle index among the branch-node's outcomes. The exit-slot distributor assigns exits evenly across the source node's height — the middle slot aligns with the successor's centre row, making the bypass arrow horizontal.
  • Successor's tree-child outcomes must appear after any cross-sub pills in the successor's outcome list. The tree walk places leaves in outcome order — pills land at the top rows, the tree child at the bottom row — so the tree child appears below the successor after the vertical shift.

When syncing states that participate in a diamond from the upstream HTML, preserve these orderings even if the upstream uses a different order.

Cross-sub pill targets. Pill edges always connect to the rootState of the destination sub-diagram, not to an internal state within it. Even when the upstream source transitions directly to an internal state, the flowchart pill targets the sub-diagram root. This keeps pill rendering clean and avoids routing arrows into the interior of another sub-diagram's layout.

Edge labels. Every labeled edge displays its text. On near-horizontal segments — where the vertical span between source and target is less than half the node height — the label is truncated to fit the available horizontal gap, with the full text accessible on hover. Tooltips are driven by a custom JS div element (not SVG <title>, which is unreliable in some browsers). When adding a new labeled edge, no manual truncation threshold is needed; the renderer computes it from the actual coordinates.

A separate REAL_EDGES set names the cross-sub-diagram edges that render as real arrows (vs. clickable → Label pills). The rule is single-parent: a cross-sub-diagram target gets a real arrow only when no other state in the machine transitions to it. Today that's only Drive Cycle's Play Call → the 6 Play roots (Pass, Rush, Punt, FG, Spike, Kneel).

When the upstream state machine changes:

  1. A new state is added. Add it to STATES (with slimmed shape), then append its id to the states list of the sub-diagram it belongs to. The layout re-computes automatically on next load. If the new state is reachable from only one source in a different sub-diagram, add the edge key (<srcId>:<outcomeIndex>) to REAL_EDGES.
  2. An existing state changes outcomes. Update its outcomes in STATES. If an outcome now targets a state with multiple sources (it didn't before), remove that edge's entry from REAL_EDGES so it renders as a pill.
  3. A state is removed. Delete its STATES entry and remove its id from the sub-diagram states list; sweep REAL_EDGES for any stale edge keys referencing it.
  4. A new sub-diagram emerges. Add an entry to SUB_DIAGRAMS naming the root state, the phase (init / exec / aftermath), the friendly label used by pills targeting it, and the initial states list. The layout algorithm places it automatically below existing sub-diagrams in the same phase band.

Verify by opening nfl_sim_flowchart.html in a browser (file:// works; no build step or server required). The HTML has no external runtime dependencies.

Pointers