JRNI -> Voyage Current State Comparison (Deep Forensic)
Evaluation date: 2026-04-24.
This document compares the legacy JRNI / BookingBug estate with the current Voyage platform repository and the Meridian National Bank tenant proof repository.
The briefing remains evidence-heavy, but it now presents that evidence as narrative reference material rather than one-claim-per-line deposition notes.
1. Framing
Voyage is the headless Cloudflare-native platform repo for the JRNI / BookingBug rebuild, and it owns workers, shared packages, docs, and deployment patterns rather than tenant UI applications (CLAUDE.md:1-22). Tenant UI implementations and customer-specific forks belong in peer repositories such as voyage-bank, while the legacy source tree remains available as sibling repositories, with bookingbug identified as the core monolith (CLAUDE.md:42-50). This comparison therefore treats JRNI as a mature federated SaaS estate and Voyage as a Cloudflare-native per-tenant platform with tenant forks (CLAUDE.md:1-22).
The comparison is freshly researched from current repository evidence. The cited parity matrix records the 2026-04-05 59-of-59 parity claim and explicitly distinguishes tenant-runtime parity from legacy SaaS control-plane machinery (docs/specs/jrni-feature-parity-matrix.md:43-58; docs/specs/jrni-feature-parity-matrix.md:1-25). Where Meridian documents describe broken or demo-quality live surfaces, this briefing keeps those caveats intact rather than upgrading them rhetorically; where later Meridian materials describe fixed admin rendering and regional fanout, this briefing keeps that later checkpoint separate from the earlier audit defects and from the April 24 re-check that found material remediation plus residual unevenness (../voyage-bank/voyage-bank-reality-audit.md:8-21; ../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:339-361; ../voyage-bank/docs/reality-roadmap.md:5-28). The source basis for all of that work is the current local checkout state summarized in the repository evidence and the two checked-out repository SHAs listed at the bottom of this file (CLAUDE.md:42-50).
2. Executive Summary
Voyage has a documented full JRNI feature-parity baseline: after the 2026-04-05 final review, the matrix records 59 Complete and 0 Partial capabilities in scope (docs/specs/jrni-feature-parity-matrix.md:43-58). That parity does not mean the hosting model stayed the same. Voyage deliberately favors isolated per-customer Workers and D1 databases over JRNI-style shared runtime feature switches (CLAUDE.md:1-22), and it decomposes capability into domain Workers for control plane, auth, catalog, availability, booking, events, payments, notifications, integrations, queue, and reporting (docs/architecture/v2-enterprise-plan.md:155-177). The underlying stack similarly replaces JRNI-era infrastructure categories with Workers, D1, R2, Queues, Durable Objects, WebSockets, Cache API, KV, and Worker Secrets (docs/architecture/v2-enterprise-plan.md:138-152).
Meridian National Bank is the proof deployment for that model: the design describes 380 branches, 208,000 employees, 68 million customers, network-scale UIs, and continuous bank traffic simulation (../voyage-bank/docs/architecture/voyage-bank-design.md:3-23), while the roadmap records all 10 regional ops databases loaded with 1,696,932 sessions, 702,912 bookings, and 926,566 queue entries (../voyage-bank/docs/reality-roadmap.md:49-68). The strongest architectural differentiator remains the unified service_sessions spine, where appointments, walk-ins, and event registrations share the same staff-occupancy record (docs/architecture/unified-session-architecture.md:5-12). Beyond-parity work added an API contracts package, session-centric audit logging, a command engine with reversible snapshots, real-time presence, bulk actions, assignment ranking, calendar reconciliation, and role-lane workload generation (../voyage-bank/docs/architecture/voyage-bank-factories/f31-beyond-parity.md:1-18). JRNI still retains meaningful strengths in mature multi-tenant SaaS operations, runtime configurability, and extensive legacy deployment machinery (docs/analysis/deploy.md:5-18), so the honest current read is that Meridian’s backend route families and D1 plumbing are materially broader than its deployed frontend polish, with top-level admin data wiring now mostly live-backed but still uneven (../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:285-301; ../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:359-359).
3. Two Platforms At Different Lifecycles
| Dimension | JRNI legacy state | Voyage current state | Review implication |
|---|---|---|---|
| Runtime shape | Rails monolith plus related services; Rails 4.0.13 appears in the Gemfile. ../bookingbug/Gemfile:86-116 |
Domain-split Cloudflare Workers are listed as the platform module map. docs/architecture/v2-enterprise-plan.md:155-177 |
Compare operating models, not only feature names. |
| Data shape | JRNI uses MySQL, Redis, ar-octopus sharding, Sidekiq, Pusher, AWS SDKs, and related gems. ../bookingbug/Gemfile:86-116 |
Voyage uses D1 databases per tenant, with config, ops, and archive roles. docs/architecture/v2-enterprise-plan.md:73-121 |
Voyage turns tenant isolation into a first-class data boundary. |
| Deployment shape | JRNI deployment analysis identifies Capistrano, host inventories, AWS metadata, and service restarts. docs/analysis/deploy.md:5-18 |
Voyage is Cloudflare-native with Workers, D1, R2, Queues, DOs, KV, and Pages in tenant forks. docs/architecture/v2-enterprise-plan.md:138-152 |
Voyage reduces platform moving parts by moving work to Cloudflare primitives. |
| Configuration shape | JRNI Configurator manages config, CSS, translations, JavaScript, themes, and deployments via Express, React, MongoDB, and S3. docs/analysis/configurator-api.md:4-24 |
Voyage anti-goals reject runtime JS injection, broad behavior stores, and shared-codebase feature switches. docs/architecture/anti-goals.md:1-31 |
Voyage chooses code-as-configuration over operator-editable runtime mutation. |
| Calendar/integration shape | JRNI Exchange integration spans .NET services, RabbitMQ, Redis, MySQL, Ruby connector code, and legacy EWS. docs/analysis/exchange-integration-deep-dive.md:5-17 |
Voyage integration architecture is per-tenant, event-driven, Worker-based, and D1-backed. docs/architecture/per-tenant-integrations.md:76-130 |
Voyage narrows integration blast radius by tenant and by Worker. |
| Staff-time model | JRNI has scheduled slots, spaces, and queue-centric live status rather than one universal staff-time primitive. docs/analysis/jrni-overlap-handling.md:9-12 |
Voyage elevates service_sessions as the primary interaction record across source tables. docs/architecture/unified-session-architecture.md:5-12 |
Voyage’s primary model is operational occupancy rather than product silo. |
JRNI’s slot and space models show how much legacy meaning was spread across specialized records: Slot encodes booked, blocked, waitlisted, queue, and externally created states and associates to person, service, resource, company, session, clinic, and recurring block records, while Space belongs to a slot and carries queue, guest, notes, attachments, and object-mapping relationships (../bookingbug/app/models/slot.rb:44-60; ../bookingbug/app/models/slot.rb:19-35; ../bookingbug/app/models/space.rb:13-35). Voyage’s v2 plan answers that sprawl with a more explicit domain split across control plane, auth, hierarchy, tenant config, catalog, scheduling, customers, booking, payments, events, queue, notifications, integrations, extensibility, automation, and reporting, and it marks company hierarchy, tenant config, payments, extensibility, integrations, and queue-concierge as first-class concerns rather than afterthoughts (docs/architecture/v2-enterprise-plan.md:155-177; docs/architecture/v2-enterprise-plan.md:207-219).
4. Parity Baseline
The parity matrix is the canonical audit of Journey/JRNI functionality versus Voyage implementation, and it is explicit that this is a reference-implementation audit rather than a mandate to copy JRNI’s hosting model (docs/specs/jrni-feature-parity-matrix.md:1-25). Its standard for Complete is real code and persistence at a level that should not surprise a JRNI operator (docs/specs/jrni-feature-parity-matrix.md:1-25). The summary table records 57 Complete, 0 Partial, 0 Planned, 0 Missing, and 2 Anti-Goal rows, while the bottom-line paragraph states 59 Complete / 0 Partial out of 59 after the final 2026-04-05 review (docs/specs/jrni-feature-parity-matrix.md:43-58). The safest reading is therefore that 59 tenant-runtime capabilities were treated as complete while two SaaS-runtime configuration capabilities were categorized as anti-goals (docs/specs/jrni-feature-parity-matrix.md:43-58).
That baseline is broad. Appointment and booking parity covers public booking, availability lookup, basket checkout, self-service lookup, reschedule/cancel, admin lists, admin create/edit/reschedule, status workflow, notes, questions, attendees, waitlist, external bookings, payments, communications, and notification preferences (docs/specs/jrni-feature-parity-matrix.md:63-80). Events, queue, kiosk, notifications, automation, tenant configuration, integrations, payments, reporting, and audit logs are all represented in completed rows (docs/specs/jrni-feature-parity-matrix.md:82-145). Configurator-style CSS, JS, translations, and broad feature flags are marked as anti-goals rather than missing features (docs/specs/jrni-feature-parity-matrix.md:147-159), while Exchange sync, Google calendar parity, integration webhooks, payments, reporting dashboards, and audit exports are still marked Complete in the integrations section (docs/specs/jrni-feature-parity-matrix.md:161-170). The final audit note says both demoted capabilities were later reimplemented and reverified, bringing the total back to 59 Complete / 0 Partial (docs/specs/jrni-feature-parity-matrix.md:172-180).
4.1 Dated Reconciliation Note
The 59 Complete / 0 Partial matrix claim is a source-code-level parity statement recorded on 2026-04-05, not a blanket claim that every Meridian surface was already deployment-clean that day (docs/specs/jrni-feature-parity-matrix.md:43-58).
The checked-in Meridian reality audit answers a different question: whether the deployed admin was an honest, fully working live enterprise surface at the 2026-04-08 audit checkpoint. Its answer is no, because hardcoded numbers, missing routes, and internal errors still existed then (../voyage-bank/voyage-bank-reality-audit.md:3-10). The April 24 re-check keeps the core honesty conclusion but narrows the gap substantially: broken Apr 8 route/path defects are largely closed, while remaining issues are now concentrated in fallback/default behavior and deep surfaces (../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:47-59; ../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:340-361).
The current checked-in Meridian roadmap should be read as the later remediation checkpoint. Its body still carries a Date: 2026-04-08 header, but the repository version of that file was last modified on 2026-04-12, and the checked-in text reports 13 of 13 admin pages rendering with real data after route and fanout fixes (../voyage-bank/docs/reality-roadmap.md:3-16; ../voyage-bank/docs/reality-roadmap.md:82-109).
Read as a timeline, these are not contradictions. They are four different statements about different layers: parity coverage on 2026-04-05, live deploy defects observed on 2026-04-08, a 2026-04-12 remediation checkpoint confirmed by the current roadmap revision, and a 2026-04-24 re-check that confirms major route and wiring fixes while quantifying remaining unevenness (docs/specs/jrni-feature-parity-matrix.md:43-58; ../voyage-bank/voyage-bank-reality-audit.md:8-21; ../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:339-361; ../voyage-bank/docs/reality-roadmap.md:82-109).
| Capability family | Matrix state | Current interpretation |
|---|---|---|
| Appointments and booking | Complete rows cover booking flow, availability, checkout, self-service, admin management, status workflow, notes, questions, attendees, waitlist, external bookings, payments, comms, and preferences. docs/specs/jrni-feature-parity-matrix.md:63-80 |
The parity baseline is broad enough to cover operational appointment management rather than only public booking. |
| Events and registration | Complete rows cover listing, registration, series, occurrence management, capacity, pricing, courses, invitations, imports, and event ops. docs/specs/jrni-feature-parity-matrix.md:82-145 |
Event parity is tenant-local and operational, not just public event pages. |
| Queue and kiosk | Complete rows cover queue backend, queue board, walk-in join, kiosk lookup/check-in, offline recovery, threshold notifications, and escalation. docs/specs/jrni-feature-parity-matrix.md:82-145 |
Queue parity is especially relevant because Voyage unifies queue work with staff occupancy. |
| Notifications and automation | Complete rows cover template CRUD, transactional send, reminders, follow-ups, attendee mailings, campaigns, and channel opt-outs. docs/specs/jrni-feature-parity-matrix.md:82-145 |
Messaging parity is real enough to drive lifecycle automation, though tenant rollout still matters. |
| Configuration and branding | Runtime configurator and feature flags are anti-goals. docs/specs/jrni-feature-parity-matrix.md:147-159 |
Voyage intentionally does not rebuild JRNI’s generic runtime behavior platform. |
| Integrations and reporting | Exchange, Google, webhooks, payments, reports, and audit exports are Complete. docs/specs/jrni-feature-parity-matrix.md:161-170 |
Parity extends beyond scheduling into integration and operational evidence surfaces. |
5. Beyond Parity: F24-F35 Factory Work
Beyond-parity work is easiest to understand as a deliberately layered progression. F24 created a canonical @voyage/api-contracts package with Zod schemas for staff-facing booking action endpoints and missing admin client functions, and the current core repo now exposes cancel, reschedule, reassign, complete, no-show, check-in, notes, comms, reverse, bulk, responses, events, native, EFX, and reports from packages/api-contracts (../voyage-bank/docs/architecture/voyage-bank-factories/f24-api-contracts.md:1-23; packages/api-contracts/src/index.ts:1-15). F25 replaced audit-as-notes with a dedicated session-centric audit log, a queryable role-scoped audit model, and reversal-token support, which is visible today in booking_audit_log writes that capture actor, role, action, old/new values, request ID, reversible window, and reversal token metadata (../voyage-bank/docs/architecture/voyage-bank-factories/f25-audit-log.md:1-23; packages/booking-audit/src/audit-log.ts:136-185). F26 then extracted booking transitions into a command engine with standard CommandContext, CommandResult, and side-effect handling, and the current command package exports cancel, reschedule, reassign, complete, no-show, check-in, start-serving, waitlist promotion, add-note, pending-cancellation, restore-from-pending, reverse-action, and helpers (../voyage-bank/docs/architecture/voyage-bank-factories/f26-command-engine.md:61-102; packages/booking-commands/src/index.ts:1-17). The cancel command returns calendar-sync and notification side effects and records a reversible audit event, while the reverse command claims a reversal token, reapplies the saved snapshot, emits calendar-sync and notification side effects, and writes a reverse audit entry (packages/booking-commands/src/cancel.ts:18-85; packages/booking-commands/src/reverseAction.ts:14-76).
F28 and F29 moved that backend shape into reusable and tenant-consumable UI layers. F28 created @voyage/booking-ui-kit with hooks, reference components, detail panel, modals, undo toast, audit trail, and bulk UI foundations (../voyage-bank/docs/architecture/voyage-bank-factories/f28-booking-ui-kit.md:1-25), and the current kit includes a WebSocket-backed usePresence hook, a BulkActionBar for selected booking IDs, and an AuditTrailSection that renders actor, role, relative time, and JSON detail (packages/booking-ui-kit/src/hooks/usePresence.ts:70-195; packages/booking-ui-kit/src/components/BulkActionBar.tsx:1-40; packages/booking-ui-kit/src/components/AuditTrailSection.tsx:1-77). F29 adopted that UI kit into the Meridian staff app, moved appointment state into the URL, and targeted full staff appointment actions plus undo-toast wiring (../voyage-bank/docs/architecture/voyage-bank-factories/f29-staff-pages-adoption.md:1-18). F31 then packaged the clearest beyond-parity differentiators together: reversible undo snapshots, real-time booking presence and edit locks through Durable Objects, and bulk cancel/reassign flows for sick-staff operations (../voyage-bank/docs/architecture/voyage-bank-factories/f31-beyond-parity.md:1-18). That work is now visible in the booking worker’s exported BookingPresenceDO and its BOOKING_PRESENCE Durable Object binding (workers/booking/wrangler.toml:15-22).
The remaining factories push scheduling, assignment, and data generation beyond the legacy baseline. F32 built the assignment engine around skill match, workload balance, round robin, next availability, customer value, specialty, and language match (../voyage-bank/docs/architecture/voyage-bank-factories/f32-assignment-engine.md:17-103). F33 closed the calendar-sync loop by routing inbound Exchange and Google changes through the command engine with pending-cancellation, move rejection, and decline handling, and the current Exchange sync worker processes inbound and outbound queue messages, runs a five-minute pending-cancellation cron, and renews subscriptions on schedule (../voyage-bank/docs/architecture/voyage-bank-factories/f33-calendar-sync-reconciliation.md:1-18; workers/exchange-sync/src/index.ts:1-116). F34 added a canonical service-role matrix so generated data cannot assign staff outside their role lane (../voyage-bank/docs/architecture/voyage-bank-factories/f34-service-role-matrix.md:122-147). F35 replaced branch-total generation with role-lane daily workload generation that includes staff event blocking, queue interactions, event schedules, registrations, attendance, follow-ups, and output rows across bookings, service_sessions, queue entries, events, registrations, orders, and staff status (../voyage-bank/docs/architecture/voyage-bank-factories/f35-role-lane-generator.md:28-119).
| Factory | Beyond-parity contribution | Implemented code or current artifact |
|---|---|---|
| F24 | Reusable API contracts instead of route-local schema drift. | packages/api-contracts/src/index.ts exports the current contract surface. packages/api-contracts/src/index.ts:1-15 |
| F25 | Dedicated audit log with role-scoped query and reversal metadata. | recordAuditEvent inserts structured audit rows and optional reversal tokens. packages/booking-audit/src/audit-log.ts:136-185 |
| F26 | Command engine with side-effect list and reversible snapshots. | cancelBookingCommand builds reversal info, writes audit, and returns side effects. packages/booking-commands/src/cancel.ts:18-85 |
| F28 | Tenant-consumable UI hooks/components for booking actions. | Presence, bulk bar, and audit trail components exist in the UI kit. packages/booking-ui-kit/src/hooks/usePresence.ts:70-195; packages/booking-ui-kit/src/components/BulkActionBar.tsx:1-40; packages/booking-ui-kit/src/components/AuditTrailSection.tsx:1-77 |
| F29 | Staff app adoption with URL-state, action modals, undo toast, and inspectable appointments. | Factory scope and acceptance checklist define staff-pages adoption. ../voyage-bank/docs/architecture/voyage-bank-factories/f29-staff-pages-adoption.md:1-18; ../voyage-bank/docs/architecture/voyage-bank-factories/f29-staff-pages-adoption.md:99-170 |
| F31 | Undo, presence/edit locks, and bulk sick-staff operations. | Durable Object presence class exists and the booking worker exports it. workers/booking/src/durable-objects/presence.ts:185-256; workers/booking/src/index.ts:1-51 |
| F32 | Smart assignment and reassignment policy. | Factory defines ranking strategies and proposal API. ../voyage-bank/docs/architecture/voyage-bank-factories/f32-assignment-engine.md:17-103 |
| F33 | Inbound calendar reconciliation through command engine. | Exchange worker has queue and scheduled handlers for inbound/outbound sync and pending cancellations. workers/exchange-sync/src/index.ts:1-116 |
| F34 | Role-service eligibility control for generated data. | Factory forbids fallback to any staff when no service-qualified staff exist. ../voyage-bank/docs/architecture/voyage-bank-factories/f34-service-role-matrix.md:122-147 |
| F35 | Role-lane workload generation with event scheduling. | Factory requires session, booking, queue, event, order, and staff-status outputs. ../voyage-bank/docs/architecture/voyage-bank-factories/f35-role-lane-generator.md:28-119 |
6. Meridian National Bank Proof Point
Meridian is a separate deployment repository rather than a subfolder or runtime dependency of the platform repo (../voyage-bank/docs/architecture/voyage-bank-design.md:3-23). Its design calls for a self-contained tenant repo, customer-specific Workers, customer-specific D1 topology, and no runtime tenant feature flags, and it names 380 branches, 208,000 employees, 68 million customers, national-to-branch drilldown, hotspot detection, cross-branch routing, UTO-backed staff timelines, and continuous simulation (../voyage-bank/docs/architecture/voyage-bank-design.md:3-23). The same design uses one config DB, ten current regional ops shards, yearly regional archive shards, a customer directory, sixty-four customer shards, reporting, simulation, and R2 buckets, with bookings, queue entries, event registrations, service_sessions, orders, and active notes living in those hot regional shards (../voyage-bank/docs/architecture/voyage-bank-design.md:124-150; ../voyage-bank/docs/architecture/voyage-bank-design.md:171-200). Customer profiles are routed through a customer directory and 64 deterministic customer shards (../voyage-bank/docs/architecture/voyage-bank-design.md:215-247).
The implementation plan translates that design into a launch target of 380 branches, 208,000 staff, 68 million customers, 10 regions, 64 customer shards, roughly 90 D1 databases, five Pages surfaces, runtime workers, and a simulation stack (../voyage-bank/docs/architecture/voyage-bank-implementation-plan.md:3-18). It enumerates those launch databases as one config DB, ten current ops DBs, ten prior-year archive DBs, one customer directory, sixty-four customer shards, one reporting DB, and three simulation DBs (../voyage-bank/docs/architecture/voyage-bank-implementation-plan.md:167-203), and it makes generate-topology.ts responsible for Wrangler files, all 90+ D1 bindings, R2 bindings, queue bindings, Durable Object bindings, and generated shard-routing code (../voyage-bank/docs/architecture/voyage-bank-implementation-plan.md:158-166).
The roadmap then shows what that topology was used for. It states that all 10 regional ops databases were loaded with 1,696,932 sessions, 702,912 bookings, and 926,566 queue entries (../voyage-bank/docs/reality-roadmap.md:49-68); that 13 admin pages were rendering with real data after route/schema remediation and cross-region fanout refactors (../voyage-bank/docs/reality-roadmap.md:82-110); and that the UI fix factory built shared ops-fanout and customer-fanout helpers while refactoring reporting routes that had previously assumed a single ops shard (../voyage-bank/docs/reality-roadmap.md:111-149). The same roadmap still identifies reporting rollup freshness and live simulator work as the remaining path for keeping dashboards truthful at the right edge (../voyage-bank/docs/reality-roadmap.md:241-259).
That should still be read alongside the ecosystem audit, which lists five frontend surfaces and twenty-three workers, says the booking surface is functional while staff, kiosk, observer, and admin surfaces need varying levels of repair or positioning, and concludes that the backend is materially more complete than the frontends, with frontend-to-gateway alignment as the biggest systemic problem (../voyage-bank/docs/ecosystem-audit.md:19-64; ../voyage-bank/docs/ecosystem-audit.md:5-18).
| Meridian proof dimension | Evidence | Reviewer conclusion |
|---|---|---|
| Branch scale | 380 branches are named in the design and later verified as real config-side branch data in roadmap/audit docs. ../voyage-bank/docs/architecture/voyage-bank-design.md:3-23; ../voyage-bank/docs/reality-roadmap.md:82-110 |
This is not a four-location sample; it is a national-branch topology. |
| Operational data scale | 1,696,932 sessions, 702,912 bookings, and 926,566 queue entries are recorded as loaded across 10 regional ops DBs. ../voyage-bank/docs/reality-roadmap.md:49-68 |
D1 was exercised as a sharded operational data estate. |
| Shard model | The design uses ten regional hot ops shards plus archives and customer profile shards. ../voyage-bank/docs/architecture/voyage-bank-design.md:124-150 |
Meridian proves the multi-D1 approach at tenant scale. |
| Admin realness | The roadmap says 13/13 admin pages rendered with real data after fanout fixes. ../voyage-bank/docs/reality-roadmap.md:82-110 |
Admin proof improved after early audit defects. |
| Remaining honesty gap | The Apr 8 audit warns that hardcoded tour claims and failing endpoints made the earlier live admin deploy less trustworthy than its backend wiring (../voyage-bank/voyage-bank-reality-audit.md:8-21), while the Apr 24 re-check reports broad remediation with most top-level admin now real-backed and a smaller set of still-mixed or broken surfaces (../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:47-59; ../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:359-359). |
The proof point is real infrastructure, not uniformly finished product polish. |
7. Architectural Differentiation
7.1 Unified Session / Occupancy Model
The unified session architecture states that service_sessions is the primary record for every customer interaction and that source tables record how the customer arrived (docs/architecture/unified-session-architecture.md:5-12). The same architecture identifies service_sessions, session_effects, session_audit_log, event series/occurrences, and carts/cart_items as the core table family (docs/architecture/unified-session-architecture.md:14-39). The unified-time occupancy specification then frames appointments, event participation, and queue serving as the same staff-time occupancy problem, with one day timeline for staff and one utilization/load-balancing surface for administrators (docs/architecture/unified-time-occupancy-spec.md:7-17; docs/architecture/unified-time-occupancy-spec.md:18-39; docs/architecture/unified-time-occupancy-spec.md:41-69).
Current code follows that model closely. The public booking checkout path creates a service_sessions row with source_type = appointment (workers/booking/src/routes/checkout.ts:400-432), booking routes synchronize appointment booking state back into service_sessions for being-served, completed, no-show, check-in, cancellation, and schedule changes (workers/booking/src/routes/bookings.ts:688-830), the queue join path writes both a queue_entries row and a service_sessions row with source_type = walkin and status waiting_in_queue (workers/queue/src/routes/entries.ts:896-945), and the event sync helper upserts service_sessions rows with source_type = event while preserving conflict-sensitive assignment behavior (workers/events/src/session-sync.ts:77-193). Staff action routes then read service_sessions, update the unified session state, and propagate changes back to bookings, queue entries, or event registrations depending on source type (workers/staff-feed/src/routes/actions.ts:41-238), while the staff feed builds a day schedule from those unified session rows and maps appointment, walk-in, event, break, blocked, and external block types into timeline blocks (workers/staff-feed/src/routes/feed.ts:29-110).
7.2 Edge-Native Deployment
Voyage’s v2 stack maps compute to Workers, database to D1, object storage to R2, background jobs to Queues, scheduled jobs to Cron Triggers, real-time behavior to Durable Objects plus WebSockets, and secrets to Worker Secrets plus encrypted D1 columns (docs/architecture/v2-enterprise-plan.md:138-152). The platform package manifest uses pnpm and Turborepo scripts for build, test, lint, typecheck, and queue deployment (package.json:1-17), and the workspace file keeps packages and workers in the same workspace boundary (pnpm-workspace.yaml:1-5). At runtime, the edge gateway composes request-id, auth, tenant resolver, CORS, and rate-limit middleware before forwarding unmatched traffic to domain workers (workers/edge-gateway/src/index.ts:1-24), with explicit types for tenant, auth, shard, environment, service bindings, and gateway variables (workers/edge-gateway/src/types.ts:3-63). The control-plane worker resolves tenants by slug or verified domain and returns active config and ops database identifiers (workers/control-plane/src/index.ts:15-50), while the reporting-rollup worker and scoring-cron worker trigger scheduled recompute activity through the reporting surface (workers/reporting-rollup/src/index.ts:1-64; workers/scoring-cron/src/index.ts:1-50).
7.3 Per-Tenant Deployment Model
Voyage’s repo instructions explicitly define per-customer deployment as customer-specific Workers, D1 databases, and codebases composed at deploy time rather than by runtime flags (CLAUDE.md:1-22). The architecture anti-goals reinforce that boundary by rejecting generic tenant feature flags, broad behavior stores, runtime JS injection, shared-codebase feature switches, and operator-editable app builders (docs/architecture/anti-goals.md:1-31). Meridian applies that model directly by keeping bank behavior in bank code and config files instead of platform feature switches (../voyage-bank/docs/architecture/voyage-bank-design.md:3-23), using a generated vendor-snapshot model where upstream Voyage changes are intentionally imported rather than inherited at runtime (../voyage-bank/docs/architecture/voyage-bank-design.md:24-35). The factory docs repeatedly describe a file-level fork model in which new core files are copied or merged into voyage-bank rather than consumed live across repos (../voyage-bank/docs/architecture/voyage-bank-factories/f24-api-contracts.md:1-23), and the bank implementation plan explicitly excludes the shared platform control plane from the hot path by routing Meridian domains directly to Meridian workers through generated topology (../voyage-bank/docs/architecture/voyage-bank-implementation-plan.md:3-18).
7.4 Typed Shared Packages
The typed package layer is part of how Voyage keeps that per-tenant model coherent. The domain-types package defines canonical booking statuses, sources, channels, cancel/reassign reason codes, and booking actions (packages/domain-types/src/booking.ts:1-80), plus canonical service-session statuses, source types, actions, and the ServiceSession interface (packages/domain-types/src/session.ts:1-54). The API contracts package exports Zod schemas for booking actions, bulk operations, events, native, EFX, and reports (packages/api-contracts/src/index.ts:1-15). The API client builds tenant-prefixed URLs, attaches admin headers for admin paths, parses JSON envelopes, and raises typed ApiError instances (packages/api-client/src/client.ts:1-47). The schedule engine exports bitsets, buffer-time helpers, time constants, booking-status values, and availability-affecting statuses (packages/schedule-engine/src/index.ts:1-13), and its bitset implementation supports construction, bitwise operations, popcount, contiguous chunks, serialization, equality, and clone semantics (packages/schedule-engine/src/bitset.ts:1-169). The notification client adds the same kind of typed surface for service-binding and HTTP notification calls with tenant headers and internal notification operations (packages/notification-client/src/index.ts:140-260).
7.5 OIDC Auth Platform
The auth platform is similarly more complete than a thin demo login layer. The auth worker exposes OpenID discovery, JWKS, authorize, token, demo-login, native device/session refresh, userinfo, account, session verify, and logout routes (workers/auth/src/index.ts:40-54). The discovery document advertises authorization-code flow, ES256 ID token signing, public-client token auth, authorization-code and refresh grants, profile/email scopes, and S256/plain PKCE methods (workers/auth/src/routes/discovery.ts:5-30). OIDC client records are stored as D1 rows with client ID, redirect URIs, public-client flag, required roles, and default path (workers/auth/src/types.ts:44-84). Authorization validates client ID, redirect URI, response type, state, code challenge, and client role access before creating an authorization code (workers/auth/src/routes/authorize.ts:149-218), while the token flow validates client and redirect URI, consumes the code, enforces PKCE, loads user/session state, signs access and ID tokens, hashes access/refresh tokens, stores them in D1, and returns OAuth JSON (workers/auth/src/routes/token.ts:25-118). Refresh token exchange hashes the refresh token, loads and validates the session, rotates the refresh token, stores new hashes, and returns new access, ID, and refresh tokens (workers/auth/src/routes/token.ts:120-189). Underneath that, JWT signing uses ECDSA P-256 with ES256 headers and JWKS publishes the generated public key (workers/auth/src/crypto.ts:26-38), while the auth client generates state, nonce, and PKCE values, exchanges codes, verifies state, and persists sessions in browser storage (packages/auth-client/src/index.ts:105-128).
7.6 Monorepo + Turborepo + pnpm
The repo organization is straightforward but important to the overall operating model. The root package is private, pins pnpm 10.32.1, and delegates build, test, lint, and typecheck to Turbo (package.json:1-17). The workspace includes both packages/* and workers/*, keeping shared libraries and deployable Workers in one typed boundary (pnpm-workspace.yaml:1-5). The v2 architecture plan makes TypeScript 5 strict mode and Turborepo explicit non-negotiables (docs/architecture/v2-enterprise-plan.md:2511-2522).
8. Where JRNI Still Has Strengths
JRNI still has more mature shared-estate operational machinery. The Terraform analysis shows an AWS/EKS/Terraform estate with EKS clusters, ingress, IAM, state infrastructure, ECR, Aurora, KMS, Secrets Manager, and Kubernetes secrets (docs/analysis/jrni-tf.md:3-18). The deployment analysis shows a Capistrano control repo with stage files, host inventories, AWS region/VPC metadata, migrations, Slack notifications, and service restarts (docs/analysis/deploy.md:5-18). Argo CD materials add app, ESS, ops, and shared/tools deployment domains plus bootstrap charts and application definitions (docs/analysis/argocd.md:3-18), and the ESS promotion footprint includes multiple .NET deployments, services, ingresses, service accounts, RabbitMQ, and tenant-specific RDS/Redis references (docs/analysis/argocd.md:31-57).
JRNI also remains materially broader as a runtime configuration platform. Configurator spans per-client/product/environment config, CSS, translations, JavaScript, branding themes, iframe deployments, MongoDB, S3 publishing, and admin/search/auth endpoints (docs/analysis/configurator-api.md:4-24). Configurator JS fetches tenant-specific config assets, merges them with defaults, lets frontends mutate UI and config at runtime, and can swap product JS/CSS assets by reading manifests (docs/analysis/configurator-js.md:2-26; docs/analysis/configurator-js.md:41-73). The legacy Gemfile also shows accumulated operational integrations across Sidekiq, Redis, MySQL, Pusher, AWS SDKs, SendGrid, Twilio, and older SAML/OAuth-era libraries (../bookingbug/Gemfile:86-116). The honest tradeoff is that Voyage’s per-tenant model reduces runtime configuration complexity, but it also gives up JRNI’s operator-editable Configurator flexibility (docs/architecture/anti-goals.md:1-31).
9. Per-Tenant Deployment Model Justification
Voyage’s architecture goal is near-zero per-tenant infrastructure cost, ruthless per-tenant customization, full enterprise functionality, and a modern stack (docs/architecture/v2-enterprise-plan.md:5-11). The v2 plan separates a global control plane from tenant data planes with config, ops, and archive databases (docs/architecture/v2-enterprise-plan.md:38-71), and the multi-D1 design keeps config small, isolates operational growth, bounds shard size, supports independent backup/migration/deletion, and enables incremental schema rollout (docs/architecture/v2-enterprise-plan.md:73-121). The anti-goals explain why: runtime feature flags and behavior stores are treated as complexity traps that make behavior hard to test and reason about (docs/architecture/anti-goals.md:1-31).
The integration architecture gives a concrete blast-radius example. JRNI’s shared integration pipeline can let one bad integration affect many tenants, whereas Voyage’s per-tenant queue-and-worker model confines the impact to that tenant deployment (docs/architecture/per-tenant-integrations.md:7-16). Meridian demonstrates the same principle in practice by diverging into single-tenant, direct-domain, topology-generated routing rather than relying on hot-path control-plane lookups (../voyage-bank/docs/architecture/voyage-bank-design.md:24-35), and the implementation plan makes that explicit by requiring no control-plane lookup on the hot path once the tenant topology has been generated (../voyage-bank/docs/architecture/voyage-bank-implementation-plan.md:3-18).
10. Code-Level Proof
| Proof | Local claim | GitHub link | Local citation |
|---|---|---|---|
| Unified appointment session insertion | Public checkout inserts source_type = appointment into service_sessions. |
workers/booking/src/routes/checkout.ts#L400-L432 | workers/booking/src/routes/checkout.ts:400-432 |
| Booking/session state synchronization | Booking routes update or insert service_sessions when appointment state changes. |
workers/booking/src/routes/bookings.ts#L688-L830 | workers/booking/src/routes/bookings.ts:688-830 |
| Walk-in session insertion | Queue join inserts a queue entry plus a source_type = walkin service session. |
workers/queue/src/routes/entries.ts#L896-L945 | workers/queue/src/routes/entries.ts:896-945 |
| Event session upsert | Event registration sync upserts source_type = event service sessions. |
workers/events/src/session-sync.ts#L77-L193 | workers/events/src/session-sync.ts:77-193 |
| Staff action fanout | Staff check-in/start/finish actions update service_sessions and propagate to the source table. |
workers/staff-feed/src/routes/actions.ts#L41-L238 | workers/staff-feed/src/routes/actions.ts:41-238 |
| Booking command engine | Booking routes import command functions and API contract schemas rather than keeping all transition logic route-local. | workers/booking/src/routes/bookings.ts#L1-L80 | workers/booking/src/routes/bookings.ts:1-80 |
| Cancel command | Cancel command validates status, builds reversal, writes audit, and returns side effects. | packages/booking-commands/src/cancel.ts#L18-L85 | packages/booking-commands/src/cancel.ts:18-85 |
| Reverse command | Reverse command claims a reversal token, applies the snapshot, and writes a reverse audit entry. | packages/booking-commands/src/reverseAction.ts#L14-L76 | packages/booking-commands/src/reverseAction.ts:14-76 |
| Audit recording | Audit package writes structured audit rows with old/new values and optional reversal tokens. | packages/booking-audit/src/audit-log.ts#L136-L185 | packages/booking-audit/src/audit-log.ts:136-185 |
| Presence Durable Object | BookingPresenceDO manages WebSocket viewers and editing state per booking room. | workers/booking/src/durable-objects/presence.ts#L185-L256 | workers/booking/src/durable-objects/presence.ts:185-256 |
| OIDC discovery | Auth discovery advertises authorization-code flow, ES256, refresh tokens, scopes, and PKCE methods. | workers/auth/src/routes/discovery.ts#L5-L30 | workers/auth/src/routes/discovery.ts:5-30 |
| JWT signing key | Auth crypto creates ECDSA P-256 signing keys and signs ES256 JWTs. | workers/auth/src/crypto.ts#L158-L203 | workers/auth/src/crypto.ts:158-203 |
| Edge gateway middleware | Edge gateway applies request-id, auth, tenant resolver, CORS, and rate limiting before forwarding. | workers/edge-gateway/src/index.ts#L1-L24 | workers/edge-gateway/src/index.ts:1-24 |
| Reporting fanout variables | Reporting context exposes fanout helpers for ops and customer shard queries. | workers/reporting/src/types.ts#L27-L32 | workers/reporting/src/types.ts:27-32 |
| API contract bulk schemas | Bulk cancel and bulk reassign schemas enforce request shape and result envelope. | packages/api-contracts/src/booking/bulk.ts#L1-L39 | packages/api-contracts/src/booking/bulk.ts:1-39 |
| UI kit presence hook | Presence hook manages WebSocket URL, heartbeat, reconnect, viewer list, and editing messages. | packages/booking-ui-kit/src/hooks/usePresence.ts#L70-L195 | packages/booking-ui-kit/src/hooks/usePresence.ts:70-195 |
11. What Is Still In Motion
The Meridian reality audit is explicit that the system was not a pure frontend mockup, but also not an honest fully working live enterprise admin at the time of that audit (../voyage-bank/voyage-bank-reality-audit.md:8-21). It identifies hardcoded tour numbers, hardcoded data-source badges, missing gateway routes, internal errors, and source/live mismatch as integrity gaps (../voyage-bank/voyage-bank-reality-audit.md:8-21). Its pipeline table classifies D1 config data as connected, reporting facts as partially connected or inconsistent, worker SQL routes as mostly real, edge gateway routing as broken for some routes, and frontend pages as mostly API-backed in source (../voyage-bank/voyage-bank-reality-audit.md:261-295). It also says generated seed files and loader scripts existed, but that clean completion proof for a full D1 seed load was not yet present in the repo at that point (../voyage-bank/voyage-bank-reality-audit.md:297-337). The audit’s bottom line was therefore that the codebase supported a real Cloudflare/D1 demo with partial seeded data and backend wiring, not a fully truthful live real-time enterprise app (../voyage-bank/voyage-bank-reality-audit.md:348-366). The Apr 24 audit keeps the same high-level framing but materially revises the current verdict: many former route/method gaps are fixed, while queue strategy defaults, branch-overview synthetic fallbacks, a broken customer report route, and dead deep profile URLs are now the largest remaining integrity blockers (../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:50-58; ../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:241-261; ../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:340-359).
The later roadmap records clear movement after that audit: 13 admin pages rendering, cross-region ops fanout, reporting route refactors, and 131 passing tests after the UI fix factory (../voyage-bank/docs/reality-roadmap.md:82-110). Even then, the roadmap still lists WS5 reporting rollup and WS2 live traffic simulator as not started (../voyage-bank/docs/reality-roadmap.md:5-28). The ecosystem audit, one day after the roadmap’s in-body date, still characterizes the customer booking surface as functional while admin, staff, kiosk, and observer surfaces remain broken or placeholder in deployed form, and it identifies frontend-to-gateway path alignment and tenant wiring as the biggest systemic problem even though backend route families exist (../voyage-bank/docs/ecosystem-audit.md:19-64; ../voyage-bank/docs/ecosystem-audit.md:201-256).
12. Summary
Voyage is not just an architecture proposal; the platform repo already contains domain Workers, shared packages, command/audit/contract layers, OIDC auth, Durable Object presence, queue/reporting cron wrappers, and unified-session code (workers/booking/src/index.ts:1-51). Its strongest architectural difference from JRNI is the combination of per-tenant Cloudflare deployment and a unified session/occupancy spine that collapses appointment, queue, and event work into one staff-time model (docs/architecture/unified-session-architecture.md:5-12). Meridian proves that model at larger tenant scale with 380 branches, 10 regional ops shards, 64 customer shards, reporting and simulation databases, and loaded multi-million operational row counts (../voyage-bank/docs/reality-roadmap.md:49-68). The honest current-state conclusion is therefore stronger than marketing copy and weaker than a blanket production claim: parity is documented, architecture is materially implemented, Meridian demonstrates scale, and several tenant-facing surfaces still need deployment-quality hardening (../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:359-359).
13. Evidence Appendix
Key platform architecture sources:
CLAUDE.md:1-22docs/architecture/v2-enterprise-plan.md:5-11docs/architecture/v2-enterprise-plan.md:38-71docs/architecture/v2-enterprise-plan.md:73-121docs/architecture/v2-enterprise-plan.md:138-177docs/architecture/anti-goals.md:1-31docs/architecture/per-tenant-integrations.md:7-16docs/architecture/per-tenant-integrations.md:76-130docs/specs/jrni-feature-parity-matrix.md:43-58docs/specs/jrni-feature-parity-matrix.md:63-170
Key current Voyage implementation sources:
workers/booking/src/routes/checkout.ts:400-432workers/booking/src/routes/bookings.ts:688-830workers/queue/src/routes/entries.ts:896-945workers/events/src/session-sync.ts:77-193workers/staff-feed/src/routes/actions.ts:41-238workers/staff-feed/src/routes/feed.ts:29-110packages/api-contracts/src/index.ts:1-15packages/booking-audit/src/audit-log.ts:136-185packages/booking-commands/src/cancel.ts:18-85packages/booking-commands/src/reverseAction.ts:14-76workers/auth/src/index.ts:40-54workers/auth/src/routes/discovery.ts:5-30workers/auth/src/routes/token.ts:25-189workers/edge-gateway/src/index.ts:1-24
Key Meridian tenant-proof sources:
../voyage-bank/docs/architecture/voyage-bank-design.md:3-35../voyage-bank/docs/architecture/voyage-bank-design.md:124-247../voyage-bank/docs/architecture/voyage-bank-implementation-plan.md:3-18../voyage-bank/docs/architecture/voyage-bank-implementation-plan.md:158-203../voyage-bank/docs/reality-roadmap.md:3-16../voyage-bank/docs/reality-roadmap.md:49-68../voyage-bank/docs/reality-roadmap.md:82-149../voyage-bank/docs/reality-roadmap.md:241-259../voyage-bank/voyage-bank-reality-audit.md:3-10../voyage-bank/voyage-bank-reality-audit.md:261-366../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:339-361../voyage-bank/voyage-bank-reality-audit-2026-04-24.md:359-359../voyage-bank/docs/ecosystem-audit.md:5-18../voyage-bank/docs/ecosystem-audit.md:19-64../voyage-bank/docs/ecosystem-audit.md:201-256
Key JRNI comparison sources:
../bookingbug/Gemfile:86-116../bookingbug/app/models/slot.rb:44-60../bookingbug/app/models/space.rb:13-35../bookingbug/lib/services/validation/booking_conflicts.rb:81-120../bookingbug/app/workers/double_booking_scanner_job.rb:20-71../bookingbug/lib/live_status/generators/queuing/queuer.rb:22-49../bookingbug/lib/scheduler/slots.rb:41-83docs/analysis/deploy.md:5-18docs/analysis/argocd.md:3-18docs/analysis/configurator-api.md:4-24docs/analysis/configurator-js.md:2-26docs/analysis/exchange-integration-deep-dive.md:5-17docs/analysis/jrni-overlap-handling.md:53-81
14. Commit SHAs
- voyage:
14f3b190db8817399dcd30e1dc4e1ae7674bbf8a - voyage-bank:
ef4d7d4f9a66a05d68c265debc681d506a0606c6