Jarvis · 6 September 2026 · Review, no app changes

Scope correction: the upstream T3 recommendations in this report are outside the intended work scope. Use the Jarvis-only current-workspace review instead.

Reduce the work behind each interaction

The strongest next improvements are bounded reads, fewer refreshes and explicit client queue ownership. A broad rewrite would add risk before addressing these costs.

Reviewed 5fee488c7, which contains merged PRs #5 and #6. TTS model selection and the planned Pocket migration are excluded.

What the merges already improved

PR #5 removed the retired manager dialog and kept the desktop voice runtime. PR #6 introduced shared presentation fanout, shared model-choice policy, a fixed follow-up worker pool, and mobile durable-state reconciliation. Those directions are sound. The findings below concern work that remains in the merged revision.

Scope and confidence

Source review covered the linked web, desktop, mobile, Jarvis runtime, orchestration and projection paths. Two extracted production-helper checks confirmed unbounded report chaining and unstable topology snapshots. No browser, simulator, microphone, live database, dev server or repo-wide suite was used. No end-to-end latency, heap or bundle benchmark was run. Provider adapters, Git, terminal and packaging were not individually certified. The review agents could not run because of an account limit; the findings were inspected in the primary review.

This is an architecture and performance review, not a claim that the entire app is free of other defects. Source-level costs are distinguished from measured user impact.

Recommended sequence

  1. Bound server task reads and remove catalog-driven desk amplification (1–2).
  2. Use those reads for mobile recovery (4), while bounding renderer speech independently (3).
  3. Reduce disabled/idle desktop work (5–6).
  4. Then deepen shared interaction ownership (7) and make a product decision on legacy navigation (8).

Start with finding 1. Its query seam benefits task desk, presentation and follow-up callers and makes mobile recovery cheaper. Use representative long histories; an empty database hides the problem.

1StrongHigh priorityConfirmed source path

Make task reads proportional to the answer

Getting a task desk loads the node shell, then full detail for each distinct recent task. Each detail read starts eight projection queries, including all message text, plans and checkpoints. The desk is capped at 20 recent tasks, but their histories are not capped. Presentation delivery and follow-up dispatch use the same full-detail interface. A small status read gets more expensive as conversations age.

Change

Give the projection module narrow, bounded reads for task summaries, exact result/origin correlation, and queued-message existence. Batch desk metadata by thread IDs. Keep Jarvis marker interpretation in its adapter and retain the generic T3 seam. Do not copy another durable report store into the design.

Current

Desk → up to 20 full histories → extract title/objective/state

Proposed

Desk → bounded batch metadata → task cards

The deeper query module improves locality: one implementation controls data volume. Its interface gives every caller leverage without loading a conversation. Keep full detail for callers that actually display history.

Proof and acceptance

Test a desk with 20 long threads: assert bounded selected rows/text bytes and equivalent objectives, state and node identity. Measure SQL count and p50/p95 latency. Presentation tests must preserve exact turn/origin correlation. Do not replace history with an arbitrary latest-20 slice.

Source evidence at reviewed commit
2StrongHigh priorityConfirmed source path

Stop catalog refreshes from multiplying desk reads

The desktop voice runtime fetches every online node’s task desk whenever the catalog object changes. Mesh refresh publishes pending states and individual node results, plus catalog preparation updates. Overlapping refreshes use tokens to reject old writes but still perform the reads. React may batch some publications, so the exact request multiplier needs measurement. The cleanup flag discards answers; it does not cancel RPC work.

Change

Make the mesh module own per-node in-flight refresh sharing. Have the voice adapter invalidate desks for the node and facts that changed, rather than any catalog object. Use explicit focus/command/reconnect invalidation for desk freshness. Preserve incremental visibility of fast nodes.

Current

Each catalog publication → every online desk

Proposed

Relevant node change → one shared node read

This seam puts refresh scheduling in one module. Callers get leverage from shared work, and locality improves because cancellation and identity checks no longer depend on each UI effect.

Proof and acceptance

Instrument RPC counts for 1, 3 and 10 nodes, overlapping refresh requests, one slow node and node removal during a read. Assert unchanged provider/catalog metadata does not reload every desk. Preserve stale-result rejection.

Source evidence at reviewed commit
3StrongHigh priorityHelper behavior reproduced

Bound pending speech at the renderer

The server fanout has a 256-event sliding buffer, but each mounted renderer reporter appends every received event to a Promise chain. That chain has no capacity or expiry. The 512-ID deduplication set does not bound queued callbacks. A blocked speech operation can retain later reports indefinitely; a slow one can produce stale spoken updates. Browser fallback also relies solely on end/error events to settle.

Change

Replace the promise chain with a small ephemeral presentation queue module with capacity, cancellation and a declared overflow rule. Use connection generation to discard obsolete pending speech. Keep approval/input notifications visible in durable task UI. Do not add acknowledgements, replay or a delivery ledger.

Current

Live events → unlimited Promise callbacks → speech

Proposed

Live events → bounded ephemeral queue → speech adapter

The queue interface should own delivery lifetime. Web speech and desktop speech are real adapters at this seam. That gives cancellation tests locality and callers leverage without another queue hidden in React.

Proof and acceptance

An extracted production-helper check queued 1,000 callbacks behind one unresolved delivery, then observed all 1,000 execute after release. Add integration checks for capacity, a never-settling browser utterance, disconnect/reconnect, unmount and multiple nodes. No measured memory or audible-latency claim is made here.

Source evidence at reviewed commit
4StrongMedium priorityConfirmed source path

Reconcile mobile turns without downloading conversations

Mobile reconnect/foreground reconciliation requests a desk and a durable snapshot for each retained turn. The lookup transport supplies no history window. Mobile then keeps only session status and latest-turn state. Within a node these reads run together, and the loop waits for that node before moving to the next. Long histories and a slow first node increase recovery cost.

Change

Reuse a generic bounded thread-status read from the projection work in finding 1. Preserve the distinction between a confirmed missing thread and an unreachable node. Deduplicate retained thread IDs and bound concurrency across nodes so one slow route does not delay every other node.

Current

Foreground → full snapshots → discard histories

Proposed

Foreground → bounded task statuses → retire settled listeners

The snapshot/status seam should expose what reconciliation needs. A narrow interface gives web/mobile adapters leverage while keeping durable-state interpretation local to the owning module.

Proof and acceptance

Compare response bytes and recovery latency with long threads on local, LAN and relay routes. Cover 404, authentication failure, disconnection, running/settled turns and concurrent node removal. A failed request must never be treated as task completion.

Source evidence at reviewed commit
5StrongMedium priorityConfirmed source path; cost unprofiled

Give disabled desktop voice a cheap lifecycle

JarvisManagerHost mounts the lazy voice runtime for every Electron renderer. Once mounted, it refreshes the mesh, fetches desks and pushes recognition context without checking voice enabled/readiness state. Lazy loading only splits the file here; the desktop mounts it immediately. This is catalog/IPC work, not evidence that the microphone or model runs while disabled.

Change

Keep a small desktop lifecycle adapter subscribed to typed voice availability/enabled state and menu actions. Mount or activate the heavier interaction module when needed. Preserve first-capture transcripts during activation. Gate recognition-context updates on capability and semantic vocabulary changes.

Current

Electron mount → voice runtime → catalog + desk work

Proposed

Desktop lifecycle adapter → activate interaction work when needed

The lifecycle seam gives the interaction module a simpler interface. Locality improves because disabled/active transitions are owned once instead of being implicit in individual effects.

Proof and acceptance

Count RPCs, context IPC and React commits while voice is disabled, enabled, unavailable and toggled during capture. Verify Full and Controller first-press behavior and that Headless never acquires speech authority. Real device acceptance is still required for microphone/hotkey behavior.

Source evidence at reviewed commit
6StrongMedium priorityHelper behavior reproduced

Replace topology polling with one stable snapshot

useDesktopLocalBootstraps installs a two-second timer per mounted consumer. Its reader invokes synchronous Electron IPC, filters into a new array and sets React state even when topology is identical. The hook is used by the open command palette and legacy sidebar. On web the missing-bridge path also returns a new empty array. This is a small but recurring avoidable cost.

Change

Let desktop publish topology changes to one renderer store with stable snapshot identity. Reuse the existing environment registry where it already has the needed topology. Until a change event exists, use a single shared poll with equality checks and no timer on unsupported clients.

Current

Each consumer → timer → synchronous IPC → fresh array

Proposed

Topology event → stable shared snapshot → affected consumers

The topology module should give consumers a read/subscribe interface. Desktop IPC is its adapter. The deletion test favors removing per-consumer timers: complexity disappears into one owner instead of moving to each caller.

Proof and acceptance

The extracted reader returned deeply equal but referentially distinct snapshots and made two bridge reads for two unchanged reads. Verify one shared observation, stable identity and preservation of the last successful snapshot on failure. Measure renderer stalls before claiming frame-time savings.

Source evidence at reviewed commit
7Worth exploringMedium priorityArchitectural judgment

Move shared interaction transitions out of React

The desktop voice runtime and mobile provider still own overlapping workflow decisions: clarification, model answers, request identity, target selection, submission and retained turn state. Mobile mirrors active turns in a ref Map and React array; desktop keeps clarification and capture snapshots in refs. PR #6 already shared model-choice policy, which should remain. The remaining friction is lifecycle ownership across callers, not merely file length.

Change

Deepen a Jarvis interaction module in the existing client-runtime package. Keep pure typed transitions and request effects together behind a small interface. Leave audio capture, navigation and foreground notifications as platform adapters. Share only behavior both clients actually need; do not build a generic workflow framework or move dispatch authority to the client.

Current

Desktop React workflow + mobile React workflow

Proposed

Interaction module → desktop adapter / mobile adapter

Two platform adapters justify this seam. Locality means cancellation, clarification and request identity are fixed and tested once. Leverage comes from a common transition contract, not from extracting more one-line helpers.

Proof and acceptance

First write a transition matrix for existing behavior: ambiguity, rejection, cancellation, retry, node removal, reconnect, model answer and route changes during capture. Exercise both adapters through the same interface. Keep origin identity and execution-node ownership explicit.

Source evidence at reviewed commit
8Worth exploringMedium priorityProduct decision; bundle impact unmeasured

Decide whether the legacy sidebar still earns its cost

The app retains 3,960 lines in the current sidebar and 3,715 in the legacy sidebar, and AppSidebarLayout statically imports both. Behavior also branches outside them: the new-thread shortcut takes a different path depending on the legacy preference. This creates a second navigation behavior to maintain. Line counts are scope evidence, not a defect or a bundle-size measurement.

Change

Inventory legacy-only behavior and migrate the few capabilities worth keeping. Then retire the legacy preference and implementation. If both experiences remain intentional, lazy-load the inactive one and centralize shared task/navigation actions behind an existing seam.

Current

Two sidebars + external preference branches

Proposed

One sidebar with retained capabilities

This is a deletion opportunity with clear locality benefits: one navigation module and fewer branches. Avoid a new abstraction that simply wraps both implementations and preserves all the complexity.

Proof and acceptance

Compare keyboard navigation, project ordering, remote nodes, drafts, task creation, drag/drop and user preferences before deletion. Measure production chunks and initial parse cost separately. This is lower priority than database and reconnect work.

Source evidence at reviewed commit

Keep these constraints

Keep deterministic grounding and server authorization, qualified task/node references, durable task results with ephemeral speech, the ordinary provider adapters, and the Full/Controller/Headless capability split. Do not delete ownership or cancellation controls merely to shorten the implementation. Keep shipped migration IDs intact.

Validation before calling improvements done

Use focused backend tests and client lifecycle tests, then targeted package lint/typecheck. Measure query counts, selected text bytes, RPC counts, queue depth, reconnect latency and disabled-client activity on representative data. Ask for device/browser acceptance separately when implementation reaches that stage. No numerical speedup is promised by this review.