Project 5: LiveView Operations Board
Build a two-browser incident board that reacts quickly to transient notifications while recovering correctness from authoritative state after duplicates, gaps, disconnects, and reconnects.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Level 3 — Advanced |
| Time Estimate | 14–20 hours |
| Language | Elixir (alternatives: TypeScript client, Erlang backend) |
| Primary Tools | Phoenix 1.8, Phoenix LiveView, Phoenix.PubSub, Ecto, Telemetry |
| Prerequisites | OTP processes, LiveView lifecycle, Ecto transactions, PubSub topics, revisioned state |
| Key Topics | Connected mount, transient notifications, state projection, revision gaps, render coalescing, authorization |
| Observable Deliverable | Operator view, wallboard view, reconnect diagnostics, and a deterministic two-client test suite |
1. Learning Objectives
By completing this project, you will be able to:
- Explain why a LiveView has disconnected and connected mount phases and subscribe exactly once during the connected phase.
- Separate an authoritative incident model from the transient notification that says the model changed.
- Detect duplicate, stale, contiguous, and gapped revisions without treating message arrival order as business truth.
- Recover a disconnected client by reloading current authorized state instead of expecting PubSub replay.
- Coalesce high-frequency notifications into a bounded render cadence while preserving the latest committed facts.
- Measure notification age, revision lag, reconnect recovery, mailbox pressure, and render count.
- Re-check authorization on reload so a long-lived process cannot retain access after role revocation.
2. Theoretical Foundation
2.1 Core Concepts
LiveView has two relevant lifecycle phases. The first render can happen without a persistent WebSocket-connected LiveView process. Once the client connects, the connected mount establishes the long-lived server process that can receive messages. Subscribing during both phases creates duplicate registration or pointless work; subscribing only when connected aligns the PubSub registration with the process that will handle handle_info messages.
A notification is not the state. Phoenix.PubSub broadcasts transient BEAM messages. It does not persist history or replay missed publications. The database or incident repository owns the incident record and monotonically increasing revision. The notification only carries enough information to discover that a newer projection may exist: incident identifier, committed revision, event identifier, and timestamp.
Revision handling is a small state machine. If the local revision is 22, notification 22 is duplicate or stale, 23 is contiguous, and 26 proves a gap. A gap is not repaired by guessing what revisions 23–25 contained. The correct transition is to mark the projection stale, reload current authorized state, and record that recovery occurred by reload.
Rendering is another queue. A LiveView can ingest messages faster than a browser or human can usefully render them. A board usually needs the latest incident status, not every intermediate repaint. The process may update its in-memory projection on each accepted revision while scheduling at most one render tick per interval. This is coalescing of display work, not loss of durable domain facts.
Authorization is continuous. A topic name is routing, not access control. Authorization must precede the initial load and subscription, and it must be checked when reloading incident detail. If the user’s role is revoked, the LiveView must stop exposing the topic’s data and unsubscribe or terminate according to policy.
2.2 Mental Model
durable commit boundary
Operator action ─────────────────────────────────────────────┐
v
┌──────────────────┐
│ Incident store │
│ truth + revision │
└────────┬─────────┘
│ commit r23
v
┌──────────────────┐
│ PubSub notice │
│ id + revision │
└───────┬──────────┘
│ transient
┌────────────────────┴─────────────────┐
v v
┌────────────────────┐ ┌────────────────────┐
│ Operator LiveView │ │ Wallboard LiveView │
│ local revision=22 │ │ local revision=22 │
└─────────┬──────────┘ └─────────┬──────────┘
│ │
r23 contiguous: reload disconnected: notice missed
│ │ reconnect
v v
render committed state reload truth at revision 26
record gap 23 -> 26
The invariant is simple: the rendered incident must be derivable from the latest authorized repository read. PubSub shortens detection latency; it does not define correctness.
2.3 Revision Classification
Use this conceptual transition table:
| Incoming Revision | Local Revision | Classification | Action |
|---|---|---|---|
| lower | 22 | Stale | Ignore; increment stale counter |
| equal | 22 | Duplicate | Ignore; increment duplicate counter |
| 23 | 22 | Contiguous | Reload affected record or validated projection |
| greater than 23 | 22 | Gap | Mark stale, reload authoritative board, record gap |
| any | unknown after reconnect | Untrusted local state | Reload full authorized projection |
Even a contiguous notification should normally trigger a repository read. A payload snapshot can become stale between authorization, commit, and delivery; the repository read provides one consistent rule for both normal and recovery paths.
2.4 Race Windows
There is no magical atomic operation that means “read the database and subscribe to PubSub at the same instant.” Document one safe race policy. A robust policy is:
subscribe -> load revision R -> process queued notices
If a queued notice revision <= R: ignore it.
If a queued notice revision = R + 1: reload.
If a queued notice revision > R + 1: perform gap recovery.
The inverse order, load then subscribe, has a window where a commit can occur after the load but before subscription. It can still be made safe with a second revision check, but the policy must be explicit and tested.
2.5 Common Misconceptions
- “
broadcastreturning:okmeans both browsers rendered.” It only means the broadcast path returned successfully; subscribers acknowledge neither receipt nor handling. - “The topic is an authorization boundary.” Anyone with code access to the PubSub name can attempt to subscribe. Domain authorization is separate.
- “A reconnect will receive events sent while offline.” Phoenix.PubSub has no replay log.
- “Every event needs a render.” The application needs every durable state transition recorded, but the UI may render the latest projection at a bounded rate.
- “Same-sender BEAM ordering solves board consistency.” Multiple publishers, reconnects, and distributed paths mean the application still needs revisions.
2.6 Why This Matters
Operational dashboards often appear correct in a one-tab demo and fail during deploys, network loss, or bursts. The pattern learned here generalizes to stock boards, fleet monitoring, fulfillment screens, support consoles, and security operations centers. The useful architecture is not “push the entire truth everywhere”; it is “commit truth once, emit a low-latency hint, and make every projection recoverable.”
3. Project Specification
3.1 What You Will Build
Build an incident operations application with two simultaneous views:
- Operator view: authorized controls for acknowledging, mitigating, resolving, assigning, and annotating an incident.
- Wallboard view: read-only status cards grouped by severity and lifecycle state.
- Diagnostics drawer: connection status, local revision, repository revision, last event age, duplicate/stale/gap counts, reconnect count, render count, and recovery reason.
- Fault controls in development: disconnect one browser, delay its handler, inject duplicate notices, inject a stale revision, and create a revision jump.
The incident store is authoritative. Every successful mutation increments a board revision in the same database transaction as the incident change. Only after commit does the application broadcast a compact change notice.
3.2 Functional Requirements
- Connected subscription: Subscribe only after the LiveView is connected, with no duplicate registration after reconnect or navigation.
- Authoritative load: Render from repository data and expose the revision used for that projection.
- Compact event envelope: Include event ID, board ID, incident ID, committed revision, event type, occurred-at timestamp, and schema version.
- Revision state machine: Count and handle stale, duplicate, contiguous, and gap notices explicitly.
- Reconnect recovery: Reload current state and revision after reconnect; never wait for missed publications.
- Render coalescing: Cap visible updates to a configured cadence while preserving the latest repository projection.
- Authorization: Verify board access before load/subscribe and again on each recovery/detail reload.
- Diagnostics: Make event age, revision lag, reconnect recovery, and coalescing visible in the UI.
- Multi-client behavior: A committed action in either authorized operator session updates all connected views.
- Revocation behavior: A role revocation prevents the next reload and removes protected data from the socket.
3.3 Non-Functional Requirements
- Freshness SLO: Under baseline load, p99 committed-to-rendered age below 500 ms on the local development environment.
- Recovery SLO: After reconnect, the wallboard shows the current repository revision within 2 seconds.
- Render cap: No more than 10 board renders per second during an update burst.
- Correctness: The rendered revision never moves backward.
- Privacy: Diagnostics show opaque IDs and bounded metadata, not incident secrets or authorization tokens.
- Testability: Time, event IDs, and repository revisions can be controlled in tests.
3.4 Event and State Outlines
These are design outlines, not runnable definitions:
ChangeNotice
event_id: UUID
event_type: incident.changed
board_id: opaque identifier
incident_id: opaque identifier
revision: positive integer
occurred_at: UTC timestamp
schema_version: integer
SocketProjection
board: authorized incident summaries
local_revision: integer
connection_state: connecting | live | stale | recovering | forbidden
pending_render: boolean
diagnostics: counters + timestamps
3.5 Real World Outcome
Start the server, open /boards/operations in two browser profiles, and label one window Operator and the other Wallboard. Resolve INC-104 in the operator window. The operator button becomes disabled while the transaction commits, then both windows show RESOLVED, revision 23, and a green LIVE badge. The diagnostics drawer on the wallboard reports an event age below the target SLO.
Next, use browser developer tools to switch the wallboard network offline. Perform three more incident mutations in the operator window, producing revisions 24–26. Bring the wallboard online. It must not animate through imaginary intermediate events. It reloads revision 26, shows a temporary RECOVERED badge, and records gap 23→26; recovered_by=repository_reload.
The reference transcript should look exactly like this:
Browser A — Operator Browser B — Wallboard
INC-104 API latency [MITIGATING] revision=22 age=84ms LIVE
action: resolve -> committed r23 update r23 applied without refresh
[Browser B network offline]
commits r24, r25, r26 no transient delivery
[Browser B reconnect]
mount revision=26 source=database gap 23->26 recovered_by=reload PASS
4. Solution Architecture
4.1 High-Level Design
┌────────────────┐ command ┌────────────────────┐ transaction ┌──────────────┐
│ Operator │────────────>│ Incident context │────────────────>│ PostgreSQL │
│ LiveView │ │ auth + mutation │ │ truth + rev │
└────────────────┘ └─────────┬──────────┘ └──────────────┘
│ after commit
v
┌────────────────────┐
│ Phoenix.PubSub │
│ transient notice │
└───────┬────────────┘
│
┌──────────────────┴──────────────────┐
v v
┌─────────────────────┐ ┌─────────────────────┐
│ Operator projection │ │ Wallboard projection│
│ classify -> reload │ │ classify -> reload │
└──────────┬──────────┘ └──────────┬──────────┘
└──────────────┬──────────────────────┘
v
bounded render scheduler
4.2 Key Components
| Component | Responsibility | Key Decision |
|---|---|---|
| Incident context | Authorize and commit mutations | Increment revision in the same transaction |
| Board repository | Return one authorized projection and revision | Single source of UI truth |
| Event envelope builder | Create compact post-commit notice | IDs and revision, not sensitive snapshot |
| PubSub topic policy | Produce opaque tenant/board topics | Routing never substitutes for authorization |
| LiveView revision reducer | Classify incoming revision | Gaps force reload |
| Render scheduler | Coalesce repaint work | Latest state wins within a bounded interval |
| Diagnostics model | Record freshness and recovery | Bounded labels and monotonic counters |
4.3 State Invariants
local_revisionnever decreases.- Protected board data exists in the socket only while authorization is valid.
- A
LIVEbadge means the last repository load succeeded and no known gap is pending. - At most one render timer is outstanding for a LiveView.
- A notification payload is never treated as the durable incident record.
- A reconnect always causes a repository reconciliation before claiming live status.
4.4 Algorithm Overview
Revision-aware message handling:
ON change_notice:
validate envelope schema and board identity
compute event_age
IF revision <= local_revision:
classify duplicate_or_stale; stop
ELSE IF revision == local_revision + 1:
reload authorized affected projection
ELSE:
mark stale
reload authorized full board
record revision gap and recovery reason
schedule one bounded render tick if none exists
Each classification is constant time. Repository work dominates runtime. Board reload cost depends on the number of visible incidents, so query limits and projection fields must be explicit.
5. Implementation Guide
5.1 Development Environment Setup
Use current project-compatible versions rather than blindly upgrading an existing application:
$ elixir --version
Erlang/OTP 29 [...]
Elixir 1.20.x [...]
$ mix phx.new incident_board
* creating incident_board/lib/incident_board/application.ex
* creating incident_board/lib/incident_board_web/router.ex
...
Fetch and install dependencies? [Yn]
Create a development database, run migrations, seed at least four incidents, and verify two independent browser sessions can connect. Commands and generated output are allowed; do not copy a complete implementation from a tutorial.
5.2 Suggested Project Structure
incident_board/
├── lib/
│ ├── incident_board/
│ │ ├── incidents.ex # authorized commands and reads
│ │ ├── incidents/incident.ex # durable model
│ │ ├── incidents/board_event.ex # envelope construction
│ │ └── telemetry.ex # bounded metrics definitions
│ └── incident_board_web/
│ ├── live/operations_live.ex # lifecycle and projection state
│ └── live/operations_live.html.heex
├── priv/repo/migrations/
└── test/
├── incident_board/incidents_test.exs
└── incident_board_web/live/operations_live_test.exs
5.3 The Core Question You’re Answering
“How does a real-time UI remain correct after it inevitably misses transient notifications?”
Before implementing, explain in writing why “subscribe and update assigns” is insufficient. Your answer must cover initial render, the read/subscribe race, duplicate subscriptions, missed events, role revocation, and render overload.
5.4 Concepts You Must Understand First
- Disconnected versus connected mount
- Which phase owns a persistent process?
- Why can subscribing in both phases duplicate delivery?
- Reference: Phoenix LiveView lifecycle documentation.
- PubSub delivery semantics
- What does
broadcastacknowledge? - What happens while a subscriber is disconnected?
- Reference: Phoenix.PubSub 2.2 documentation.
- What does
- Derived projections
- Why is a wallboard a projection rather than a source of truth?
- How is a projection repaired?
- Book: “Designing Data-Intensive Applications, 2nd Edition,” derived data and stream processing chapters.
- Long-lived authorization
- Which permissions can change after mount?
- How is protected socket state removed?
- Render pressure
- Which updates supersede older display states?
- Which domain events may never be discarded from the durable record?
- Book: “Release It!, 2nd Edition,” stability patterns.
5.5 Questions to Guide Your Design
- Does the notice carry only identifiers and revision, or a projection snapshot? Why?
- What exact rule distinguishes stale, duplicate, contiguous, and gapped events?
- Which query reloads one incident and which reloads the whole board?
- What prevents two render timers from being scheduled concurrently?
- What will the wallboard display during recovery instead of falsely claiming
LIVE? - How will a revoked operator lose topic access and cached socket data?
5.6 Thinking Exercise
Draw five columns labeled Browser, LiveView Process, Mailbox, PubSub Registry, and Database. Trace this sequence:
- Disconnected mount reads revision 20.
- Connected mount subscribes.
- Revision 21 commits while the connected process loads.
- A duplicate revision 21 arrives.
- The socket disconnects and misses revisions 22–24.
- It reconnects with a new process and loads revision 24.
- The user’s role is revoked before revision 25.
For every step, mark where truth exists, where a transient message can exist, and which authorization check applies.
5.7 The Interview Questions They’ll Ask
- “Why subscribe only during connected mount?”
- “How do you close the read-versus-subscribe race?”
- “Why isn’t a PubSub event an event store?”
- “How do revisions repair duplicate, stale, and missing notifications?”
- “How would you prevent a burst from causing excessive LiveView renders?”
- “How do you test two clients and a reconnect deterministically?”
5.8 Hints in Layers
Hint 1 — Prove lifecycle first: Display connection state and subscription count before implementing domain updates.
Hint 2 — Broadcast less: Use an opaque identifier and committed revision. Reload the authorized projection on receipt.
Hint 3 — Make gaps explicit: Once a gap is detected, reject incremental assumptions until a repository reconciliation succeeds.
Hint 4 — Separate ingest from display: Store the latest valid projection immediately but schedule at most one render tick per cadence window.
Hint 5 — Test revocation as a state transition: Change authorization while the LiveView is connected, then force a reload-producing event.
5.9 Books That Will Help
| Topic | Book | Focus |
|---|---|---|
| LiveView lifecycle | “Programming Phoenix LiveView” by Tate & DeBenedetto | Lifecycle and state management chapters |
| Phoenix real-time architecture | “Real-Time Phoenix” by Stephen Bussey | PubSub, Channels, and Presence chapters |
| Derived state | “Designing Data-Intensive Applications, 2nd Edition” | Derived data and stream processing |
| Stability | “Release It!, 2nd Edition” | Backpressure, timeouts, and stability patterns |
5.10 Implementation Phases
Phase 1 — Durable incident model (4–5 hours)
- Define incident states and legal transitions.
- Add board revision and commit it atomically with mutations.
- Build an authorized board projection query.
Checkpoint: Repository tests prove revision monotonicity and reject unauthorized reads.
Phase 2 — Connected two-browser updates (4–5 hours)
- Add connected-only subscription.
- Broadcast compact notices only after successful commit.
- Reload and render the affected projection.
Checkpoint: One committed action updates two independent sessions exactly once.
Phase 3 — Revision recovery (4–6 hours)
- Add classification counters.
- Implement duplicate/stale ignore and gap reload.
- Reconcile on reconnect.
Checkpoint: Offline wallboard misses three revisions, reconnects, and renders current state without replay.
Phase 4 — Render control and diagnostics (3–5 hours)
- Add bounded render scheduling.
- Record event age, revision lag, render/coalescing count, and recovery reason.
- Show live/stale/recovering badges.
Checkpoint: A 100-notice burst produces no more renders than the configured cap and ends at the latest revision.
Phase 5 — Authorization and fault tests (3–5 hours)
- Re-check access on reload.
- Remove protected assigns on revocation.
- Add two-client, reconnect, burst, and role-change tests.
Checkpoint: The full Definition of Done passes without sleeps that hide races.
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale |
|---|---|---|---|
| Event payload | Full snapshot / IDs + revision | IDs + revision | Keeps truth and authorization in repository |
| Initial race policy | Load then subscribe / subscribe then load | Subscribe then load, discard notices at or below loaded revision | Queued notices are safely classified |
| Gap handling | Guess transitions / reload | Reload | PubSub has no replay |
| Render strategy | Per notice / bounded coalescing | Bounded coalescing | Protects browser and LiveView under bursts |
| Topic granularity | One global / tenant-board | Tenant-board with opaque IDs | Limits fanout while preserving domain checks |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples |
|---|---|---|
| Domain unit | Prove legal incident transitions and revisions | Resolve increments once; invalid transition rejected |
| LiveView integration | Prove connected subscription and projection reload | Two clients observe revision 23 |
| Race tests | Exercise queued notice during load | Notice equal to loaded revision ignored |
| Recovery tests | Prove offline correctness | Reconnect at revision 26 |
| Load tests | Prove render cap and freshness | 100 notices collapse to bounded renders |
| Security tests | Prove continuous authorization | Revoked user cannot reload board |
6.2 Critical Test Cases
- Initial disconnected render does not subscribe.
- Connected mount registers exactly once.
- Duplicate event ID and equal revision do not rerender.
- Lower revision never moves the UI backward.
- Revision jump marks the board stale before reload.
- Failed reload keeps
RECOVERINGor shows an error; it never claimsLIVE. - Reconnect after three commits loads the newest revision.
- Role revocation clears protected state and denies the next reload.
- A notification burst respects the render cap and ends at the latest revision.
- Malformed or future-schema envelopes are rejected and counted.
6.3 Deterministic Test Data
Initial board revision: 22
Incident: INC-104, state=MITIGATING, severity=SEV-1
Connected clients: operator-A, wallboard-B
Notices:
E-230 revision=23 incident=INC-104 occurred_at=T+000ms
E-230 revision=23 incident=INC-104 occurred_at=T+005ms # duplicate
E-219 revision=21 incident=INC-104 occurred_at=T-500ms # stale
E-260 revision=26 incident=INC-207 occurred_at=T+900ms # gap
Expected final state:
local_revision=26
duplicate_count=1
stale_count=1
gap_count=1
recovery_reason=repository_reload
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Correction |
|---|---|---|
| Subscribe in both mount phases | Each change handled twice | Guard subscription by connected lifecycle |
| Treat event payload as truth | Stale or unauthorized detail displayed | Reload authorized repository projection |
| Ignore revision gaps | Board silently diverges | Mark stale and reconcile |
| Render per notice | High CPU, large diffs, browser lag | Coalesce render cadence |
| Trust topic secrecy | Revoked user keeps data | Re-authorize on load/recovery |
| Assert only final UI | Duplicate handling remains invisible | Assert diagnostics and handler counts |
7.2 Debugging Strategies
- Inspect
Process.info(liveview_pid, :message_queue_len)during a burst, but do not poll it per event. - Log event ID, incoming revision, local revision, classification, and recovery reason as structured fields.
- Attach Telemetry handlers that record bounded measurements and perform no blocking I/O.
- Verify the database revision directly whenever the UI reports a gap.
- Reproduce reconnect with browser offline mode and with server process termination; they exercise different lifecycle paths.
7.3 Performance Traps
Large snapshots amplify serialization and mailbox memory. Per-event database N+1 queries amplify latency. High-cardinality metric labels amplify observability cost. Repeated timers amplify render work. Measure committed-to-handled age and committed-to-rendered age separately so repository or rendering delay is not misdiagnosed as PubSub delay.
8. Extensions & Challenges
8.1 Beginner Extensions
- Add severity and assignee filters that retain the current revision.
- Add a visible stale/recovering badge with last successful load time.
- Export the diagnostics drawer as a text report.
8.2 Intermediate Extensions
- Support tenant-scoped boards and verify cross-tenant topic isolation.
- Add a configurable render cadence and compare 5, 10, and 20 renders per second.
- Add optimistic operator feedback that reconciles with committed state.
8.3 Advanced Extensions
- Run two Phoenix nodes and repeat reconnect tests during a rolling deployment.
- Add a durable incident event log and rebuild the wallboard projection from it.
- Propagate trace and correlation context from command through commit, notice, reload, and render.
9. Real-World Connections
9.1 Industry Applications
- Security operations: Alerts change frequently, but the case database remains authoritative.
- Fleet monitoring: Position or health notices trigger projection reads while disconnect recovery reloads the current fleet view.
- Fulfillment: Warehouse boards need low latency without treating socket delivery as order history.
- Trading and pricing dashboards: Display work is coalesced, while durable ledgers and market feeds retain the actual event record.
9.2 Related Open Source Projects
- Phoenix LiveView: Server-rendered, long-lived interactive processes and lifecycle semantics.
- Phoenix.PubSub: Local and distributed transient topic fanout.
- Phoenix LiveDashboard: A reference for operational visibility and bounded diagnostic presentation.
9.3 Interview Relevance
This project demonstrates more than framework syntax. It provides evidence that you understand transient delivery, derived data, lifecycle races, long-lived authorization, overload control, and testable recovery—core concerns in any real-time distributed UI.
10. Resources
10.1 Essential Reading
- Phoenix.PubSub 2.2 — subscription, broadcast, custom dispatch, and migration semantics.
- Phoenix LiveView lifecycle — connected state and process lifecycle.
- Erlang process signals — pairwise ordering and distributed signal loss.
- “Programming Phoenix LiveView” by Bruce Tate and Sophie DeBenedetto — lifecycle and state management chapters.
- “Designing Data-Intensive Applications, 2nd Edition” by Martin Kleppmann et al. — derived data and stream processing chapters.
10.2 Tools & Documentation
- Telemetry — synchronous handlers and span conventions.
- Phoenix testing — integration testing foundations.
- Browser developer tools — offline mode, WebSocket inspection, and timing.
10.3 Related Projects in This Series
- Project 4: Supplies distributed topic and pool behavior used by the board.
- Project 6: Extends the UI with eventually consistent responder presence.
- Project 7: Measures the mailbox and render pressure this board must control.
11. Self-Assessment Checklist
11.1 Understanding
- I can explain both LiveView mount phases without notes.
- I can explain why PubSub is a notification path rather than board history.
- I can classify stale, duplicate, contiguous, and gapped revisions.
- I can state the race policy between subscribing and loading.
- I can explain why topic routing does not provide authorization.
11.2 Implementation
- Two clients receive one committed update exactly once.
- Reconnect reloads current state without replay assumptions.
- Render cadence is bounded and measurable.
- Diagnostics expose event age, gap recovery, and coalescing.
- Role revocation removes future access and protected socket state.
11.3 Growth
- I can diagram the path from transaction to browser render.
- I documented the delivery guarantees and non-guarantees.
- I can defend which state is durable and which is ephemeral.
- I can explain this design in a systems interview.
12. Submission / Completion Criteria
Minimum Viable Completion
- Two browser sessions update after one committed incident mutation.
- Connected mount subscribes once.
- Reconnect reloads the repository and reaches the current revision.
- Duplicate and gap counters are visible.
Full Completion
- All functional and non-functional requirements pass.
- The deterministic race, reconnect, burst, and authorization tests pass.
- Diagnostics prove the freshness and recovery SLOs.
- The architecture document names PubSub’s transient boundary.
Excellence
- Repeat the experiment across two clustered nodes during rolling restart.
- Produce a before/after report for unbounded versus coalesced rendering.
- Demonstrate trace correlation from command commit through browser handling.
Return to the Pub/Sub in Elixir deep-dive guide or browse the expanded project index.