{
  "version": 1,
  "id": "Jarvis-circe-app-audit-20260913",
  "repo": "/home/abstergo/Documents/Jarvis",
  "repoName": "Jarvis",
  "scope": "circe-app-audit-20260913",
  "title": "Circe app review: dead code and integration gaps",
  "sinceCommit": "69e66477f005b99a1415bc4df0e8e5a5f957b944",
  "lastReviewCommit": "69e66477f005b99a1415bc4df0e8e5a5f957b944",
  "reviewRound": 1,
  "loc": {
    "added": 0,
    "removed": 0
  },
  "updatedAt": "2026-09-13T15:00:14.570496+00:00",
  "htmlPath": "/home/abstergo/Documents/reviews/Jarvis-circe-app-audit-20260913-abbyview.html",
  "jsonPath": "/home/abstergo/Documents/reviews/Jarvis-circe-app-audit-20260913-abbyview.json",
  "liveUrl": "https://circe-app-audit.plan.abstergo.fyi/",
  "confidence": {
    "score": 0,
    "max": 5,
    "recommendation": "Changes required",
    "rationale": "5 major findings, 5 minor findings, and 1 integration note. Score is the review rubric\u2019s readiness total, not confidence in every claim."
  },
  "actionableOpen": 10,
  "verdict": "CHANGES_REQUESTED",
  "baseline": {
    "frozenAt": "2026-09-13T15:00:14.570496+00:00",
    "title": "Circe app review",
    "lede": "10 actionable findings; four new regression tests fail while 384 existing focused tests pass.",
    "walkthrough": {
      "prose": "The scan separated unreachable behavior from live but inconsistent integrations. Five high-priority findings concern upgrades, node/project/account identity, and speech cancellation. Four have deterministic failing regressions; the account issue is supported by a complete source trace. Cleanup follows those repairs.",
      "bullets": [
        "Preserve old installs with a new forward migration before further branding cleanup.",
        "Unify semantic-host, Conversations-project, and provider-account selection around existing typed identities.",
        "Make live speech own cancellation and current context, then restore the report mute control.",
        "Remove confirmed fx, Android-recognizer and clarification leftovers only after preserving their still-used pieces."
      ]
    },
    "filesChanged": [
      {
        "path": "apps/mobile/modules/circe-local-asr/expo-module.config.json",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/mobile/src/features/circe/CirceMobileProvider.tsx",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/mobile/src/features/voice-input/useVoiceInputController.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/mobile/src/native/circeLocalAsr.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/mobile/src/native/voiceTranscription.android.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/circe/Layers/CirceCodexSupervisor.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/circe/Layers/CirceController.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/circe/Layers/CirceFastSupervisor.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/circe/Layers/CirceGrokSupervisor.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/circe/Layers/CirceOpencodeSupervisor.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/circe/Layers/CirceProjectLexicon.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/circe/Layers/CirceWsRpc.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/circe/Services/CirceFastSupervisor.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/circe/Services/CirceLiveVoice.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/circe/controllerHelpers.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/circe/conversationsProject.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/persistence/Migrations.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/persistence/Migrations/041_CirceTaskDesks.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/server/src/serverRuntimeStartup.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/web/src/circePreferences.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/web/src/components/circe/CirceControlCenter.tsx",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/web/src/components/circe/CirceLiveVoice.bridge.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/web/src/components/circe/CirceLiveVoice.logic.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/web/src/components/circe/CirceLiveVoiceRuntime.tsx",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/web/src/components/circe/CirceManager.logic.test.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/web/src/components/circe/CirceManager.logic.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/web/src/components/circe/CirceVoiceReporter.logic.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/web/src/components/circe/CirceVoiceReporter.tsx",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "apps/web/src/components/circe/CirceVoiceRuntime.tsx",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "docs/user/circe.md",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "infra/relay/.env.example",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "infra/relay/src/http/Api.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "infra/relay/src/voice/LiveVoiceSessions.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "packages/circe-client-runtime/src/circe/mesh.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "packages/contracts/src/providerInstance.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "scripts/build-desktop-artifact.ts",
        "delta": "reviewed reference; unchanged"
      },
      {
        "path": "scripts/circe-fx-packaging.ts",
        "delta": "reviewed reference; unchanged"
      }
    ],
    "flowDiagram": null,
    "sinceCommit": "69e66477f005b99a1415bc4df0e8e5a5f957b944",
    "loc": {
      "added": 0,
      "removed": 0
    }
  },
  "roundDeltas": [],
  "findings": [
    {
      "id": "f1",
      "status": "open",
      "fixedAt": null,
      "reviewRound": 1,
      "title": "Add a forward migration for installed Jarvis databases",
      "file": "apps/server/src/persistence/Migrations.ts",
      "lines": "137-154",
      "severity": "major",
      "pBadge": "P1",
      "type": "logic",
      "category": "data-integrity",
      "section": "actionable",
      "whyItMatters": "The in-memory upgrade regression recreates the old table names at the existing ledger position, preserves an alias, then invokes the current migrator. Reading that alias fails with \u201cno such table: circe_project_aliases\u201d. Fresh database tests pass because they execute the rewritten migrations. This affects all presets that reuse an existing state directory; it does not delete the old data, but makes it inaccessible to the new services.",
      "bodyParagraphs": [
        "The rebrand changes historical migration SQL to create circe_* tables but keeps the already-shipped IDs. runMigrations runs only IDs above the database ledger. An installation already through migration 65 therefore runs no rename and retains jarvis_project_aliases, jarvis_task_desks, jarvis_follow_up_queue, and jarvis_push_registrations while the current services query circe_*.",
        "The in-memory upgrade regression recreates the old table names at the existing ledger position, preserves an alias, then invokes the current migrator. Reading that alias fails with \u201cno such table: circe_project_aliases\u201d. Fresh database tests pass because they execute the rewritten migrations. This affects all presets that reuse an existing state directory; it does not delete the old data, but makes it inaccessible to the new services."
      ],
      "suggestedDiff": null,
      "fixInstruction": "Preserve shipped migration history and add a new migration after 65 that handles old and fresh schemas transactionally. Audit persisted Circe payload names as part of the same upgrade, and preserve aliases, task desks, queue identities, and push registrations.",
      "evidence": "Reproduced: circeUpgrade.audit.test.ts fails against an in-memory database. No live data was read or written.",
      "related": [
        "apps/server/src/persistence/Migrations/041_CirceTaskDesks.ts",
        "apps/server/src/circe/Layers/CirceProjectLexicon.ts"
      ],
      "prompt": "Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate.\n\nIn `apps/server/src/persistence/Migrations.ts` around lines 137-154, The rebrand changes historical migration SQL to create circe_* tables but keeps the already-shipped IDs. runMigrations runs only IDs above the database ledger. An installation already through migration 65 therefore runs no rename and retains jarvis_project_aliases, jarvis_task_desks, jarvis_follow_up_queue, and jarvis_push_registrations while the current services query circe_*. Preserve shipped migration history and add a new migration after 65 that handles old and fresh schemas transactionally. Audit persisted Circe payload names as part of the same upgrade, and preserve aliases, task desks, queue identities, and push registrations. Reproduced: circeUpgrade.audit.test.ts fails against an in-memory database. No live data was read or written. Keep the repair in Circe ownership. Check each affected client and preset, preserve node-qualified identities, and use focused tests. Do not modify inherited T3 implementations, delete shipped migration IDs, or operate on the live database. Update this review sidecar only after validating the fix.",
      "followUp": false
    },
    {
      "id": "f2",
      "status": "open",
      "fixedAt": null,
      "reviewRound": 1,
      "title": "Select a node that can actually run semantic interpretation",
      "file": "packages/circe-client-runtime/src/circe/mesh.ts",
      "lines": "356-366",
      "severity": "major",
      "pBadge": "P1",
      "type": "logic",
      "category": "correctness",
      "section": "actionable",
      "whyItMatters": "With an online Controller followed by an online Headless execution node, the regression selects the Controller. Web commands that need semantic interpretation and mobile submissions can fail even while a usable execution node is connected. Registry ordering should not decide whether the main product preset can direct work.",
      "bodyParagraphs": [
        "selectCirceSemanticNode filters only reachability, then chooses the ambient node or first online entry. It ignores the advertised execution capability. The Circe RPC handler explicitly rejects circe.interpret on Controller nodes.",
        "With an online Controller followed by an online Headless execution node, the regression selects the Controller. Web commands that need semantic interpretation and mobile submissions can fail even while a usable execution node is connected. Registry ordering should not decide whether the main product preset can direct work."
      ],
      "suggestedDiff": null,
      "fixInstruction": "Choose semantic nodes using the advertised capabilities and catalog readiness before applying the existing ambient preference. Keep execution targets separate from the interpretation host, and return a typed unavailable result when no eligible host exists.",
      "evidence": "Reproduced: expected node-laptop (Headless), received node-desktop (Controller). Server rejection is at CirceWsRpc.ts lines 345-350.",
      "related": [
        "apps/server/src/circe/Layers/CirceWsRpc.ts",
        "apps/web/src/components/circe/CirceVoiceRuntime.tsx",
        "apps/mobile/src/features/circe/CirceMobileProvider.tsx"
      ],
      "prompt": "Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate.\n\nIn `packages/circe-client-runtime/src/circe/mesh.ts` around lines 356-366, selectCirceSemanticNode filters only reachability, then chooses the ambient node or first online entry. It ignores the advertised execution capability. The Circe RPC handler explicitly rejects circe.interpret on Controller nodes. Choose semantic nodes using the advertised capabilities and catalog readiness before applying the existing ambient preference. Keep execution targets separate from the interpretation host, and return a typed unavailable result when no eligible host exists. Reproduced: expected node-laptop (Headless), received node-desktop (Controller). Server rejection is at CirceWsRpc.ts lines 345-350. Keep the repair in Circe ownership. Check each affected client and preset, preserve node-qualified identities, and use focused tests. Do not modify inherited T3 implementations, delete shipped migration IDs, or operate on the live database. Update this review sidecar only after validating the fix.",
      "followUp": false
    },
    {
      "id": "f3",
      "status": "open",
      "fixedAt": null,
      "reviewRound": 1,
      "title": "Resolve the Conversations project by workspace identity",
      "file": "apps/server/src/circe/Layers/CirceController.ts",
      "lines": "1257-1264",
      "severity": "major",
      "pBadge": "P1",
      "type": "logic",
      "category": "correctness",
      "section": "actionable",
      "whyItMatters": "The regression puts a user-owned Conversations project before the managed Conversations project. The controller starts the provider thread in decoy-conversations instead of managed-conversations. This puts general-question history and tool execution in the wrong workspace. The existing resolveCirceConversationsProjectId helper already resolves by root, but has no production caller.",
      "bodyParagraphs": [
        "General-question execution searches the shell catalog for the first project titled Conversations. Startup creates the managed project by its canonical workspace root, so title matching is a different identity rule. A user project may have the same title, and the managed project may be renamed.",
        "The regression puts a user-owned Conversations project before the managed Conversations project. The controller starts the provider thread in decoy-conversations instead of managed-conversations. This puts general-question history and tool execution in the wrong workspace. The existing resolveCirceConversationsProjectId helper already resolves by root, but has no production caller."
      ],
      "suggestedDiff": null,
      "fixInstruction": "Use the managed workspace identity from conversationsProject.ts when selecting the destination. Fail or recover explicitly when that project is unavailable; do not retain an ambient coding project silently. Cover duplicate titles and renamed managed projects.",
      "evidence": "Reproduced through CirceController.execute: status started, projectId decoy-conversations instead of managed-conversations.",
      "related": [
        "apps/server/src/circe/conversationsProject.ts",
        "apps/server/src/serverRuntimeStartup.ts"
      ],
      "prompt": "Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate.\n\nIn `apps/server/src/circe/Layers/CirceController.ts` around lines 1257-1264, General-question execution searches the shell catalog for the first project titled Conversations. Startup creates the managed project by its canonical workspace root, so title matching is a different identity rule. A user project may have the same title, and the managed project may be renamed. Use the managed workspace identity from conversationsProject.ts when selecting the destination. Fail or recover explicitly when that project is unavailable; do not retain an ambient coding project silently. Cover duplicate titles and renamed managed projects. Reproduced through CirceController.execute: status started, projectId decoy-conversations instead of managed-conversations. Keep the repair in Circe ownership. Check each affected client and preset, preserve node-qualified identities, and use focused tests. Do not modify inherited T3 implementations, delete shipped migration IDs, or operate on the live database. Update this review sidecar only after validating the fix.",
      "followUp": false
    },
    {
      "id": "f4",
      "status": "open",
      "fixedAt": null,
      "reviewRound": 1,
      "title": "Preserve provider-instance credentials in direct supervision",
      "file": "apps/server/src/circe/Layers/CirceController.ts",
      "lines": "482-496",
      "severity": "major",
      "pBadge": "P1",
      "type": "logic",
      "category": "security",
      "section": "actionable",
      "whyItMatters": "For a work Codex instance backed by a custom home and a personal login in ~/.codex, Circe can send the work utterance and catalog context to the personal account. OpenCode and Grok use the same family-level approach. Checking that the driver is Codex does not prove that it is the selected Codex account. Existing tests check family separation, not separation between two instances of the same driver.",
      "bodyParagraphs": [
        "The direct supervisor is selected by driver family after resolving a provider instance, but receives only the prompt, and optionally the model. Its credential loader independently reads process-wide overrides or default home-directory credentials. The chosen instance ID and its configuration never reach that loader.",
        "For a work Codex instance backed by a custom home and a personal login in ~/.codex, Circe can send the work utterance and catalog context to the personal account. OpenCode and Grok use the same family-level approach. Checking that the driver is Codex does not prove that it is the selected Codex account. Existing tests check family separation, not separation between two instances of the same driver."
      ],
      "suggestedDiff": null,
      "fixInstruction": "Use the ordinary selected-instance adapter for supervision unless a public contract can establish that the direct path belongs to that exact instance. Do not infer credentials from the family or add instance-specific branches inside T3 providers. Add same-driver, different-account regression coverage.",
      "evidence": "Source trace: Codex auth candidates at CirceCodexSupervisor.ts lines 93-106; OpenCode uses ~/.local/share/opencode/auth.json. No real credential files or external provider APIs were accessed.",
      "related": [
        "apps/server/src/circe/Layers/CirceCodexSupervisor.ts",
        "apps/server/src/circe/Layers/CirceOpencodeSupervisor.ts",
        "apps/server/src/circe/Layers/CirceGrokSupervisor.ts",
        "packages/contracts/src/providerInstance.ts"
      ],
      "prompt": "Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate.\n\nIn `apps/server/src/circe/Layers/CirceController.ts` around lines 482-496, The direct supervisor is selected by driver family after resolving a provider instance, but receives only the prompt, and optionally the model. Its credential loader independently reads process-wide overrides or default home-directory credentials. The chosen instance ID and its configuration never reach that loader. Use the ordinary selected-instance adapter for supervision unless a public contract can establish that the direct path belongs to that exact instance. Do not infer credentials from the family or add instance-specific branches inside T3 providers. Add same-driver, different-account regression coverage. Source trace: Codex auth candidates at CirceCodexSupervisor.ts lines 93-106; OpenCode uses ~/.local/share/opencode/auth.json. No real credential files or external provider APIs were accessed. Keep the repair in Circe ownership. Check each affected client and preset, preserve node-qualified identities, and use focused tests. Do not modify inherited T3 implementations, delete shipped migration IDs, or operate on the live database. Update this review sidecar only after validating the fix.",
      "followUp": false
    },
    {
      "id": "f5",
      "status": "open",
      "fixedAt": null,
      "reviewRound": 1,
      "title": "Keep report identity through live speech cancellation",
      "file": "apps/web/src/components/circe/CirceVoiceReporter.tsx",
      "lines": "36-46",
      "severity": "major",
      "pBadge": "P1",
      "type": "logic",
      "category": "correctness",
      "section": "actionable",
      "whyItMatters": "The composed regression uses the actual speakPresentation function and live controller, enqueues a report, clears the report queue as disconnect does, then delivers session.started. The supposedly cleared report is still sent as session.commentary.append. The same loss of identity prevents terminal events from retracting obsolete approval prompts. Successful queuing also cannot establish that audio played.",
      "bodyParagraphs": [
        "The live branch converts a presentation to plain text, calls speak, and immediately reports played. The playback queue loses ownership even when the live session is still connecting. Its cancel callback only cancels browser speech; it cannot remove the live controller\u2019s pending append.",
        "The composed regression uses the actual speakPresentation function and live controller, enqueues a report, clears the report queue as disconnect does, then delivers session.started. The supposedly cleared report is still sent as session.commentary.append. The same loss of identity prevents terminal events from retracting obsolete approval prompts. Successful queuing also cannot establish that audio played."
      ],
      "suggestedDiff": null,
      "fixInstruction": "Carry presentation and turn identity into the one live speech owner and give it bounded cancellation and honest local outcome semantics. Route disconnect, new-input interruption, terminal supersession, and teardown through that owner. Keep this ephemeral; do not add a durable delivery ledger.",
      "evidence": "Reproduced: \u201cObsolete report from disconnected node\u201d is sent after queue.clear(). Regression patch includes the real reporter-to-controller composition.",
      "related": [
        "apps/web/src/components/circe/CirceLiveVoice.logic.ts",
        "apps/web/src/components/circe/CirceLiveVoice.bridge.ts",
        "apps/web/src/components/circe/CirceVoiceRuntime.tsx"
      ],
      "prompt": "Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate.\n\nIn `apps/web/src/components/circe/CirceVoiceReporter.tsx` around lines 36-46, The live branch converts a presentation to plain text, calls speak, and immediately reports played. The playback queue loses ownership even when the live session is still connecting. Its cancel callback only cancels browser speech; it cannot remove the live controller\u2019s pending append. Carry presentation and turn identity into the one live speech owner and give it bounded cancellation and honest local outcome semantics. Route disconnect, new-input interruption, terminal supersession, and teardown through that owner. Keep this ephemeral; do not add a durable delivery ledger. Reproduced: \u201cObsolete report from disconnected node\u201d is sent after queue.clear(). Regression patch includes the real reporter-to-controller composition. Keep the repair in Circe ownership. Check each affected client and preset, preserve node-qualified identities, and use focused tests. Do not modify inherited T3 implementations, delete shipped migration IDs, or operate on the live database. Update this review sidecar only after validating the fix.",
      "followUp": false
    },
    {
      "id": "f6",
      "status": "open",
      "fixedAt": null,
      "reviewRound": 1,
      "title": "Refresh app facts used by an ongoing live conversation",
      "file": "apps/web/src/components/circe/CirceLiveVoice.logic.ts",
      "lines": "910-916",
      "severity": "minor",
      "pBadge": "P2",
      "type": "logic",
      "category": "correctness",
      "section": "actionable",
      "whyItMatters": "The server instructions tell the model to answer status and current-model questions directly from that reference data. If the user switches tasks, a task starts on another device, or the default changes during the session, those answers can describe the startup snapshot for up to the ten-minute session limit. A completion report does not refresh the whole catalog.",
      "bodyParagraphs": [
        "The context callback is evaluated once during session startup. It includes current project, current task, running-task count, provider names, and default model. No subsequent catalog, target, or settings update sends replacement context to the live model.",
        "The server instructions tell the model to answer status and current-model questions directly from that reference data. If the user switches tasks, a task starts on another device, or the default changes during the session, those answers can describe the startup snapshot for up to the ten-minute session limit. A completion report does not refresh the whole catalog."
      ],
      "suggestedDiff": null,
      "fixInstruction": "Either supply bounded event-driven context updates through the live owner or delegate questions requiring current state to the existing deterministic backend. Do not restart the voice session on every UI update or treat startup data as current.",
      "evidence": "Verified call sites: options.context is read only at startup; CirceLiveVoiceRuntime.tsx supplies its snapshot; CirceLiveVoice.ts directs overview answers to reference data.",
      "related": [
        "apps/web/src/components/circe/CirceLiveVoiceRuntime.tsx",
        "apps/server/src/circe/Services/CirceLiveVoice.ts"
      ],
      "prompt": "Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate.\n\nIn `apps/web/src/components/circe/CirceLiveVoice.logic.ts` around lines 910-916, The context callback is evaluated once during session startup. It includes current project, current task, running-task count, provider names, and default model. No subsequent catalog, target, or settings update sends replacement context to the live model. Either supply bounded event-driven context updates through the live owner or delegate questions requiring current state to the existing deterministic backend. Do not restart the voice session on every UI update or treat startup data as current. Verified call sites: options.context is read only at startup; CirceLiveVoiceRuntime.tsx supplies its snapshot; CirceLiveVoice.ts directs overview answers to reference data. Keep the repair in Circe ownership. Check each affected client and preset, preserve node-qualified identities, and use focused tests. Do not modify inherited T3 implementations, delete shipped migration IDs, or operate on the live database. Update this review sidecar only after validating the fix.",
      "followUp": false
    },
    {
      "id": "f7",
      "status": "open",
      "fixedAt": null,
      "reviewRound": 1,
      "title": "Restore a reachable control for muting voice reports",
      "file": "apps/web/src/circePreferences.ts",
      "lines": "4-10",
      "severity": "minor",
      "pBadge": "P2",
      "type": "logic",
      "category": "correctness",
      "section": "actionable",
      "whyItMatters": "After the user stops a conversation, a later completion can start a new announcement session whenever an API key is configured. Without a key, reports can still use browser speech. There is no supported UI path to remain silent while leaving voice configured. Old false preferences can also remain stuck without a way to re-enable them.",
      "bodyParagraphs": [
        "Voice reports default to enabled, but setCirceVoiceReportsEnabled has no production caller. The current control-center button toggles a live session only. It does not change the report preference.",
        "After the user stops a conversation, a later completion can start a new announcement session whenever an API key is configured. Without a key, reports can still use browser speech. There is no supported UI path to remain silent while leaving voice configured. Old false preferences can also remain stuck without a way to re-enable them."
      ],
      "suggestedDiff": null,
      "fixInstruction": "Expose the persisted report preference in the Circe settings or control center and wire it to the reporter lifetime. Keep session start/stop semantics clear, and make both mute and unmute reachable. Reconcile the browser fallback with the documented live-only voice policy.",
      "evidence": "Knip and a full web-source caller search find only the setter definition. Report auto-activation is in CirceVoiceReporter.tsx lines 44-46.",
      "related": [
        "apps/web/src/components/circe/CirceControlCenter.tsx",
        "apps/web/src/components/circe/CirceVoiceReporter.tsx",
        "docs/user/circe.md"
      ],
      "prompt": "Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate.\n\nIn `apps/web/src/circePreferences.ts` around lines 4-10, Voice reports default to enabled, but setCirceVoiceReportsEnabled has no production caller. The current control-center button toggles a live session only. It does not change the report preference. Expose the persisted report preference in the Circe settings or control center and wire it to the reporter lifetime. Keep session start/stop semantics clear, and make both mute and unmute reachable. Reconcile the browser fallback with the documented live-only voice policy. Knip and a full web-source caller search find only the setter definition. Report auto-activation is in CirceVoiceReporter.tsx lines 44-46. Keep the repair in Circe ownership. Check each affected client and preset, preserve node-qualified identities, and use focused tests. Do not modify inherited T3 implementations, delete shipped migration IDs, or operate on the live database. Update this review sidecar only after validating the fix.",
      "followUp": false
    },
    {
      "id": "f8",
      "status": "open",
      "fixedAt": null,
      "reviewRound": 1,
      "title": "Stop shipping the disconnected fx supervisor",
      "file": "scripts/build-desktop-artifact.ts",
      "lines": "4092-4101",
      "severity": "minor",
      "pBadge": "P2",
      "type": "style",
      "category": "maintainability",
      "section": "additional",
      "whyItMatters": "The obsolete dependency therefore increases release contents and can fail a release build through Effect.orDie even though the app never invokes it. The old plan.fx field, fallback comments, and fx-specific parser code remain as well. The shared CIRCE_FAST_SUPERVISOR_SYSTEM_PROMPT is still imported by the direct supervisors and must survive removal of the obsolete service.",
      "bodyParagraphs": [
        "CirceFastSupervisorLive is absent from the production server composition and has no runtime consumer. Production Knip identifies the whole layer as unused. Desktop artifact construction still downloads, verifies, extracts, and bundles the pinned fx executable on supported targets.",
        "The obsolete dependency therefore increases release contents and can fail a release build through Effect.orDie even though the app never invokes it. The old plan.fx field, fallback comments, and fx-specific parser code remain as well. The shared CIRCE_FAST_SUPERVISOR_SYSTEM_PROMPT is still imported by the direct supervisors and must survive removal of the obsolete service."
      ],
      "suggestedDiff": null,
      "fixInstruction": "Remove the disconnected layer, fx-specific service code, plan field, binary staging, and resource packaging in one focused cleanup. Move or rename the still-used prompt without changing its consumers. Keep credential compatibility reads separate from binary execution.",
      "evidence": "Verified by production Knip plus production composition and packaging searches. Existing fx packaging tests pass, but test a product path with no runtime consumer.",
      "related": [
        "apps/server/src/circe/Layers/CirceFastSupervisor.ts",
        "apps/server/src/circe/Services/CirceFastSupervisor.ts",
        "apps/server/src/circe/controllerHelpers.ts",
        "scripts/circe-fx-packaging.ts"
      ],
      "prompt": "Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate.\n\nIn `scripts/build-desktop-artifact.ts` around lines 4092-4101, CirceFastSupervisorLive is absent from the production server composition and has no runtime consumer. Production Knip identifies the whole layer as unused. Desktop artifact construction still downloads, verifies, extracts, and bundles the pinned fx executable on supported targets. Remove the disconnected layer, fx-specific service code, plan field, binary staging, and resource packaging in one focused cleanup. Move or rename the still-used prompt without changing its consumers. Keep credential compatibility reads separate from binary execution. Verified by production Knip plus production composition and packaging searches. Existing fx packaging tests pass, but test a product path with no runtime consumer. Keep the repair in Circe ownership. Check each affected client and preset, preserve node-qualified identities, and use focused tests. Do not modify inherited T3 implementations, delete shipped migration IDs, or operate on the live database. Update this review sidecar only after validating the fix.",
      "followUp": false
    },
    {
      "id": "f9",
      "status": "open",
      "fixedAt": null,
      "reviewRound": 1,
      "title": "Remove or connect the orphaned Android local recognizer",
      "file": "apps/mobile/src/native/voiceTranscription.android.ts",
      "lines": "55-67",
      "severity": "minor",
      "pBadge": "P2",
      "type": "style",
      "category": "maintainability",
      "section": "additional",
      "whyItMatters": "Normal Android availability therefore stays false while the unused native recognizer, Expo module, support checks, and tests remain maintained. Metro platform variants explain why some static tools miss the gap. This is a disconnected feature subtree, not proof that all mobile voice code is dead; iOS file transcription is still used.",
      "bodyParagraphs": [
        "The Android CirceLocalAsr module and getLocalLiveVoiceRecognizer implementation remain in the app, but the live recognizer has no non-test caller. The only voice-input UI consumes getLocalVoiceTranscriber, which returns null on Android.",
        "Normal Android availability therefore stays false while the unused native recognizer, Expo module, support checks, and tests remain maintained. Metro platform variants explain why some static tools miss the gap. This is a disconnected feature subtree, not proof that all mobile voice code is dead; iOS file transcription is still used."
      ],
      "suggestedDiff": null,
      "fixInstruction": "Decide the intended Android product capability and either connect the existing on-device recognizer through Circe-owned mobile composition or remove its native module and unused wrapper. Preserve the working iOS dictation path and avoid inventing file-URI sentinels.",
      "evidence": "Caller trace: useVoiceInputController.ts lines 16, 107 and 209 use only getLocalVoiceTranscriber. All getLocalLiveVoiceRecognizer consumers are tests.",
      "related": [
        "apps/mobile/src/features/voice-input/useVoiceInputController.ts",
        "apps/mobile/modules/circe-local-asr/expo-module.config.json",
        "apps/mobile/src/native/circeLocalAsr.ts"
      ],
      "prompt": "Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate.\n\nIn `apps/mobile/src/native/voiceTranscription.android.ts` around lines 55-67, The Android CirceLocalAsr module and getLocalLiveVoiceRecognizer implementation remain in the app, but the live recognizer has no non-test caller. The only voice-input UI consumes getLocalVoiceTranscriber, which returns null on Android. Decide the intended Android product capability and either connect the existing on-device recognizer through Circe-owned mobile composition or remove its native module and unused wrapper. Preserve the working iOS dictation path and avoid inventing file-URI sentinels. Caller trace: useVoiceInputController.ts lines 16, 107 and 209 use only getLocalVoiceTranscriber. All getLocalLiveVoiceRecognizer consumers are tests. Keep the repair in Circe ownership. Check each affected client and preset, preserve node-qualified identities, and use focused tests. Do not modify inherited T3 implementations, delete shipped migration IDs, or operate on the live database. Update this review sidecar only after validating the fix.",
      "followUp": false
    },
    {
      "id": "f10",
      "status": "open",
      "fixedAt": null,
      "reviewRound": 1,
      "title": "Delete superseded clarification and presentation helpers",
      "file": "apps/web/src/components/circe/CirceManager.logic.ts",
      "lines": "474-481",
      "severity": "minor",
      "pBadge": "P2",
      "type": "style",
      "category": "maintainability",
      "section": "additional",
      "whyItMatters": "These tests exercise superseded behavior while the real runtime now uses typed model choices, exact frame IDs, and shared command context. Keeping both approaches makes it easier to reintroduce unguarded string rewriting during the next change. Unused exports that are internally used or are test seams are a different category and should remain.",
      "bodyParagraphs": [
        "appendCirceChoice and applyCirceClarificationChoice implement the older string-rewriting approach to clarification. They are referenced only by each other and tests. circeManagerCatalogIsReady and presentationStatus likewise have no production consumers.",
        "These tests exercise superseded behavior while the real runtime now uses typed model choices, exact frame IDs, and shared command context. Keeping both approaches makes it easier to reintroduce unguarded string rewriting during the next change. Unused exports that are internally used or are test seams are a different category and should remain."
      ],
      "suggestedDiff": null,
      "fixInstruction": "Remove the proven orphan helpers and their obsolete tests, preserving current typed clarification logic and meaningful caller-level tests. Limit deletion to confirmed unused behavior rather than the entire Knip export list.",
      "evidence": "Verified normal and production Knip output, then full web-source caller searches excluding tests.",
      "related": [
        "apps/web/src/components/circe/CirceVoiceReporter.logic.ts",
        "apps/web/src/components/circe/CirceManager.logic.test.ts"
      ],
      "prompt": "Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate.\n\nIn `apps/web/src/components/circe/CirceManager.logic.ts` around lines 474-481, appendCirceChoice and applyCirceClarificationChoice implement the older string-rewriting approach to clarification. They are referenced only by each other and tests. circeManagerCatalogIsReady and presentationStatus likewise have no production consumers. Remove the proven orphan helpers and their obsolete tests, preserving current typed clarification logic and meaningful caller-level tests. Limit deletion to confirmed unused behavior rather than the entire Knip export list. Verified normal and production Knip output, then full web-source caller searches excluding tests. Keep the repair in Circe ownership. Check each affected client and preset, preserve node-qualified identities, and use focused tests. Do not modify inherited T3 implementations, delete shipped migration IDs, or operate on the live database. Update this review sidecar only after validating the fix.",
      "followUp": false
    },
    {
      "id": "f11",
      "status": "open",
      "fixedAt": null,
      "reviewRound": 1,
      "title": "Relay-funded voice exists without a node client",
      "file": "infra/relay/.env.example",
      "lines": "34-38",
      "severity": "info",
      "pBadge": "",
      "type": "info",
      "category": "maintainability",
      "section": "additional",
      "whyItMatters": "This is an integration gap to resolve before advertising the deployment-key option as available to app users. The relay endpoint itself is reachable, so it is not safe to label that service dead or delete its schema. It may be intentionally staged work; the current node does not select it.",
      "bodyParagraphs": [
        "The relay exposes authenticated create/release live-voice APIs, usage limits, persistence, and an optional deployment API key. The source-built node\u2019s CirceLiveVoice service always uses its own stored key and calls OpenAI directly. No server or client-runtime production call site invokes the relay create/release endpoints.",
        "This is an integration gap to resolve before advertising the deployment-key option as available to app users. The relay endpoint itself is reachable, so it is not safe to label that service dead or delete its schema. It may be intentionally staged work; the current node does not select it."
      ],
      "suggestedDiff": null,
      "fixInstruction": "Confirm whether relay-funded voice is intended to ship. If so, connect it through Circe-owned session composition with explicit authority and lifecycle handling; otherwise document the endpoint as staged and remove misleading setup claims.",
      "evidence": "Repository-wide createLiveVoiceSession/releaseLiveVoiceSession searches find relay handlers and contracts, but no shipped node caller. No relay was deployed or contacted.",
      "related": [
        "infra/relay/src/voice/LiveVoiceSessions.ts",
        "infra/relay/src/http/Api.ts",
        "apps/server/src/circe/Services/CirceLiveVoice.ts"
      ],
      "prompt": "Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate.\n\nIn `infra/relay/.env.example` around lines 34-38, The relay exposes authenticated create/release live-voice APIs, usage limits, persistence, and an optional deployment API key. The source-built node\u2019s CirceLiveVoice service always uses its own stored key and calls OpenAI directly. No server or client-runtime production call site invokes the relay create/release endpoints. Confirm whether relay-funded voice is intended to ship. If so, connect it through Circe-owned session composition with explicit authority and lifecycle handling; otherwise document the endpoint as staged and remove misleading setup claims. Repository-wide createLiveVoiceSession/releaseLiveVoiceSession searches find relay handlers and contracts, but no shipped node caller. No relay was deployed or contacted. Keep the repair in Circe ownership. Check each affected client and preset, preserve node-qualified identities, and use focused tests. Do not modify inherited T3 implementations, delete shipped migration IDs, or operate on the live database. Update this review sidecar only after validating the fix.",
      "followUp": true
    }
  ],
  "coverage": [
    [
      "Core and shared runtime",
      "Command grounding, clarification, task identity, node choice, catalog readiness, provider selection, reverse transitions. Focused core and runtime tests plus both package typechecks."
    ],
    [
      "Server composition",
      "Preset enforcement, controller interpretation and execution, Conversations identity, follow-up ownership, presentation fanout, push and voice contracts, desktop-use boundary, and migration upgrade behavior."
    ],
    [
      "Web and desktop",
      "Voice owner, report lifecycle, interruption and reconnect, session context, preferences, lazy composition, tray/global-shortcut dynamic imports, and release resources."
    ],
    [
      "Mobile",
      "Circe node selection, retained focus and reconnect helpers, native ASR consumers, platform variants, and Expo module reachability. No simulator or device verification."
    ],
    [
      "Packaging and relay",
      "fx staging and native dependencies, Headless packaging, shipped SQL migrations, relay voice handlers and node integration, deployment configuration. No full release build or remote deployment of Circe."
    ]
  ],
  "limitations": "Snapshot audit of Circe ownership at origin/main 69e66477f0. Inherited T3 provider, session, Git, terminal, persistence-engine and coding-UI internals were treated as dependencies. This is not a line-by-line certification of every source file. No app browser, simulator, microphone, live install, or real provider was used. No Circe source fixes, PR, or push were made.",
  "validation": {
    "existingTests": 384,
    "existingFiles": 19,
    "newFailingRegressions": 4,
    "typechecks": [
      "@circe/core",
      "@circe/client-runtime"
    ],
    "regressionPatch": "/home/abstergo/Documents/reviews/Jarvis-circe-app-audit-regressions.patch",
    "logs": [
      "/home/abstergo/Documents/reviews/Jarvis-circe-app-audit-regressions.log",
      "/home/abstergo/Documents/reviews/Jarvis-circe-app-audit-speech-regression.log",
      "/home/abstergo/Documents/reviews/Jarvis-circe-app-audit-identity-regression.log",
      "/home/abstergo/Documents/reviews/Jarvis-circe-app-audit-focused-tests.log",
      "/home/abstergo/Documents/reviews/Jarvis-circe-app-audit-typecheck.log"
    ]
  },
  "history": [
    {
      "round": 1,
      "at": "2026-09-13T15:00:14.570496+00:00",
      "confidence": 0,
      "actionableOpen": 10,
      "note": "Initial current-snapshot review; no fixes applied."
    }
  ],
  "syncMeta": {
    "instructions": "Preserve baseline and finding bodies. Fix passes update statuses and validation only. This review covers the current Circe snapshot, not merely the rebrand diff."
  }
}
