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:

  1. Explain why a LiveView has disconnected and connected mount phases and subscribe exactly once during the connected phase.
  2. Separate an authoritative incident model from the transient notification that says the model changed.
  3. Detect duplicate, stale, contiguous, and gapped revisions without treating message arrival order as business truth.
  4. Recover a disconnected client by reloading current authorized state instead of expecting PubSub replay.
  5. Coalesce high-frequency notifications into a bounded render cadence while preserving the latest committed facts.
  6. Measure notification age, revision lag, reconnect recovery, mailbox pressure, and render count.
  7. 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

  • broadcast returning :ok means 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

  1. Connected subscription: Subscribe only after the LiveView is connected, with no duplicate registration after reconnect or navigation.
  2. Authoritative load: Render from repository data and expose the revision used for that projection.
  3. Compact event envelope: Include event ID, board ID, incident ID, committed revision, event type, occurred-at timestamp, and schema version.
  4. Revision state machine: Count and handle stale, duplicate, contiguous, and gap notices explicitly.
  5. Reconnect recovery: Reload current state and revision after reconnect; never wait for missed publications.
  6. Render coalescing: Cap visible updates to a configured cadence while preserving the latest repository projection.
  7. Authorization: Verify board access before load/subscribe and again on each recovery/detail reload.
  8. Diagnostics: Make event age, revision lag, reconnect recovery, and coalescing visible in the UI.
  9. Multi-client behavior: A committed action in either authorized operator session updates all connected views.
  10. 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

  1. local_revision never decreases.
  2. Protected board data exists in the socket only while authorization is valid.
  3. A LIVE badge means the last repository load succeeded and no known gap is pending.
  4. At most one render timer is outstanding for a LiveView.
  5. A notification payload is never treated as the durable incident record.
  6. 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

  1. Disconnected versus connected mount
    • Which phase owns a persistent process?
    • Why can subscribing in both phases duplicate delivery?
    • Reference: Phoenix LiveView lifecycle documentation.
  2. PubSub delivery semantics
    • What does broadcast acknowledge?
    • What happens while a subscriber is disconnected?
    • Reference: Phoenix.PubSub 2.2 documentation.
  3. 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.
  4. Long-lived authorization
    • Which permissions can change after mount?
    • How is protected socket state removed?
  5. 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

  1. Does the notice carry only identifiers and revision, or a projection snapshot? Why?
  2. What exact rule distinguishes stale, duplicate, contiguous, and gapped events?
  3. Which query reloads one incident and which reloads the whole board?
  4. What prevents two render timers from being scheduled concurrently?
  5. What will the wallboard display during recovery instead of falsely claiming LIVE?
  6. 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:

  1. Disconnected mount reads revision 20.
  2. Connected mount subscribes.
  3. Revision 21 commits while the connected process loads.
  4. A duplicate revision 21 arrives.
  5. The socket disconnects and misses revisions 22–24.
  6. It reconnects with a new process and loads revision 24.
  7. 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

  1. “Why subscribe only during connected mount?”
  2. “How do you close the read-versus-subscribe race?”
  3. “Why isn’t a PubSub event an event store?”
  4. “How do revisions repair duplicate, stale, and missing notifications?”
  5. “How would you prevent a burst from causing excessive LiveView renders?”
  6. “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

  1. Initial disconnected render does not subscribe.
  2. Connected mount registers exactly once.
  3. Duplicate event ID and equal revision do not rerender.
  4. Lower revision never moves the UI backward.
  5. Revision jump marks the board stale before reload.
  6. Failed reload keeps RECOVERING or shows an error; it never claims LIVE.
  7. Reconnect after three commits loads the newest revision.
  8. Role revocation clears protected state and denies the next reload.
  9. A notification burst respects the render cap and ends at the latest revision.
  10. 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.
  • 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.
  • 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.