chore(locket): catch feat/locket-pro up to dev (merge-conflict resolution)#559
Conversation
…o LM Studio T049) Ground truth (ollama-raw-curl-proof part10 + user correction): Ollama's native /api/chat streams reasoning in message.thinking, and handleOllamaChatLine routes it to onReasoning UNGATED — so it renders (device: reasoning=211, user saw it). The opposite of the OpenAI-compat path (T049) which gates reasoning_content on thinkingEnabled and drops it. Replays the captured Ollama NDJSON at the XHR boundary (endpoint :11434 → the native path). Falsified: stop routing message.thinking → onReasoning turns it RED; revert → green. Proves the LM Studio drop (T049) is provider-path-specific, not 'remote thinking is just off'. remoteHarness: chunk the replayed body per-line so it drives both OpenAI SSE (data:…\n\n) and Ollama NDJSON (…\n). T049 still red after the change (verified). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> EOF
…oning-dropped is the real bug) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nking block mid-stream (DEV-B14)
B14 (device: reasoning dumped into the answer bubble until </think>, then reclassified) is FIXED: the render
parses <think> live during streaming. Proven by pausing the stream DEEP inside the still-open <think> and
asserting the reasoning is in thinking-block-content while the answer hasn't leaked it. Falsified against the
REAL lever — breaking parseThinkTags' <think> detection (messageContent.ts:258) makes the reasoning leak into
the answer → red; revert → green. (My first attempt broke the REASONING_DELIMITERS array, which parseThinkTags
does NOT use — a wrong-lever miss I caught before committing.)
Harness: llama fake gains char-by-char streaming + scriptCompletion({pauseAfter}) / releaseStream() to hold a
stream mid-token and assert the in-flight render — reusable for any streaming-render test. engine:'llama' to
match the device (gguf).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…d, DEV-B14 fixed, proven) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…engine seam, proven non-vacuous Audit confirmed FALSE GREEN: the old assertion rode litert.calls.generateRaw, which the main litert turn never uses, so it stayed green even when the '[User wanted an image…]' marker was forced onto every send — it guarded nothing. Rewritten to assert on the boundary that CARRIES the model prompt (litert sendMessage text), where the marker would actually land if useChatGenerationActions.ts:448 fired. Non-vacuous checks added (send reached the engine; exact prompt reached the model unmodified). Falsified: forcing shouldRouteToImageGenerationFn=true makes line 448 fire → the marker appears in sendMessage → red; revert → green. (Old test: green even when the marker was forced — proof it was theater.) Harness: litert fake now records sendMessage args (calls.sendMessage) — the seam that carries the turn text. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ot engine booleans Audit confirmed FALSE GREEN: the old test asserted liteRTService/llmService.isModelLoaded(), which the REDUNDANT unloadAllTextEngines() satisfies even with the residency swap deleted — so the accounting could go stale/co-resident and it stayed green. Now asserts modelResidencyManager.getResidents() (the accounting the residency manager owns): keeps BOTH states as a proven transition — after litert loads the one text resident is 'lrt'; after llama loads it is 'llm' (litert evicted, never co-resident/stale). Falsified: no-op'ing modelResidencyManager.register makes the accounting empty/stale → red; revert → green (the old test stayed green through the same break — proof it was theater). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…hisper (DEV-B1 generalised) Generalises T023 (whisper-specific) to the real invariant: with text+image+whisper+tts+embedding resident, the REAL activeModelService.ejectAll leaves whisper+tts+embedding resident (getResidents after = [embedding, tts, whisper]) — the memory budget stays inflated after the user ejected everything. Root: index.ts:436-437 ejectAll → unloadAllModels + count=(textUnloaded+imageUnloaded); sidecars are never unloaded. Precondition asserts all 5 types resident first (non-vacuous). The fix must free every resident type. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eral, RED) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…oves the black box) + proven test
The Currently Loaded section showed only the model name + DISK size — residency was a black box. Now it also
shows the RAM the model consumes (hardwareService.formatModelRam → ~X GB RAM), with testIDs
currently-loaded-model{,-name,-ram}. Rendered test validates it on the ACTUAL ModelSelectorModal UI, wired
with the real loaded path (llmService.getLoadedModelPath) reached via the real Home-picker load. Proven:
dropping formatModelRam from the meta turns it red; revert → green.
Part of finishing the residency cluster: residency is now VISIBLE in the UI (what's loaded + how much RAM),
not only in the getResidents() black box.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…parity with text)
Same black-box removal for the image model: Currently Loaded now shows disk size + estimated RAM
(hardwareService.estimateImageModelRam → formatBytes) with testIDs currently-loaded-image-model{,-name,-ram},
mirroring the proven TextTab change. So the model selector shows how much memory EACH loaded model (text +
image) consumes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rModal test mock The Currently-Loaded RAM feature calls hardwareService.formatModelRam / estimateImageModelRam; the existing rntl mock lacked them (undefined → meta render crashed → 11 failures). Blast-radius miss on my part — added the two mocked methods so the modal renders. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n X + Join Slack) above About Compelling, on-brand (no exclamations/em-dashes): leads with the value — new features land there first, subscribers get promo discounts, feedback shapes the roadmap; Slack = issues fixed fast, debug together, early access. Emerald-accented section so it stands out above About. Opens x.com/alichherawalla and the Slack invite via Linking. testIDs follow-on-x / join-slack. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e About screen Same two community links as the settings card, on the About screen (community section under Open Source), same on-brand outcome-first copy. testIDs about-follow-on-x / about-join-slack. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…line strings) Single source of truth in sharePrompt.ts (next to GITHUB_URL); Settings + About import them instead of duplicating the raw URLs. Also fixes an AnimatedListItem index collision in About. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tion) + evictByKey [TDD] TDD: the failing integration test (modelSelectorEjectResident) was written first, then the feature built to green. The model selector now shows an 'In Memory' section listing EVERY resident (text/image/whisper/tts/ embedding) with its RAM and an individual Eject — so a user can free any single model (incl. the whisper sidecar ejectAll leaks), leaving the others resident; ejected models lazy-reload on next use (ensureResident). Adds modelResidencyManager.evictByKey(key): the missing capability — release() only forgot the accounting, never called the model's unload(). evictByKey runs under the FIFO lock, calls the real unload, removes it. Logs throughout ([MODEL-SM] user eject requested/done, [ModelResidency] evicted ... by user request). Proven: RED without the feature; falsified — no-op'ing evictByKey turns it red (eject can't free whisper); revert → green. No regression (ModelSelectorModal rntl 46 pass). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…on next send (proven transition) Real path: load text (Home picker) → user ejects (evictByKey) → not resident → send → answer renders AND text is resident again. The false→true residency transition across the send is the reload proof (the answer alone could come from the engine fake; the re-registration is what proves ensureResident reloaded it). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ejectAll unloaded only text+image and counted (textUnloaded?1:0)+(imageUnloaded?1:0); whisper/tts/embedding sidecars registered with modelResidencyManager leaked and kept charging the memory budget after the user ejected everything. Loop the remaining residents through modelResidencyManager.evictByKey after unloadAllModels so their real unload runs (they lazy-reload on next use). Flips two device-grounded reds green, both via real interactions over device-boundary fakes and asserted on the UI residents surface: - ejectAllLeavesWhisper (T023): real download-gesture arrival -> whisper resident -> ejectAll -> probe-residents no longer contains whisper. - ejectAllUnloadsEveryType (T023b): image+whisper co-resident via real load paths -> ejectAll -> getResidents() empty. Rebuilt from register() shortcuts to real interactions. Both falsified: removing the eviction loop leaves the sidecar resident -> red. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pro submodule 521ba200 -> 042b4bc2 (removes the Pro Tools settings section registration; Settings no longer surfaces it per product decision). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
whisperStore.downloadModel auto-loaded the STT model resident the instant the download
finished (await get().loadModel()), leaving a phantom ~1.5GB whisper in RAM the user
never transcribed with. On device makeRoomFor then counted that squatter against a
heavier text load (fits=true into 1.6GB real free RAM) -> thrash/OOM ('this gemma4 e2b
is struggling... some coresident bullshit in the ram').
A download only puts the file on disk. Loading is owned by the transcribe path: whisper
lazy-loads on demand via startRecording -> ensureWhisperForTranscription, and is warmed
fits-gated at launch by modelPreloader.preloadStt -- exactly the deferred-loading model
every other model follows, and what useWhisperTranscription's own NOTE already documents.
Flips T022 (whisperResidentOnDownload.rendered.redflow) green via the real download
gesture + UI residents probe. Falsify: restore the auto-load -> whisper resident -> red.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…oval Nets the new-feature UI work added this session that previously had no behavioral test: - Settings 'Stay in the loop' card: renders Follow-on-X + Join-Slack; each tap hands the OS the exact shared URL constant (Linking.openURL(FOLLOW_X_URL/SLACK_INVITE_URL)). - Settings: Pro Tools row no longer renders (absence guard, mirrors the Voice/TTS removal guard). - New AboutScreen.test.tsx: the same Follow-on-X + Join-Slack links render and open the right URLs. Asserts against the imported shared constants (test-side DRY), not re-hardcoded strings. All falsified: wrong URL -> the openURL assert fails; re-added Pro Tools -> the absence guard fails; revert -> green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gging infra Characterization test (green): on a <=6GB device, sending a text turn frees the idle whisper sidecar (reclaimSttForGeneration) while the text model stays and the reply renders. Real Home->Chat, whisper co-resident via the real download+select, RAM dropped to 6GB, real send. Falsified: keep the device roomy (>6GB) -> whisper stays -> red. Getting there surfaced real infra + coupling issues, fixed here: - logger.ts: when DEBUG_LOGS is set under jest, mirror every logger.* to process.stderr (jest mocks console.*). Now 'DEBUG_LOGS=1 npx jest <file>' surfaces ALL source [X-SM] traces with no per-test patching. Opt-in, quiet by default. - ChatInput: [COMPOSER-SM] handleSend trace (matches the [GEN-SM]/[MODEL-SM] convention; on-device visible via the debug log sink). - chatHarness.send(): drive the composer's real onChangeText handler + invoke the send button's resolved onPress. Once a whisper/STT model is present, RTL fireEvent.changeText/press stop reaching the composer (the subtree reshapes with voice availability) -- a ChatInput coupling smell (text-send readiness should not depend on voice; feeds the send-piping consolidation). Also dismiss the onboarding spotlight in setup so its AttachStep never intercepts gestures. Blast radius: 13 tests across 6 chat suites still green. - DEVICE_TEST_FINDINGS: B1 fix traceability. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…y UI, not getResidents() The reclaim result now rides on the real user-facing indicator: open the ModelSelectorModal and assert the whisper row is gone from the 'In Memory' section while the text row remains — the feature built to remove the residency black box. Replaces the service-level expect(getResidents()). Falsified: roomy device -> whisper stays listed -> red. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ict/budget × modalities × text/voice) Area 3 was text/image + eject-centric. Adds the missing cells, all validated through the model selector In Memory UI (the black-box-removing feature), not getResidents(): - T111 ✅ sttReclaimedOnSend — text turn reclaims idle whisper on a tight device - T112 ✅ modelSelectorEjectResident — per-model eject from In Memory - T113 ✅ modelSelectorShowsLoadedRam — loaded model + RAM shown - T114 ✅ lazyReloadAfterEject — ejected model reloads on next send - T115 ❌ voice-note send reclaims STT after transcription (voice twin of T111) - T116 ❌ allowed co-residence: text + whisper on a roomy device - T117 ❌ auto-eviction on OS memory-warning (UI-flow version) - T118 ❌ embedding sidecar lazy-loads + co-resides on first RAG query - T119 ❌ whisper blocked->free->retry when a heavy owns RAM - T120 ❌ TTS co-residence during a voice turn Counts: 110->120 cases, 52->56 automated, 33->39 to-write. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Roomy device: a heavy text model and the whisper STT sidecar co-reside — the single-model rule evicts heavies for each other (T026) but not a reclaimable sidecar. Validated through the model selector In Memory section: text-only -> load whisper -> BOTH resident-item-text and resident-item-whisper listed. Falsified: skip the whisper load -> whisper not listed -> red. Also scaffolds docs/RESIDENCY_TEST_MISMATCHES.md for logging any device-evidence mismatch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
modelResidencyManager registers a real AppState('memoryWarning') listener that reclaims idle
sidecars (whisper/tts/embedding) on OS pressure while keeping the active heavy. The RN jest mock
swallows the listener, so nativeBoundary now installs a capturing AppState + exposes
emitMemoryWarning() (OS event -> the app's real listener -> real handleMemoryWarning). Validated
through the model selector In Memory section: fire the warning -> whisper row gone, text row stays.
Falsified: no warning -> whisper stays -> red. Blast radius: 9 chat/memory tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…emory UI) Voice twin of T111: enter voice mode (real gesture), record a voice note + release to send (real transcribeFile -> onTranscript -> send). On a <=6GB device the send goes through the same handleSendFn -> reclaimSttForGeneration path, freeing idle whisper for the LLM turn (confirmed via [ModelResidency] reclaiming idle STT trace). Reply is an AUDIO bubble (spoken via TTS), so asserted via audio-bubble-<id> + the reply content, then the reclaim via the model selector In Memory section (whisper gone, text stays). Falsified: roomy device -> whisper stays -> red. Notes for the harness quirks hit: setupWhisperModel must precede render (voice tests' order); a ModelSelectorModal cannot be rendered mid-voice-turn (disrupts the record state) so the In Memory check runs AFTER voiceSend. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, needs download-only helper
…t no-op) Device 2026-07-15: an app-kill mid-download (7 concurrent multi-GB models, then 'network connection lost') left failed text-download cards whose STORE entry had lost its downloadId, while the native row kept it. retry() bailed at `if (!entry?.downloadId) return` before reaching the iOS re-issue path, so downloadModelBackground never fired — every retry tap was a silent no-op (the log shows ~40 retry dispatches, 0 re-downloads). The downloadId is only needed on Android, which resumes the WorkManager job in place. iOS re-issues from scratch (restartIosTextDownload rebuilds from the entry's modelId/fileName/metadata) and never uses downloadId. Move the downloadId guard into the Android branch; iOS retry now only requires the entry to exist. Device-verified working. Regression: 169 download/models tests pass, tsc clean.
When the 3-download concurrency cap is full, downloadModelBackground blocks until a slot frees, so a retried item sat showing 'failed' with no signal it was actually queued (the '4th item won't retry' report — it was queued, not dead). Mark the entry 'pending' the instant retry runs, mirroring Android. Device-verified.
The icon badge counted only isActiveStatus (downloading + queued) and dropped failed, so a failed/retriable download was invisible — no signal to open the manager. Add a shared isFailedStatus; the badge now counts active + failed and the Download Manager header surfaces a matching 'N failed' count so the two agree (T001 updated: badge == downloading + queued + failed).
…de hang) Device 2026-07-15 (P0): in VOICE MODE, tapping + to attach a photo hung the app completely — could not even navigate back. The picker works fine in chat mode, so the differentiator is voice mode's active iOS AVAudioSession (playback, for TTS): presenting the native camera/photo picker on top of it wedges the main thread on the audio-route handoff. Add audioSessionManager.deactivate() (setAudioSessionActivity(false)) and call it before launchImageLibrary/launchCamera. TTS/recording re-assert the session on their next call (ensurePlayback re-activates every call), so no explicit restore. iOS-only + Android-safe: audioSessionManager is entirely Platform.OS==='ios' (no-op on Android), so the Android picker path is byte-for-byte unchanged. tsc clean; 108 audio-session/chat-input tests pass. Needs on-device verification (a native main-thread hang can't be reproduced in jest).
… wedge) Device 2026-07-15 (P1): tapping Save in the fullscreen image viewer left the chat input unresponsive (nav still worked) — ONLY on Save. The viewer is a <Modal> and the 'Image Saved' confirmation is an AppSheet (also a modal); iOS cannot present a second modal on top of the open viewer, so the pair wedges the UI. Close the viewer first, then save + show the alert on the next tick after it fades out. Android has no nested-modal conflict, so this is harmless there. tsc clean; 13 tests pass.
Debug config now uses ai.offgridmobile.dev so a dev build installs alongside the App Store / TestFlight build (Release stays ai.offgridmobile, so releases and their provisioning are untouched). Update the CLAUDE.md log-pull to target the .dev container and to read the device UDID from `devicectl list devices` instead of a stale hardcoded one — the mismatch is what broke the earlier log pulls.
…ed id The script hardcoded BUNDLE_ID=ai.offgridmobile for the launch, so after Debug got the .dev suffix it installed ai.offgridmobile.dev but launched the old ai.offgridmobile — the non-.dev app kept coming up. Read CFBundleIdentifier from the built .app's Info.plist so launch always matches install (verified: resolves to ai.offgridmobile.dev).
Core half of the voice-mode picker-hang fix: pass handleAttachPress to the audioMode slot as onAttachPress (adds a [COMPOSER-SM] log), and bump the pro submodule to fix/voice-attach-reroute (3599b5b1) which uses it for the + button. iOS → native ActionSheetIOS; Android → the same attachPicker popover as before (unchanged).
…ed feedback Guards the two device fixes: (1) retry re-issues even when the store entry lost its downloadId (app-kill) — verified RED with the old `!entry?.downloadId` guard; and (2) a retried item leaves the failed state (queued feedback). Mounts the real DownloadManagerScreen over the download+fs fake, taps the failed-retry-button.
…er-dismiss delay - pickFromLibrary/pickFromCamera re-assert ensurePlayback() in a finally, so cancelling the picker (which deactivated the session) doesn't leave voice mode muted. iOS-only no-op on Android. - Extract the 350ms save-after-viewer-dismiss delay to a documented VIEWER_FADE_OUT_MS with the modal-fade coupling spelled out.
… UDID - Remove the mockist AudioModeLayout '+' test (jest.fn + toHaveBeenCalled) per our testing doctrine; the reroute is device-verified and the native sheet can't be unit-asserted. - CLAUDE.md log-pull now reads hardwareProperties.udid from `devicectl --json-output` instead of awk on the human-readable table (the last column is the model, not the UDID).
…ailed-badge fix(ios): download retry/badge, voice-mode + photo-picker hangs, .dev debug bundle
uat.sh's `git fetch --tags` recursed into the pro submodule and failed on a stale
pro pointer referenced by old core tag history but no longer on pro's remote
("not our ref") — blocking every build. The tag refresh only needs CORE tags to
pick the next beta number; the submodule is already at its pinned commit. Add
--no-recurse-submodules.
fastlane regenerates fastlane/README.md on every run, so after any build the tree is 'dirty' and the next run refuses to start. It is not build source — exclude it from the porcelain check.
fix(uat): unblock the release build (tag-fetch recursion + fastlane dirty-check)
…lake Raise the per-step waitFor budgets (4s->10s) and the overall test timeout (30s->60s). This test flaked once on a loaded pro CI runner (residency settled just past the 4s window; passed on core CI + locally with the same config). Behaviour-neutral — only more headroom for the async settling. Pairs with the stranded beta-version bump.
…rsion-sync chore: sync shipped beta version to main + harden residency test flake
Catch feat/locket-pro up to dev (595 commits, ~490 files). Conflict
resolutions (dev = canonical current; locket features preserved additively):
- whisperService.ts: HEAD base (locket transcribeFile chunking/extractWavSlice,
CoreML, diarize, vocabulary, WhisperBusyError, native logging) + dev's disjoint
deltas grafted in: activeDownloadModelId delete-scoping, B26 realtime→file
fallback, and file-ops delegated to whisperModelFiles (dev's extraction).
- whisperModels.ts: HEAD's rich catalogue (WhisperModel interface, CoreML urls,
tdrz) + single-sourced cleanTranscription with the non-ASCII \p{L}\p{N} fix.
- llm.ts: dev's version + HEAD's generateWithMaxTokens superset (streaming/GBNF/
repeatPenalty) the insights summarizer depends on.
- activeModelService: combined textOnly mmproj-skip (locket) + GPU-aware overhead
(dev) in the RAM estimate.
- ChatInput Attachments: combined document Summarize (locket) + image tap-to-view (dev).
- remoteServer: kept the 'whisper' provider arm (locket cloud transcribe).
- constants/logger/ChatScreenComponents/llmToolGeneration: took dev's canonical
versions (DevGrammar harness dropped from the chat path).
- whisperStore: kept locket additions; reconciled refreshPresentModels to not
auto-adopt a model when none is active (restores dev's explicit-select semantics).
- Deleted the two core whisper tests dev removed (pro-importing core tests).
tsc + lint green. Remaining test failures are all pro-submodule-coupled (TTS/audio
tests expecting pro-main's newer Kokoro engine) — resolved by the coupled pro
catch-up + gitlink bump, tracked as the follow-up step.
Co-Authored-By: Dishit Karia <hanmadishit74@gmail.com>
…deps in tests Point the pro submodule at the catch-up merge (recorder branch feat/continuous-smart-recorder merged with core-dev's pinned pro 3599b5b) so the pin carries BOTH the locket recorder work AND dev's newer TTS/Kokoro audio engine. Without this the core pin lagged pro-main and every voice-mode/TTS test failed. The locket recorder introduced native deps the core test env didn't mock, so requiring @offgrid/pro threw during pro activation (no slots registered → EngineBridge never mounts → TTS isReady never fires → 13 voice/TTS tests failed). Add the mocks: - @notifee/react-native (meeting reminders): full method surface + real enum values. - react-native-device-info: usePowerState hook + getPowerState (recorder power gating). Restore the onAttachPress prop in the AudioModeLayout test now that the caught-up pro has it. Full core suite green (7831 passed); the intermittent single-suite failures are pre-existing parallelism flakiness (0 individual test failures, shifts between runs). Co-Authored-By: Dishit Karia <hanmadishit74@gmail.com>
|
ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The previous bump targeted the pin core happened to hold (10f49a18), which was 13 commits behind the live recorder branch (feat/continuous-smart-recorder = 5b5921cc) — all recent locket polish (LoudnessEnhancer, header search, honest insights load, single-source RAM budget, hide-no-speech, unified feed, etc.). Repoint the submodule to the catch-up built on 5b5921cc + core-dev's pro (3599b5b), so the merge includes every recent locket commit, not a stale snapshot. tsc clean; voice/TTS/whisper suites green against the new pin. Co-Authored-By: Dishit Karia <hanmadishit74@gmail.com>
|
Paired pro-repo catch-up PR: off-grid-ai/mobile-pro#24 (submodule pin points at its tip). Review/merge them together. |
… in catch-up) The catch-up merge based whisperService on the locket branch, which predated dev's forceReset fix, so dev's cancel of the parallel B26/B28 fallback recorder was silently dropped. On an aborted/cancelled chat voice-input session, forceReset stopped the realtime stream but left the backup file recorder capturing the mic (B11-class leak). Re-add dev's guard: cancel the fallback recorder on forceReset when it's mid-flight. Chat voice mode only; the Locket recorder uses a different path. Caught by the catch-up audit (Finding 2). Co-Authored-By: Dishit Karia <hanmadishit74@gmail.com>
…ion's own recorder The prior restore used a blunt audioRecorderService.isCurrentlyRecording() check, but that recorder is a shared singleton — Voice.ts's direct-audio and file-path modes start it too. forceReset must not cancel a recording it doesn't own. Track ownership with a fallbackRecorderActive flag (set only when startRealtimeTranscription starts the fallback recorder, cleared on stop/cancel). forceReset now cancels the recorder ONLY when the realtime session owns it, and leaves any other recording untouched. Co-Authored-By: Dishit Karia <hanmadishit74@gmail.com>
|



What this is
Merge-conflict resolution only. Catches
feat/locket-proup todev(595 commits behind, ~490 files) by mergingdevin and resolving conflicts. No new feature work — every change is dev's existing work landing verbatim or a conflict resolution.How conflicts were resolved (dev = canonical; locket features preserved additively)
extractWavSlice, CoreML, diarize, vocabulary,WhisperBusyError, native logging) + dev's disjoint deltas (activeDownloadModelIddelete-scoping, B26 realtime→file fallback, file-op delegation).cleanTranscriptionsingle-sourced with the non-ASCII\p{L}\p{N}fix.generateWithMaxTokensstreaming/GBNF superset the insights summarizer needs (additive; dev's 2-arg callers unaffected).'whisper'provider arm (locket cloud transcribe).@notifee/react-native, device-info power) so@offgrid/proloads in tests.Post-merge audit + fix
Two independent 3-way audits ran against dev. The model-loading / residency / LiteRT / whisper path came back clean. One real regression was found and fixed here:
whisperService.forceReset— the catch-up built on locket's pre-fix whisperService and dropped dev's cancel of the B26/B28 fallback recorder. Restored it, and scoped it to ownership (fallbackRecorderActive) so an aborted realtime session cancels ONLY its own recorder, never a recording Voice.ts started (shared singleton).Remaining audit notes (no production impact, follow-up): the
__DEV__DevGrammar harness is half-wired on the llama.rn path (LiteRT arm still works), leavingDevGrammarModal/devInferenceas dead code; the AndroidFONTS.monomatches dev (locket'sPlatform.selectimprovement not carried — intentional for now).Gates
tsc --noEmit: clean.eslint: 0 errors. Full jest: passing (intermittent single-suite failures are pre-existing parallelism flakiness).Merge order
This PR's submodule pin points at pro PR #24's tip. Both are catch-up PRs into the locket base branches — merge them together; they unblock the existing
feat/locket-pro → devPR #433.