crisis/CrisisViz/HANDOFF.md
saymrwulf 54aae1a4dd Update all documentation for the crisis_agents layer + async refactor
Three sweeping additions and one new file, reflecting how the project
has grown:

* Parent `README.md` rewritten. The architecture mermaid now shows
  `crisis_agents` as a third sibling layer on top of the pure
  protocol algorithms, alongside the CrisisNode TCP runtime and the
  SimulatedNode in-process recorder. A fourth audience-shaped quick
  start (🤖 "run the AI-agent coordination demo") joins the
  protocol-pytest, simulation-CLI, and visualizer entries. The
  repository-layout tree expands to enumerate `src/crisis_agents/`'s
  modules. Test count corrected (~170).

* New `src/crisis_agents/README.md`. Comprehensive package
  documentation:
    - threat model + what's out of scope
    - the two principles enforced by tests: no chokepoint, no clock
    - mental-model mermaid (closed phase → boundary opens → async
      loop → quorum vote → multi-signer proof)
    - six-phase walkthrough matching the CLI output
    - module-by-module reference table
    - reuse map from `src/crisis/` (Message, LamportGraph,
      find_mutations, ProofOfWorkWeight, etc.)
    - build/run/test instructions including the `--live` Claude path
    - quorum-threshold formula in LaTeX: ⌈2N/3⌉
    - test taxonomy with the two sentinel files
      (test_no_chokepoint, test_async_quiescence) highlighted

* `INSTALL.md` extended. New Section 4 covers running the
  `crisis-agents demo`, both mocked-deterministic and `--live` with
  real Claude sub-agents. Anthropic SDK shown as optional `[live]`
  extras. Old sections renumbered (Section 5 → Section 6 for Swift,
  6 → 7 for Troubleshooting). Two new troubleshooting entries for
  live-mode failures.

* `CrisisViz/HANDOFF.md` gets a new Section 0. Brief notice that a
  sibling Python sub-project (`crisis_agents`) now exists, what it
  does, and — most importantly — that it doesn't share code with
  CrisisViz: refactoring one cannot break the other. Cross-link to
  the crisis_agents README so a future Swift-side agent has the
  pointer without having to discover it via grep.

Source-of-truth corrections in the parent README:
  - the "three audiences" framing becomes four
  - the layout tree now lists `src/crisis_agents/`
  - the architecture diagram explicitly marks the agent layer as
    "decentralized, asynchronous" (the two principles the recent
    refactors enforce)

CrisisViz code: still untouched by all this. Only its HANDOFF doc
gets a heads-up paragraph.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 22:13:00 +02:00

149 lines
9.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CrisisViz — Agent-to-Agent Handoff
> **Audience.** This file is the engineering log for the **next coding agent** to pick up this project. It assumes Swift, SwiftUI, macOS 26, and the Crisis protocol context from `../README.md`. For human-facing orientation see `README.md` next to this file.
Last updated: **2026-05-14**.
---
## 0. Sibling project notice — `crisis_agents` exists
Since this file was last meaningfully updated, a sibling Python sub-project has landed: **`src/crisis_agents/`** — a coordination layer that uses the same `crisis` protocol substrate for a fundamentally different consumer (AI agent teams, not visualization). It produces `proof_*.json` documents instead of `crisis_data.json`.
**Important for CrisisViz work:** the two sub-projects don't share code. `crisis_agents` does not produce data CrisisViz reads, and CrisisViz does not consume anything from `crisis_agents`. Refactoring either one cannot break the other.
If a future curriculum chapter wants to visualize agent coordination (decentralized detection, gossip propagation, multi-detector alarm convergence), that's a substantial new effort — see the parent README's "future CrisisViz story" note. For now, **focus on the chapter and testbed work and treat `crisis_agents` as an unrelated package living in the same repo**.
Reference: **[`../src/crisis_agents/README.md`](../src/crisis_agents/README.md)**.
---
## 1. Current state — what's shipped
- **All 10 chapters migrated** to the serial-beat timeline pattern (pure `state(at: t) -> WorldState`, scrubbable 16× to +16×, beat-bound narration).
- **Testbed green** at the last clean run: 38/38 invariants pass, 0 source-audit errors, 36/36 MP4 clips written, 279 PNGs sane, 12/12 resize cases pass.
- **Bundle pipeline works.** `./bundle.sh` produces a working `CrisisViz.app`. `./package-dmg.sh` produces a working `CrisisViz.dmg` (ad-hoc signed; first-open Gatekeeper warning, right-click → Open).
If you can't run the testbed and confirm it's green, **stop and fix that first** before making curriculum changes.
---
## 2. The pure-function timeline pattern (this is the architecture)
Every chapter is split into two files:
```
Sources/CrisisViz/Engine/ChXXTimeline.swift pure model (state machine over t)
Sources/CrisisViz/Chapters/ChXX_Foo.swift thin Canvas renderer (no logic)
```
The timeline file is the contract. It contains:
1. **A typed `BeatKind` enum** capturing the chapter's micro-events (e.g. `introduce(name:)`, `compose(by:)`, `sealAccepted(at:)`, `gossipFly(from:to:)`, `linkBroken`, …).
2. **A `WorldState` struct** holding the cumulative state at a moment in time (introduced cast set, accepted vertices per lane, in-flight envelopes, broken links, vault contents, …).
3. **A flat `[Beat]` list.** Each `Beat` has `(id, kind, durationSeconds, narration)`. Narration is one sentence describing what that beat _physically shows_.
4. **A `state(at: Double) -> WorldState` pure function.** Replays every beat up to `t` produces the state. Pure ⇒ scrubbable + reverse-playable. **No monotonic accumulators, no hidden `@State`.**
5. **A `ChXXScenes` enum** with `sceneStarts`, `sceneDurations`, `timelineT(sceneIndex, localTime)`, `narrationAt(sceneIndex, localTime)`. The chapter's existing scene count stays — scenes become navigation labels on the unified timeline.
The renderer file is thin:
```swift
TimelineView(.animation) { timeline in
Canvas { ctx, size in
let t = ChXXScenes.timelineT(sceneIndex: sceneIndex, localTime: localTime)
let world = ChXXTimeline.state(at: t)
// draw the world from `world`. No per-scene switch.
}
}
```
`SceneEngine.durationOverrides` gets per-scene durations matching the timeline's windows. `ImmersiveView.liveNarration` adds a case for the new chapter, reading from `ChXXScenes.narrationAt`.
---
## 3. Pedagogy invariants the renderer MUST hold
These are not stylistic preferences — the testbed enforces them. Break one, the source audit fails.
- **Strictly serial.** Never two simultaneous events on screen. Every beat owns its time window exclusively.
- **Cast appears via `introduce(...)` beats only.** Lanes for not-yet-introduced cast are invisible. Their lane labels are also hidden.
- **Lane = lifeline.** A vertex belonging to player $P$ sits exactly on $P$'s lane Y. No jitter, ever. Source audit forbids reintroducing `hashJitterY`.
- **Composing and open-envelope share ONE fixed top-center slot** (`detailSlotRect`, y ≈ 16..146 + ~30pt caption). They never co-occur on the timeline.
- **In-flight envelopes draw on a courier track 36pt above the lane axis** so they don't collide with the just-sealed accepted vertex on the sender's lane.
- **Cast colors via `dm.castColor(for:)`** — never `palette[i]`. Lane order via `dm.castOrderedNodes()` — never raw `sim.nodes` (would put Dave at lane 8 below 5 peers).
- **Beat tag** (small, faint, top-right) so PNG sweeps can be matched to a specific beat for debugging.
- **Scene indicator** `CH X.Y (n/N)` badge on the narration panel, visible even when collapsed.
When designing a new beat, picture all of these on screen at once (lane labels left margin, cast circles with ~50pt halos, `detailSlotRect` top band, courier track at lane Y 36, accepted-vertex rows right of cast circles, `GlassNarration` bottom-left ~340pt × 250pt expanded, `GlassControls` bottom ~80pt). If the new beat lands on top of any of those, redesign before shipping.
---
## 4. Hard-won rules from past sessions
These are the ones that bite repeatedly. Memorize them.
| Rule | Why |
|---|---|
| Restart the live `.app` after Swift changes | The Dock icon launches `CrisisViz.app`, not the `swift-run` dev binary. Run `./bundle.sh --no-launch && open CrisisViz.app`. |
| Testbed can't verify animation smoothness | Static PNG sweeps catch layout bugs; MP4 clips catch motion bugs; but the user-experience of scrub feel can only be evaluated live. Always restart and watch. |
| Narration ≡ canvas | When a scene's title is a narrative beat ("Aaron speaks. Ben listens."), hand-curate the visible-vertex set to match. Progressive reveal by `sceneVertexCount` alone will under-show or over-show and make the narration lie. |
| Arrows must be visible | Edges drawn as `Path` lines without arrowheads are not arrows. Use `drawArrowEdge`. Every narrated causal claim ("Ben copies Aaron") must be physically renderable from the data. |
| Layout is computed from the full dataset | Compute positions from `sim.nodes[step=lastStep]`, reveal only a subset via `sceneVertexCount`. Positions never jump because layout input doesn't change. |
---
## 5. Test harness reference
`swift run CrisisViz --testbed``~/Desktop/CrisisViz_Testbed/`. Five layers:
| Layer | File | Catches |
|---|---|---|
| Narrative invariants | `Testbed/NarrativeInvariants.swift` | logical claims about staging, cast assignment, geometry, sim convergence |
| Source pattern audit | `Testbed/SourceAudit.swift` | regex-forbidden patterns (lane jitter, `palette[i]`, hardcoded PIDs) |
| Per-scene MP4 clips | `Testbed/SceneVideoCapture.swift` | animation continuity (36 clips at 8s/30fps) |
| PNG time-scrubbing sweep | `Testbed/SceneCapture.swift` | frozen interpolators, all-black renders, label drift |
| Window resize + sanity | `Testbed/SceneCapture.swift` | clamping correctness, tiny renders, byte-identical frames |
When adding a new chapter / scene / design rule, **update the corresponding layer in the same commit**:
- New chapter → add invariants in `NarrativeInvariants.swift` (expected visible vertex count, expected cast members, expected edges).
- New design rule → add a `Rule` to `SourceAudit.rules` with the legitimate definition site whitelisted in `allowedFiles`.
- New animation → confirm the scene's MP4 actually contains motion. Scrub it.
---
## 6. Known open items
Surfaced from working memory at handoff. None blocking, all valuable:
1. **DA-chapter polish (Ch07 / Ch08).** Both are on the serial-timeline pattern but the shard / vault animations are still abstract relative to the cast-on-lane discipline elsewhere. Could be tightened so shards are physically carried by Aaron / Ben / Carl / Dave lanes.
2. **Per-scene visible-vertex-count invariants.** Currently 38 logical invariants; adding count assertions per scene would catch staging regressions before they ship.
3. **`LaneRenderKit` extraction.** Geometry helpers `castLaneY`, `castPosition`, `castColor`, `drawIntroducedLanes`, `drawCastFigures` are duplicated across cast-heavy chapters (Ch00, Ch01, Ch02, Ch09 — four adopters now, more than enough). Factor into a shared file.
4. **Animation smoothness verification protocol.** No testbed signal — only live-app eyeballing on Ch01 staging, Ch02 partition, Ch06 total-order convergence, Ch09 byzantine. Recurring blind spot; consider an MP4 difference-frame analyzer.
5. **CrisisNode / gossip TCP integration tests.** The real distributed runtime (`src/crisis/node.py`, `src/crisis/gossip.py`) has zero tests. Not blocking the visualizer (which uses `SimulatedNode`), but the deployable side is currently unverified.
---
## 7. How to resume
```sh
cd /Users/oho/GitClone/ClaudeCodeProjects/crisis/CrisisViz
# 1. Compile + trust SourceKit
swift build
# 2. After source changes: rebuild the bundle
./bundle.sh --no-launch
open CrisisViz.app
# 3. Verify the testbed before committing curriculum changes
swift run CrisisViz --testbed
# read ~/Desktop/CrisisViz_Testbed/INVARIANTS.md, SOURCE_AUDIT.md,
# VIDEO_CLIPS.md, MANIFEST.md, SANITY.md — all must be green
# 4. For distribution
./package-dmg.sh
# produces CrisisViz.dmg, prints SHA-256
```
If something feels broken, check in this order: (1) is `crisis_data.json` present and non-empty? (2) does `swift build` succeed cleanly? (3) does the dev binary `swift run CrisisViz` produce a window? (4) does `./bundle.sh` succeed and `open CrisisViz.app` show the same window with a Dock icon? (5) does the testbed run to completion?