Commit graph

7 commits

Author SHA1 Message Date
06ef8234d5 Foundation redesign: persistent cast, sidebars, story-beat titles
The previous chapter-by-chapter visualization felt disconnected — every
scene used a different idiom and the cast was anonymous "honest-0". This
commit lays the foundation for a coherent end-to-end story:

Cast (Sources/CrisisViz/Model/Cast.swift):
- Four named validators with stable color slots — Aaron (coral), Ben (teal),
  Carl (amber), Dave (violet, Byzantine). The first three honest nodes claim
  the lead slots; the byzantine node always plays Dave; surplus honest
  nodes become muted "Peer-N" fallbacks.
- buildAssignment(nodes:) does the mapping once at load time and is cached
  on DataManager so every render is a dictionary lookup.

DataManager:
- nodeColors / nodeNames now serve cast colors and cast display names so
  every existing call site that reads them automatically picks up the new
  naming with no chapter-by-chapter changes required.
- castRole(for:), castColor(for:), laneIndex(for:), castOrderedNodes() —
  helpers chapters can opt into when they want lane order to follow the
  cast (Aaron→Ben→Carl→Dave) rather than the simulation's raw node order.

Persistent sidebars (Sources/CrisisViz/Views/Sidebars.swift):
- CastSidebar (180pt left edge): one card per lead with color swatch,
  display name, and personality cue. BYZ badge on Dave.
- LegendSidebar (200pt right edge): persistent encoding rules — color
  = validator, stripe = round, border = vertex state, edge style =
  parent link. The answer to "every view looks different".
- ImmersiveView wraps the SceneRouter in an HStack(CastSidebar, scene,
  LegendSidebar) so chapters get a slightly narrower canvas without
  needing to know the sidebars exist.

Story-beat titles (ChapterDefinitions + SceneNarrations):
- Chapter titles are now full sentences with a [Technical: ...] suffix
  ("Aaron speaks. Ben listens. The graph begins. [Technical: gossip
  & DAG]"). Story-beat for the noob, technical handle for the engineer.
- Scene titles likewise rewritten as continuous narrative beats.
- All chapter narrations rewritten to address the cast by name and walk
  the reader through what is happening rather than asserting it.

Decisions saved to memory at project_redesign_decisions.md so future
sessions know cast names, title format, and morph-not-cut policy.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 11:18:11 +02:00
dd185e70ae Extend testbed 10x; fix window resize regressions; @MainActor delegate
Testbed (Sources/CrisisViz/Testbed/SceneCapture.swift):
- Add convergenceFineOffsets (21 slices), textScaleLadder (6 scales),
  canvasSizeMatrix (7 sizes), and four extended capture functions
  (captureComparisonAtAllSizes/AllScales, captureConvergenceFineGrained,
  captureInspectorAtAllScales) — testbed now produces 279 PNGs across
  16 folders.
- pickConvergingPair(snap:) BFS helper picks two vertices whose depth-3
  cones share >=2 ancestors, so comparison sweeps always show a real
  convergence story instead of a degenerate pair.
- runSanityChecks() walks every PNG, flags tiny files (<8KB), and
  distinguishes real mid-animation freezes (3+ byte-identical frames
  surrounded by varying frames) from expected settled-tail plateaus
  by extracting numeric _t<value>s suffixes.
- runWindowResizeUnitTests() returns a 12-case ResizeUnitReport
  covering shrink-below-min, grow-past-screen (PREVENTS TILING),
  exact boundaries, ultrawide, and headless harness — currently 12/12.

Window resize (Sources/CrisisViz/App/CrisisApp.swift):
- Mark CrisisAppDelegate @MainActor and conform to NSWindowDelegate.
- Static clampResize(proposed:visibleSize:) extracted as pure helper
  so the testbed can unit-test the resize policy without an NSWindow.
- windowWillResize caps proposed sizes at the screen visibleFrame so
  drags never push past the menu bar — that push is what arms macOS
  15+/Tahoe automatic edge tiling and silently locks every handle
  except the left edge.
- Opt out of auto-tiling explicitly via .fullScreenDisallowsTiling and
  re-attach our delegate on didBecomeKey, because SwiftUI replaces it
  on key changes.
- Initial window is 80% of visibleFrame, centered (not flush against
  edges), so the very first drag cannot trip the tile gesture.

AppSettings (Sources/CrisisViz/Engine/AppSettings.swift):
- New @Observable @MainActor settings object injected via
  .environment(settings) at App scope; survives the entire process
  and feeds the inspector text-scale slider.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 00:07:29 +02:00
93674001cd Add macOS .app bundle with native Dock icon and activation policy
- bundle.sh assembles CrisisViz.app (Info.plist + AppIcon.icns + resources)
  and ad-hoc codesigns it so it launches as a proper Foreground app.
- Tools/MakeAppIcon.swift renders the app icon programmatically (10 sizes,
  16-1024 px) as a 3-round mini-DAG matching the live node palette.
- CrisisApp.swift forces .regular activation policy via NSApplicationDelegate
  so the Dock tile and menu bar appear even when launched unbundled
  via `swift run CrisisViz`.
- Ignore build artifacts (.build, AppIcon.iconset, CrisisViz.app) and the
  user-local .claude/ auto-memory directory.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:21:18 +02:00
c269811f0f Add CrisisViz: native macOS 26 SwiftUI visualizer with vertex inspection
Keynote-style presentation of the Crisis consensus protocol — 36 scenes
across 10 chapters, single-TimelineView Canvas rendering at 60fps with
Liquid Glass chrome. Driven by the simulation JSON dump.

The signature feature is the click-to-inspect overlay on Ch02: tapping a
vertex opens a recursive hash-unwrapping animation that reveals the
selected message's payload, then its parent hashes, then their pre-images
(parent messages with their own payloads + grandparent hashes), staggered
through to genesis. Makes the abstract idea of "what does a vertex know?"
visceral.

Includes a time-scrubbing testbed harness (`swift run CrisisViz --testbed`)
that captures 180 scene PNGs + 18 inspector reveal PNGs at successive time
offsets, with a MANIFEST quality checklist for human-eye verification.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:06:58 +02:00
1491422527 Add JSON export pipeline + event recorder for visualization
The simulation now optionally records structured events (message creation,
delivery, round computation, voting, leader election) via EventRecorder and
exports a complete simulation dump to JSON via the new export_json module.
crisis_data.json captures a 10-step run that the SwiftUI visualizer consumes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:06:21 +02:00
37e9f26204 Fix round advancement and end-to-end consensus convergence
Five bugs prevented the full consensus pipeline from producing results:

1. k-reachability with k=0 required weight > 0, but some vertices had
   weight 0. Fixed: k <= 0 degenerates to simple past-containment check.

2. SVP incorrectly included the current round (v.round). The paper's
   Algorithm 6 only includes rounds strictly < v.round. With the current
   round in SVP, the voting set contained only the vertex itself (peers
   are spacelike), making agreement impossible.

3. Stage delta (δ) was computed as the SVP index, but the paper defines
   δ = d_{svp}(s, t) as distance from s=max(svp). Fixed: δ=0 at the
   newest round (initial proposal), increasing toward older rounds.

4. The voting set was recomputed per-round, but Algorithm 7 line 6
   computes it ONCE for s=max(svp). Fixed: single voting set S shared
   across all stages.

5. Demo parameters (difficulty=2, pow_zeros=0) made thresholds
   unreachable. Calibrated: difficulty=1, pow_zeros=2 gives weights
   that cross both the is_last (3*d) and SVP (6*d) thresholds.

Result: 3 honest nodes now converge on identical total order. Leaders
are elected via the full BA* pipeline (initial proposal → presorting →
gradecast → BBA binary agreement). Byzantine nodes cannot prevent
convergence.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 15:13:41 +02:00
1df4790fb4 Initial implementation of the Crisis protocol (Richter, 2019)
Complete Python PoC of "Probabilistically Self Organizing Total Order
in Unstructured P2P Networks". Implements all 10 algorithms from the paper:
message generation, integrity checks, Lamport graphs, virtual synchronous
rounds, safe voting patterns, virtual leader election (BA*), longest chain
rule, total order via Kahn's algorithm, and push/pull gossip.

Includes simulation harness, full node binary, and 72 passing tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 13:20:30 +02:00