← All stories

Voyage · EventFarm Parity Roadmap

Where we are. What's in flight. What we've learned.

The story corpus describes user journeys; this page describes our progress through them. Each closed factory cycle leaves behind a closure markdown, an artifact bundle, and an updated parity matrix entry. Each in-flight or queued cycle has a brief that names what it intends to close. Demoted predicates and surfaced bugs name themselves so the next cycle has a clear target.

Last updated: 2026-04-29 · Cycles closed since 2026-04-29 morning: 3 (EF-074 tightening, W1 mailing, EF-073 EFx Poll) · Source matrix: voyage/docs/parity/eventfarm-capability-matrix.md · Evidence: all test results

Parity matrix overview

100 EventFarm capabilities, status as of the last factory cycle. Counts are deployed-runtime-validated.

Shippable
30
30% — all carry deployed-runtime artifact pointers
Partial
47
47% — substrate exists, predicate gaps documented
Not Ready
4
4% — surface scaffolded, contract incomplete
Absent
19
19% — no implementation found

view full matrix source →

Recently completed cycles

Newest first. Pre/post numbers are passed-probe counts under strict predicates against the deployed surface. The Ratchet pattern: declare → measure → diff → close → re-measure → tighten.

2026-04-29 · Wave J · EFx breadth

EF-073 EFx Poll — attendee + station

Landed

Net-new attendee voting surface and station display surface, built to spec. Reuses the EF-074 visualizer-spec-workflow socket plumbing, snapshot/delta protocol, and tally semantics. Organizer admin (question setup, pause, free-text moderation) intentionally deferred — needs admin auth/action wiring.

Pre 8/56 passed Post 17/57 passed Δ green +9 probes Verdict Partial (organizer-admin lane explicit)
  • Closed: socket-reconnect-poll-snapshot, out-of-order-poll-deltas, late-join-poll-state, poll-results-gated, idempotent-poll-vote, cross-station-poll-isolation
  • In-scope demoted: poll-5k-load — F88 evidence has 206 missing/dropped acks under strict zero-drop predicate
  • Out-of-scope deferred: pause-freezes-poll, free-text-moderation, organizer question-type setup
  • Server-side note: workers/events doesn't yet expose /v1/live/events/{eventId}/polls/{pollId}/votes — idempotency proven via harness transport interception, real endpoint shape is follow-up
2026-04-29 · Wave J · operational

W1 mailing 1/100 stuck-sending edge

Landed

Mailing-queue-ops Deliverable 3 closed: 99/100 → 100/100 within five-minute SLO. Root cause: duplicate-invocation race between request-side waitUntil fast path and Cloudflare Queue consumer; missing stale-sending reconciler. Fix shipped both paths (defense in depth).

Pre 99/100 terminal Post 100/100 terminal Outbox 100/100 cross-check DLQ growth 0 DO delta 0 / 1M
  • Diagnosis: AE recorded a single successful queue message for the stuck mailing with queued_count=0; D1 showed 31 recipient + 31 outbox rows; mailing row remained sending with zero counters
  • Fix layer 1: hardened retry/finalize path to recover the zero-row duplicate without breaking the 5-min SLO fast path
  • Fix layer 2: added notifications-cron reconciler that rebuilds counters for stale sending rows from recipient/outbox evidence
  • Tests: Vitest coverage on both paths; deployed and verified before re-running concurrency proof
2026-04-29 · Wave J · EFx breadth

EF-074 Visualizer — Cycle 2 tightening

Landed

Three demoted predicates from Cycle 1 got strict-predicate teeth. Result: zero probes flipped green — instead, three real client-side bugs were surfaced as honest failures. The Ratchet doing its job: replacing demotion-by-skip with demotion-by-failure-with-evidence.

Pre 38/65 passed Post 38/65 passed New strict failures 3 (real product bugs) Verdict Partial (3 client gaps documented)
  • New harness adapter: visualizer-ws-adapter.mjs for delta injection — out-of-order, foreign-event
  • New harness wrapper: load-harness-wrapper.mjs wraps F88 substrate as a story-runner predicate
  • Surfaced bug 1: out-of-order release regresses to held seq N tally
  • Surfaced bug 2: foreign-event delta mutates local visualizer tally (cross-station isolation gap at the client)
  • Surfaced bug 3: 5k load wrapper finds 206 missing acks despite p99 512ms (zero-drop strictness fails the load substrate)
2026-04-29 · Wave J · EFx breadth

EF-074 Polling Results Visualizer — Cycle 1 build

Landed

First end-to-end execution of the Ratchet on a real EF row. Net-new visualizer-spec-workflow component built to spec, deployed alongside the legacy visualizer, then runtime-probed by the story harness. Old visualizer-workflow.tsx retained as reference until the 24-hour verification window completes.

Pre 4/65 passed Post 38/65 passed Δ green +34 probes Verdict Partial → Cycle 2 tightening dispatched
  • Closed: socket-reconnect-resilience, attendee-late-join-shows-prior-state, results-visible-only-after-close, display-only contract, distance-contrast (7:1), prefers-reduced-motion, above-fold
  • Demoted (then closed in Cycle 2): out-of-order-message-tolerance, n-concurrent-load-stable, cross-station-isolation
  • Deferred: organizer-pause-mid-poll (admin auth), idempotent-vote (EF-073 lane)
  • Pages deploy: 2eae434f serving visualizer.vxge-aperture.porivo.com/poll-results

In-flight

Currently no cycle dispatched. Add an entry here when dispatch-codex.sh is fired and the run dir's status is RUNNING.

— none —

Queued factories

Ready to dispatch when an operator is at the keyboard. Briefs at ~/voyage-ef-parity/factories/.

Wave J · EF-074 Cycle 3 candidate

Visualizer client-side bug fixes (3 surfaced gaps)

Queued (no brief yet)

Address the three real bugs the EF-074 tightening cycle surfaced. Each is a discrete client-side fix in visualizer-spec-workflow.tsx; the harness adapters are ready to re-verify under strict predicates.

  • Out-of-order delta replay regression in visualizer-spec-workflow.tsx release path
  • Foreign-event delta filtering at the WebSocket dispatch boundary (client-side cross-station isolation)
  • F88 substrate ack-loss investigation (206/5000 missing acks; substrate-level, not just client)
Wave J · EF-073 / EF-074 shared

EFx organizer admin surface + admin auth

Queued (no brief yet)

Closes the organizer-admin lanes deferred from both EF-073 and EF-074: organizer-pause-mid-poll, free-text moderation, question-type setup, start/pause/close CTAs. Needs admin auth/action wiring before any of these predicates can flip green.

  • Predicates blocked on this: organizer-pause-mid-poll, free-text-moderation, organizer-side question-type-single-multi-free-text
  • Stories affected: EF-073 (5 deferred modes), EF-074 (1 deferred mode)
  • Likely scope: admin route in visualizer-pages (or new admin-pages app), admin-auth handshake, organizer CTAs
Wave J · EFx breadth

EF-077 Access Control + EF-078 Lead Retrieval

Queued (no brief yet)

Two more EFx rows in the same trunk (efx-station-shell). EF-077 needs native NFC hardware proof to fully close; EF-078 has a sponsor-mobile-app gap. Both can be partially closed using the EF-073 net-new pattern — attendee/station-side first, hardware/native-side later.

  • EF-077: scan UI surface, capacity-deny state, late-policy-allow-or-deny state, per-scan audit row
  • EF-078: scoped-station shell, custom-field validation, sponsor-only export gate
  • Hardware/native-side stays deferred until Detox/XCUITest harness is wired (see native-app-shell trunk)
Wave J/II — roadmap workstreams

W3 Stripe Payments, W4 Gate 7 fleet, W5–W8

Queued (deferred — credentials/scale)

Tracked in docs/planning/wave-j-ii-roadmap.md. W3 blocked on Stripe token/account access. W4 blocked on Gate 7 fleet provisioning. W5 (50k VM driver), W6 (operating history), W7 (pen test), W8 (SOC 2 + DSR + PCI) are all sequenced after W3/W4.

Real bugs surfaced — what changes as a result

Strict predicates flushed these out. Each is a documented gap with a known target cycle. None of them existed in the pre-Ratchet view of the world.

EF-074 / Cycle 2

Out-of-order delta release regresses to held tally

Adapter holds seq N, injects N+2 first, then releases N. Visualizer reverts to the seq-N tally instead of converging on the strictly-ordered final state. Expected: snapshot-replace semantics, or queue-and-replay. Actual: latest-wins.

target: visualizer-spec-workflow.tsx · evaluator: out-of-order-message-tolerance

Evidence probe finding
EF-074 / Cycle 2

Foreign-event delta mutates local tally

Adapter injects a delta whose envelope eventId is foreign. The visualizer's WebSocket message handler doesn't filter by event id, so the foreign delta updates local state. Server-side DO boundary holds; client-side filter missing.

target: WS dispatch boundary in visualizer client · evaluator: cross-station-isolation

Evidence probe finding
EF-074 + EF-073 shared

F88 substrate: 206/5000 missing acks

Existing F88 5k-concurrent evidence has 206 ack misses despite p99 of 512ms (under the 1500ms story threshold). Strict zero-drop predicate fails. Latency is fine; reliability is not. Substrate-level, not client.

target: workers/events/ + DO ack pipeline · evaluator: n-concurrent-load-stable

EF-073 / Cycle 1

Real EF-073 vote endpoint shape missing

Story expects POST /v1/live/events/{eventId}/polls/{pollId}/votes. Existing live votes are /v1/live/sessions/:sessionId/votes with vote_id dedupe. Idempotency proven via harness transport interception this cycle, but real endpoint shape is follow-up.

target: workers/events/src/routes/live.ts · evaluator: api-call on idempotent-poll-vote

EF-073 / Cycle 1

Inherited tier fixture mismatches still demoted

Generic ui-progress-bar value-state assertions, ui-data-table handler/state fixtures, and ui-status-pill fixture labels don't map cleanly to display-only EFx instances. Stays demoted rather than force-passed.

target: tier-1 fixture parameterization in apps/stories-pages/stories/component/

What we've learned

Cross-cycle observations. These shape how we run the next factory.

The Ratchet pattern actually works on real EF rows

Three cycles in a row (EF-074 build, EF-074 tighten, EF-073 build) all closed end-to-end with the same shape: declare → measure → diff → close → re-measure → tighten. Each leaves a closure markdown, an artifact bundle, pre/post JSONL, and a matrix re-stamp. The pattern is now repeatable; subsequent EFx rows arrive with significantly less per-cycle setup overhead.

Honest demotions stay demoted — and the second cycle pays it back

EF-074 Cycle 1 demoted 5 predicates with documented blockers. Cycle 2 closed 3 of them (out-of-order, cross-station, n-concurrent) by building the named infrastructure (visualizer-ws-adapter.mjs, load-harness-wrapper.mjs). Result: zero probes flipped green numerically, but three real bugs got surfaced as strict failures instead of soft skips. The demotion ledger is paying compound interest.

Net-new beats refactor for spec-driven UI components

EF-074 Cycle 1 chose net-new build over incremental refactor of the existing visualizer-workflow.tsx. Reasoning held up: the existing component had zero spec'd data-test attributes, no tier-3 inheritance points, no display-only marker. Net-new also produced a clean reuse boundary (socket plumbing, snapshot/delta, module-switch FSM) that EF-073 inherited directly — Cycle 1 of EF-073 reused the EF-074 hooks instead of reinventing them.

Strict predicates require both transport AND end-state correctness

Every passOrFail() in these cycles requires more than 2xx. idempotent-vote requires the server-side tally to count exactly one. cross-station-isolation requires zero local tally mutation. color-contrast requires minRatio: 7 (distance-viewing target, not the default 4.5). Lax predicates were how the legacy mailing flow showed 99/100 looking like a pass — strict predicates surface the gap.

Tenant scope: workers live in voyage, not voyage-aperture

Lesson from the W1 mailing factory. The platform repo (voyage) owns workers, shared packages, and the parity matrix. voyage-aperture owns the tenant deployment surface — React tenant-shell, story corpus, harness, tenant-specific configs, deploy scripts. The W1 mailing brief incorrectly named voyage-aperture as the worker home; the factory ignored that and committed correctly. Future briefs should target both repos when worker code + matrix re-stamp + closure docs all need updates.

Defense in depth on operational fixes — both the race AND the reconciler

W1 closed the stuck-sending edge with two layers: (1) hardened the duplicate-invocation race in the request-side fast path, and (2) added a notifications-cron reconciler that rebuilds counters for stale sending rows from recipient/outbox evidence. Either alone would have closed the observed case; together they cover the class of bug. Operational fixes earn their second layer.

Codex factory dispatch is reliable when serialized; parallel risks CreateProcess Rejected stalls

Three cycles dispatched serially today, all completed cleanly via dispatch-codex.sh with gpt-5.5 + high reasoning + sandbox bypass. The CreateProcess Rejected 0% CPU stall pattern (cpu_time=0s, stdout_age >60s) was not observed today. Continue serializing until that mode is understood.

The factory's mid-flight push can diverge from local polish

EF-074 Cycle 1's harness commit pushed to origin (aa13a25) before local polish landed (45f6de2 — same parent, 22 more lines of delta-injection gating + hex/srgb color parsing). Resolved with a merge commit preserving the local superset. Pattern to watch: factory-pushed commit + local polish on the same surface during the same session.

How this page stays current

Hand-maintained for now. Future automation possible from the underlying data sources.

After each completed factory:

  • Add a card to Recently completed cycles with pre/post numbers, key wins, demotions, and commit hashes.
  • Run pnpm exec node apps/stories-pages/scripts/publish-findings.mjs to copy new JSONL/proof artifacts into the published findings directory.
  • Add screen links, post-cycle test-results links, and pre-cycle baseline links to the cycle card.
  • Move any closed bugs from Real bugs surfaced to the cycle's Closed list.
  • Add new bugs the cycle surfaced to Real bugs surfaced, with evidence links to the specific /findings?run=...&probe=... row when available.
  • Update the matrix counts at the top from voyage/docs/parity/eventfarm-capability-matrix.md.
  • Rebuild and redeploy voyage-aperture-stories; UI deploys still happen from their own app/project.
  • If a new pattern emerged, add a learning card.

Data sources: matrix at voyage/docs/parity/eventfarm-capability-matrix.md · findings at apps/stories-pages/public/findings/ · closure markdowns at voyage-aperture/docs/parity-factory/ and voyage/docs/parity-factory/ · briefs at ~/voyage-ef-parity/factories/ · artifact bundles at voyage-aperture/artifacts/parity-factory/.