Architecture review · 5 September 2026 · Jarvis-owned behavior

Jarvis needs fewer owners
for each operation.

13 findings. The worst failures sit where one module starts an operation and another decides its target, completion or recovery. Most improvements belong inside Jarvis. Two concrete integration changes would reduce recurring T3 rebase work.

Reviewed commit 2687854aa. Read-only source audit across server, core, client runtime, web, mobile, desktop voice and packaging. No app files changed, no browsers or live processes launched, and no live database touched. One minimal in-memory SQLite reproduction; no full test suite or runtime benchmark. Race findings below are distinguished from observed source behavior. This is an architectural review, not a claim that every defect in the app has been found.

The findings

  1. Reports can steal command focus
  2. Replies are not bound to the exact clarification
  3. The follow-up queue lacks one execution owner
  4. Work starts before result ownership is recorded
  5. Speech waits for unrelated node catalogs
  6. Client workflow is duplicated, and clarification edits English
  7. Voice admission disagrees with voice interruption
  8. Each listener rebuilds every relevant task presentation
  9. Mobile cannot retire a turn when its live ending was missed
  10. A generic T3 extension exists for one Jarvis startup caller
  11. Voice packaging policy is spread through the shared builder
  12. A successful publication cannot resume verification
  13. Development and release use different Pipecat source
StrongCommand authority

1. Reports can steal command focus

A presentation is persisted as attention, navigates the workspace, and takes precedence over the selected task, project and route. A completion from task A can become the ambient target for a follow-up intended for B. That incidental attention also survives a renderer reload.

Current ownershipReport → persisted attention → navigation → next command target
Proposed ownershipReport → display; explicit focus → command target

Better design

Give deliberate task focus and origin-bound follow-up state sole ownership of command context. Reports may offer an explicit focus action. Remove the localStorage attention-to-authority path.

Proof the replacement should provide

Exercise the client controller: focus B, deliver a completion from A, then submit an ambient continuation. B remains authoritative. Also cover reload and an explicit project mention.

Source-traced behavior. Explicit voice project mentions can override attention; the finding concerns ambient context. This conflicts with the documented rule that visible attention is not authority.

StrongCommand authority

2. Replies are not bound to the exact clarification

The controller chooses a candidate from a previously read pending frame, then consumes the current frame using only the session ID. It checks the frame kind, not its identity. A replaced task question can supply a new original command while the old candidate supplies its target.

Current ownershipRead A → resolve A → consume current B → combine A target + B command
Proposed ownershipAnswer + expected frame → one atomic task-desk transition

Better design

Give each pending interaction a stable identity and let one task-desk module atomically validate the answer, resolve its candidate, consume that exact frame and transition focus. Reject stale answers without consuming the replacement.

Proof the replacement should provide

Pause between selection and consumption, replace the pending question, and resume. The old answer must fail without changing the new question. Cover task, project, expiry and cancellation.

Source-traced interleaving, not a reproduced concurrent run. Existing transactional consumption correctly prevents two replies from consuming the same unchanged frame.

StrongQueued work

3. The follow-up queue lacks one execution owner

The controller reads readiness and enqueues; a separate dispatcher wakes only on later readiness events. A ready event before insertion can strand accepted work. Cancellation can also land after the dispatcher status check but before turn start. A shared sequential worker spends up to two 30-second retry delays on one task, delaying other task queues. Startup recovery uses an invalid SQLite aggregate query and suppresses its failure.

Current ownershipSnapshot check → SQL insert; separate event wakeup; separate stop
Proposed ownershipPer-thread queue coordinator → ordinary T3 dispatch

Better design

Use one Jarvis queue coordinator for enqueue, wakeup, stop and ready-state reconciliation. Serialize dispatch decisions per thread with bounded concurrency across threads. Define cancellation relative to the actual accepted dispatch. Repair recovery and keep deterministic T3 command IDs.

Proof the replacement should provide

Run coordinator startup against SQLite; inject ready-before-insert and cancel-before-dispatch interleavings; hold one failing thread while another progresses. Current dispatcher tests manually reconcile and do not cover startup.

The exact query shape was reproduced in an in-memory SQLite database: misuse of aggregate: MIN(). Scheduling and cancellation risks are source-traced. More status checks alone would only move the race.

StrongTask launch

4. Work starts before result ownership is recorded

New-task dispatch starts the turn before appending its Jarvis origin marker. A fast terminal event can reach the live projector before the task is recognized as managed. Later metadata and task-desk focus writes still gate returning started, so an accepted task can be reported as a failed dispatch.

Current ownershipStart turn → append origin → focus desk → report started
Proposed ownershipRecord origin → start turn → return accepted task; maintain focus

Better design

Persist the required origin before launching. Treat accepted turn dispatch as the execution outcome. Report later focus maintenance failures separately while preserving the accepted task reference. Apply the same ordering to new tasks, reviews and reroutes.

Proof the replacement should provide

Deliver a terminal event immediately after turn acceptance and inject failures in later desk maintenance. Preserve both origin-directed output and the accepted-task response.

Ordering and failure propagation are verified in source; fast-result loss is a race risk. Continuations already record origin before turn dispatch, so this requires consistent Jarvis composition, not a new T3 execution path.

StrongClient latency

5. Speech waits for unrelated node catalogs

The mesh publishes a catalog only after every node read finishes. Desktop then waits for every task desk, and a fresh voice submission refreshes the whole mesh again. A slow connected node can delay a request with an already-qualified target on a healthy node.

Current ownershipAll node configs + vocabularies → all desks → refresh all again → submit
Proposed ownershipNode-level catalog updates → validate required target → submit

Better design

Keep one observable catalog with independent node updates and explicit freshness. Validate the selected execution node for an explicit target. Require broader freshness only for cross-node name resolution; do not guess when unavailable catalogs could contain an ambiguous match.

Proof the replacement should provide

Hold an unrelated node response indefinitely and submit to a known healthy task reference. Then test unqualified duplicate names: unresolved freshness must cause clarification or a clear availability result.

Structural latency finding, not a timing benchmark. Reads already have bounded concurrency and per-node error handling; preserve those protections.

StrongClient ownership

6. Client workflow is duplicated, and clarification edits English

Web creates a new mesh module for each command while mobile keeps one. Each UI owns catalogs, selection and submission state. Web repairs provider/model/effort clarification by regex-editing the original utterance and parsing it again; mobile displays needs-input and drops the active turn. Mobile also owns a separate question classifier and conversation route.

Current ownershipWeb workflow + regex repair | mobile workflow + separate routing
Proposed ownershipShared Jarvis interaction module → web/mobile adapters

Better design

Deepen the Jarvis client runtime into the shared owner of catalog state, grounding, submission identity and pending answers. Carry typed pending intent and stable choices rather than mutable English. Web and mobile should render that state and supply platform capture/playback adapters.

Proof the replacement should provide

Run the same interaction cases through both adapters: provider/model clarification, multiword choices, conversation without a project, cancellation and node disconnection. Tests should cross the shared interface.

This is a policy consolidation, not a request to share React UI. Durable server control-target clarification already exists and is the better direction to extend.

StrongDesktop voice

7. Voice admission disagrees with voice interruption

The main-process voice module marks local speech busy through playback and rejects capture while busy. The hotkey calls that capture method directly. The worker explicitly implements capture as an interruption of speech, but the outer check prevents it from reaching that transition.

Current ownershipHotkey → main busy rejection ✕ worker speech interruption
Proposed ownershipHotkey → one admission/preemption owner → capture worker

Better design

Make one typed voice operation owner decide admission, preemption and completion. Beginning capture should perform the intended preemption transition there. Keep process isolation, resource guards and protocol validation inside the worker and Python host.

Proof the replacement should provide

At the public voice interface, start a long report then request capture. Verify the chosen interruption policy and exactly one terminal result. Cover release during startup and cancellation; real-device hotkey acceptance remains separate.

Source-traced contradiction, not a microphone test. Remote compute busy rejection and one-model-at-a-time memory limits are legitimate constraints.

StrongServer fan-out

8. Each listener rebuilds every relevant task presentation

Every presentation subscriber reads full thread history and project data, builds a presentation, and only then checks its origin. More devices multiply identical reads, including work for unrelated tasks. Mobile retained subscriptions make this cost worse.

Current ownershipEvent → N listeners × full history → N builds → origin filter
Proposed ownershipEvent → one Jarvis projection → origin-filtered listeners

Better design

Compose one ephemeral presentation stream per node and project each source event once, then route by origin. Where history remains costly, use a narrow public T3 read for the relevant output and correlation. Keep delivery live-only with no second durable report ledger.

Proof the replacement should provide

Count projection reads for one event with one and several subscribers. Reads should stay constant as subscribers grow. Verify origin isolation, late subscribe and reconnect without speech replay.

Source-proven repeated work, no measured throughput or memory claim. This preserves the documented ephemeral-presentation decision.

StrongReconnect lifecycle

9. Mobile cannot retire a turn when its live ending was missed

Mobile keeps one presentation listener per active turn. Successful starts remain until a completed/failed live event arrives. Reconnect and foreground refresh catalogs and task desks, but do not reconcile active turns against durable task state. Completion while disconnected can retain the turn and listener until the provider unmounts.

Current ownershipStart → live listener → missed terminal event → retained listener
Proposed ownershipReconnect → inspect durable task → retire finished listener

Better design

Reconcile retained task references against ordinary durable state on reconnect and foreground. Retire completed, removed or abandoned interactions without speaking old results. Bound local subscription ownership and clean it up when a node is removed.

Proof the replacement should provide

Start, disconnect, complete remotely, reconnect. Assert that the listener is retired and no old completion is spoken. Include node removal and several such cycles.

Source-traced lifecycle gap. Presentation history is already bounded to eight entries; the active-turn collection is the separate concern.

StrongUpstream rebase cost

10. A generic T3 extension exists for one Jarvis startup caller

OrchestrationReactor now depends on an extension whose only real implementation is Jarvis Expo push. A no-op adapter and generic reactor test wiring support it. This avoids naming Jarvis inside the reactor while still making upstream orchestration own a product startup hook.

Current ownershipT3 reactor → generic extension → Jarvis Expo push
Proposed ownershipServer composition → T3 reactor + Jarvis push

Better design

Start the Jarvis push module at server composition alongside the existing Jarvis follow-up dispatcher. Delete the extension contract, no-op implementation and generic reactor dependency. A real future generic requirement can justify a public seam then.

Proof the replacement should provide

Test push startup and shutdown through product composition and keep upstream reactor tests independent. This improves locality without changing notification behavior.

Directly conflicts with the documented preference for explicit Jarvis composition over a generic callback framework for one caller. The push sender abstraction itself has a useful test adapter and need not be removed.

StrongUpstream rebase cost

11. Voice packaging policy is spread through the shared builder

The shared desktop builder contains individual voice model filenames, native dependency resolution, Windows payload validation and Pipecat/worker staging. Jarvis voice changes and upstream desktop packaging changes therefore touch the same large implementation.

Current ownershipShared desktop builder: platform build + voice models + native rules + staging
Proposed ownershipShared builder → explicit Jarvis packaging module

Better design

Move Jarvis payload staging and validation into a product-owned packaging module. Leave a small explicit composition call in the shared builder. Keep actual platform and artifact-integrity checks; do not introduce a general plugin framework.

Proof the replacement should provide

Run existing focused packaging tests against the new interface and compare payload contents for applicable targets and presets. The deletion test should remove product knowledge from the shared builder.

Maintenance finding. Shallow build and branding hooks are allowed and worth keeping. No estimate of future merge-conflict frequency is claimed.

StrongRelease process

12. A successful publication cannot resume verification

The release transaction publishes, then makes fallible network reads to verify the release and latest pointer. If one read fails after publication, the run fails. Retrying refuses every already-published release, including the exact release it just created.

Current ownershipPublish succeeds → verification fails → retry rejects published state
Proposed ownershipPublish once → retry verifies immutable published result

Better design

Separate draft construction from published-release verification. On retry, inspect a published release without mutating it; matching source identity and assets can resume verification and return success. Reject mismatches explicitly.

Proof the replacement should provide

Use the transport adapter to fail the first read after publication, then rerun. Verify one publication, no asset mutation and eventual verified success. Keep mismatch rejection.

Source-traced failure path, no release was created. Existing draft recovery, identity and artifact checks protect correctness and should remain.

Worth exploringDependency maintenance

13. Development and release use different Pipecat source

The frozen build copies Pipecat and rewrites exact Python source fragments to defer imports. Development uses the ordinary installed dependency. Updates require maintaining textual patches, and local development does not exercise the same import behavior as the released runtime.

Current ownershipStock dependency in dev | source-rewritten dependency in release
Proposed ownershipSame reviewed dependency in dev and release → package smoke

Better design

Prefer upstream lazy imports or one explicit pinned patched dependency used in both development and packaging. Keep frozen-runtime smoke verification and the offline package-size constraints that motivated the patch.

Proof the replacement should provide

Exercise the same startup and speech paths against the common dependency, then inspect the frozen artifact. Verify imports and offline startup, not just matching source fragments.

The overlay is pinned, fails closed and avoids mutating installed packages. This is a maintenance opportunity, not evidence of a broken shipped runtime.

Start with command authority and the queue.

First separate report attention from deliberate focus and bind replies to exact pending interactions. Those are task-selection correctness issues. Then give queued work one owner and fix launch ordering so accepted work cannot disappear behind metadata failures.

Next, deepen the client runtime and unify voice admission. That gives later UI work leverage through a smaller shared interface. Remove global catalog barriers and duplicate presentation reads as separate performance changes. Reconcile mobile subscriptions through durable task state.

For rebases, remove the one-caller reactor extension and extract Jarvis packaging policy. These are small, specific ways to reduce upstream conflict surface. Do not start by moving every T3 file or designing a plugin framework.

Keep these decisions

Keep the pure deterministic Jarvis core, qualified node/task references, ordinary provider adapters, explicit Full/Controller/Headless permissions, one desktop identity, isolated native voice processes and ephemeral origin-directed presentations. Platform shortcut adapters and one-model-at-a-time memory policy solve real constraints.

Make tests follow ownership

Use negative behavioral tests at the owning interface for each failure, then the smallest broader set covering other entry points and reverse states. Source-string tests can guard forbidden imports, but they cannot prove wakeup ordering, focus authority or real playback interruption. Avoid adding more scattered guards around the current split ownership.