Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
600 commits
Select commit Hold shift + click to select a range
d915c7f
test(T051): GREEN guard — remote Ollama reasoning RENDERS (contrast t…
alichherawalla Jul 12, 2026
6ef7d77
docs(checklist): T051 Ollama green guard; T050 folded into T049 (reas…
alichherawalla Jul 12, 2026
67e4171
test(T033): PROVEN green guard — <think> reasoning renders in the thi…
alichherawalla Jul 12, 2026
ada4620
docs(checklist): T033 automated (mid-stream thinking-block green guar…
alichherawalla Jul 12, 2026
c62e13f
test(N3): fix the vacuous noImageModelDrawSend guard — assert on the …
alichherawalla Jul 12, 2026
0881cd9
test(residencySwap): fix false green — assert residency ACCOUNTING, n…
alichherawalla Jul 12, 2026
bfe13d2
test(T023b): RED — Eject All leaves ALL sidecars resident, not just w…
alichherawalla Jul 12, 2026
2e4efa2
docs(checklist): add T023b — ejectAll leaves all sidecars (DEV-B1 gen…
alichherawalla Jul 12, 2026
b61abf4
feat(residency): model selector shows the loaded model + its RAM (rem…
alichherawalla Jul 12, 2026
5ee8af7
feat(residency): image tab Currently Loaded also shows RAM consumed (…
alichherawalla Jul 12, 2026
08dd527
fix(test): add formatModelRam + estimateImageModelRam to ModelSelecto…
alichherawalla Jul 12, 2026
10cfeca
feat(settings): add 'Stay in the loop' card (Follow @alichherawalla o…
alichherawalla Jul 12, 2026
48fa40c
feat(about): add Follow @alichherawalla on X + Join Slack links to th…
alichherawalla Jul 12, 2026
fc93dcc
refactor: FOLLOW_X_URL + SLACK_INVITE_URL as shared constants (not in…
alichherawalla Jul 12, 2026
c8f7e42
feat(residency): per-model eject in the model selector (In Memory sec…
alichherawalla Jul 12, 2026
40d8b1c
test: lazy-reload after per-model eject — ejected text model reloads …
alichherawalla Jul 12, 2026
d47ed91
fix(residency): Eject All frees every resident incl. sidecars (DEV-B1)
alichherawalla Jul 12, 2026
fcdef1c
chore: bump pro — remove Pro Tools entry from Settings
alichherawalla Jul 12, 2026
d7e78c9
fix(residency): do not load whisper resident on download (DEV-B1 #1)
alichherawalla Jul 12, 2026
57eb57a
test: behavioral UI guards for the Follow/Slack cards + Pro Tools rem…
alichherawalla Jul 12, 2026
d95c044
test(residency): STT reclaimed on send (tight device) + debuggable lo…
alichherawalla Jul 12, 2026
cfb98f8
test(residency): validate STT reclaim via the model selector In Memor…
alichherawalla Jul 12, 2026
d8f91e9
docs(checklist): add residency matrix T111-T120 (co-residency/auto-ev…
alichherawalla Jul 12, 2026
fdcdeb5
test(T116): text + whisper allowed co-residence via In Memory UI
alichherawalla Jul 12, 2026
2cea43a
docs(checklist): T116 co-residence → automated (textWhisperCoresident)
alichherawalla Jul 12, 2026
2c214b8
test(T117): OS memory warning reclaims idle sidecars via In Memory UI
alichherawalla Jul 12, 2026
ce37ad2
docs(checklist): T117 auto-eviction → automated (memoryWarningEvictsS…
alichherawalla Jul 12, 2026
aa7e3d2
test(T115): voice-note send reclaims idle STT on a tight device (In M…
alichherawalla Jul 12, 2026
f7cc8bc
docs(checklist): T115 voice reclaim → automated (voiceNoteReclaimsStt)
alichherawalla Jul 12, 2026
390be1c
docs: log T119 (whisper blocked-retry) as OPEN/deferred — harness gap…
alichherawalla Jul 12, 2026
b69b780
test(T120): TTS co-resides during a voice turn via In Memory UI
alichherawalla Jul 12, 2026
29ce692
docs(checklist): T120 automated; T118/T119 deferred with honest reasons
alichherawalla Jul 12, 2026
a6c42a4
fix(test): T023 arrival — load whisper via real loadModel, not the (n…
alichherawalla Jul 12, 2026
0e218b4
test(T044): parallel calculator tools render two result bubbles (full…
alichherawalla Jul 12, 2026
3cf3829
test(T038): thinking + tool-result + answer all render (full UI, upgr…
alichherawalla Jul 12, 2026
c0aa70d
docs(checklist): T038 + T044 upgraded to full-UI (partial→automated)
alichherawalla Jul 12, 2026
d1d05d2
docs(checklist): T071 already automated (enhancementNoThinking) — fix…
alichherawalla Jul 12, 2026
df6168c
test(T038): flip Thinking on via the real quick-settings toggle, not …
alichherawalla Jul 12, 2026
7c4987d
test(T048): remote parallel tool_calls render as bubbles + reply (ful…
alichherawalla Jul 12, 2026
722b8e6
docs(checklist): T048 upgraded to full-UI (remoteParallelTools)
alichherawalla Jul 12, 2026
20ed9e7
test(T035): thinking-box header reads DONE while reasoning still stre…
alichherawalla Jul 12, 2026
e41d0ae
test(T046): add a remote server → it connects (full UI, upgrade from …
alichherawalla Jul 12, 2026
851e86d
docs(checklist): partial campaign complete — 6/13 upgraded to full-UI…
alichherawalla Jul 12, 2026
2c95bdb
test(T085): voice-mode calculator journey — STT → tool → answer (full…
alichherawalla Jul 12, 2026
e20c2ea
test(T084): voice-mode image journey — STT 'draw a dog' → route → ima…
alichherawalla Jul 12, 2026
aa7d8ec
docs(checklist): T084 + T085 voice journeys automated
alichherawalla Jul 12, 2026
95ea469
test(T014): GPU/OpenCL backend → GenerationMeta shows offloaded layer…
alichherawalla Jul 12, 2026
e22fc66
test(T020): LiteRT select is lazy (no eager warm), loads on first sen…
alichherawalla Jul 12, 2026
d527163
test(T072): enhancement reasoning must not become the image prompt (D…
alichherawalla Jul 12, 2026
a31d0fc
test(T077): chat-mode mic leaks the realtime session on navigate-away…
alichherawalla Jul 12, 2026
60137f3
test(T072): validate enhancement-reasoning red at the UI layer, not t…
alichherawalla Jul 12, 2026
556c469
docs(T015): defer NPU-gibberish as native-only (no JS surface to assert)
alichherawalla Jul 12, 2026
db1603b
test(T047): empty LAN scan shows alert AND adds no phantom server (DE…
alichherawalla Jul 12, 2026
271edba
test(T053): remote model is marked in the selector (Remote badge + se…
alichherawalla Jul 12, 2026
7c8499c
test(T057): tapping a pre-send image thumbnail opens no preview (DEV-…
alichherawalla Jul 12, 2026
b2a043e
test(T095): server+model configured → tap Continue skips onboarding i…
alichherawalla Jul 12, 2026
d992221
test(T096): support-share sheet dismisses after Share on X, does not …
alichherawalla Jul 12, 2026
7b52051
test(T097): Home Text count with a remote model active is truthful, n…
alichherawalla Jul 12, 2026
1e17504
test(T012): ModelsScreen renders N downloaded-indicator marks for N d…
alichherawalla Jul 12, 2026
ee9f5d7
test(T078): chat-mode mic double-tap opens a second realtime session …
alichherawalla Jul 12, 2026
79f4456
test(T073): enhancement step shows no streaming/progress (DEV-B30b, red)
alichherawalla Jul 12, 2026
5aafed0
test(T058,T086,T103): vision-affordance parity + voice thinking width…
alichherawalla Jul 12, 2026
0b69a3c
test(T034): max-predict cutoff is silent — no cut-off/continue indica…
alichherawalla Jul 12, 2026
3336b1f
test(T087): voice mode renders a stray empty '#' bubble after a tool …
alichherawalla Jul 12, 2026
e3c04f9
docs: defer T021 (mmproj estimate — wrong surface + harness gap) and …
alichherawalla Jul 12, 2026
39956d5
docs: mark T010/T011 deferred (need the RAG doc-attach UI harness, pe…
alichherawalla Jul 12, 2026
75d3f42
test(T083): deleting the TTS model mid-playback breaks playback (V5-g…
alichherawalla Jul 12, 2026
27f4c8d
test(T010,T011): mounted KB doc-attach — scanned-PDF message (red) + …
alichherawalla Jul 12, 2026
a6d52ca
test(T118): embedding model co-resides as a sidecar after a KB index …
alichherawalla Jul 12, 2026
044469c
test(T016): GPU/OpenCL init timeout falls back to CPU gracefully (DEV…
alichherawalla Jul 12, 2026
d17e4fd
test(T119): voice note transcribes via whisper blocked->free->retry o…
alichherawalla Jul 12, 2026
1c23eaa
docs(T021): re-scope deferral — blocked on a product decision (mmproj…
alichherawalla Jul 12, 2026
81e2414
fix(whisper): deleteModel only cancels the in-flight download when it…
alichherawalla Jul 12, 2026
82a7f3d
chore(pro): bump pointer for V4 tts residency-release fix
alichherawalla Jul 12, 2026
592bc45
fix(speech): single source of truth for speech-ready text; chat Speak…
alichherawalla Jul 12, 2026
2d9539d
chore(pro): bump pointer for Q19 speech-text DRY migration
alichherawalla Jul 12, 2026
0b99b4a
fix(whisper): don't list a truncated/sub-threshold file as a download…
alichherawalla Jul 12, 2026
e2f797a
fix(tools): single lenient tool-arg parse; recover unquoted keys + st…
alichherawalla Jul 12, 2026
019ab18
fix(tools): LiteRT surfaces tool result when the model's final turn i…
alichherawalla Jul 12, 2026
93ce7bb
fix(tools): tool router honors an explicit 'none' decline over a name…
alichherawalla Jul 12, 2026
741b327
fix(tools): LiteRT tool-loop routes media through the modelMedia seam…
alichherawalla Jul 12, 2026
2fa5edd
fix(voice): chat-mode direct-audio voice note transcribes before disp…
alichherawalla Jul 12, 2026
557a2dc
fix(stt): chat-mode mic lifecycle — stop on unmount (B11) + absorb do…
alichherawalla Jul 12, 2026
fae483c
fix(downloads): Download Manager 'downloading' count uses isDownloadi…
alichherawalla Jul 12, 2026
2c57469
fix(projects): cascade project delete, gate KB tool on project existe…
alichherawalla Jul 12, 2026
6c4828b
fix(enhance): image-prompt enhancement forces thinking OFF, streams, …
alichherawalla Jul 12, 2026
27b4112
fix(enhance): stream the partial rewrite into the enhancement message…
alichherawalla Jul 12, 2026
231a63f
fix(thinking): show remote dedicated-reasoning + 'Thinking...' header…
alichherawalla Jul 12, 2026
fa1e8cb
chore(pro): bump pointer for B27 voice thinking-block width
alichherawalla Jul 12, 2026
e326f98
chore(pro): bump pointer for B32 stray-bubble fix
alichherawalla Jul 12, 2026
b7d7635
fix(litert): re-apply sampler mid-conversation when it changes (Q18)
alichherawalla Jul 12, 2026
ef0a38d
fix(generation): surface a runtime engine failure durably inline + ke…
alichherawalla Jul 12, 2026
4ca5aa1
fix(generation): surface a silent max-predict cutoff with a cut-off i…
alichherawalla Jul 12, 2026
cd6ae22
fix(rag): roll back a failed index + name the scanned-PDF cause (KB)
alichherawalla Jul 12, 2026
b5b0de8
fix(rag): invalidate a stale-dimension tool embedding on model swap (…
alichherawalla Jul 12, 2026
2505508
fix(image): mount hydration must not orphan the active image model (i…
alichherawalla Jul 12, 2026
5b295d3
fix(settings): Reset to Defaults also resets the image params (Q12)
alichherawalla Jul 12, 2026
0749803
feat(memory): 3-mode model-loading selector (conservative/balanced/ag…
alichherawalla Jul 12, 2026
3546a1a
test(memory): 3-mode loading spec (conservative/balanced/aggressive +…
alichherawalla Jul 12, 2026
27ac4e0
fix(promo): support-open-source sheet shows at most once per session
alichherawalla Jul 12, 2026
6032147
feat(memory): implement 3-mode behavior + Load-Anyway-always + 2 pre-…
alichherawalla Jul 12, 2026
ca259f5
feat(memory): dirty-vs-clean real-RAM gate — clean residents page, di…
alichherawalla Jul 12, 2026
cd5c143
fix(memory): loadPolicySync projects the 3-mode setting, not just the…
alichherawalla Jul 12, 2026
d1de8ff
test(memory): prove no dead-end refusal (Load Anyway always offered) …
alichherawalla Jul 12, 2026
5171353
test(memory): balanced SWAP + failed-unload are dirty-vs-dirty (clean…
alichherawalla Jul 12, 2026
9ac7164
test(memory): re-pin single-model switching to conservative; 2.5x ima…
alichherawalla Jul 12, 2026
18f9ac5
fix(memory): revert the dirty-gate regression — restore Android recla…
alichherawalla Jul 13, 2026
1ba65b3
test(memory): residency matrix — co-reside/swap across all 3 modes (s…
alichherawalla Jul 13, 2026
a11e6ac
fix(memory): dirty physical ceiling refuses an oversized dirty model …
alichherawalla Jul 13, 2026
068b912
test(memory+cleanup): align override/loadPolicySync tests to ratified…
alichherawalla Jul 13, 2026
2a17bfe
fix(device-bugs): downloads recovery+retry, KB embed-fail abort, imag…
alichherawalla Jul 13, 2026
4f02171
fix(device-bugs): stuck spinner (B13), remote prompt-enhance (B30), a…
alichherawalla Jul 13, 2026
9d3bbd8
fix(image): Q7 guidance-scale default 7.5 (DRY); fix Q20 STT test; dr…
alichherawalla Jul 13, 2026
1788eb0
test(remote): align reasoning_content spec to B16/B17 — dedicated fie…
alichherawalla Jul 13, 2026
2afad7f
refactor(hygiene): extract to bring whisperService/modelResidency/ima…
alichherawalla Jul 13, 2026
298f1f9
chore(hygiene): clear all pre-push eslint errors (behavior-neutral)
alichherawalla Jul 13, 2026
5d16202
chore(hygiene): clear last 6 eslint errors in test harness (unused va…
alichherawalla Jul 13, 2026
5b35ac2
test(chat): dictation inserts into composer, not auto-send (B26 — ali…
alichherawalla Jul 13, 2026
e99ae3a
test: align 2 stale specs to ratified behavior (whisper size floor, T…
alichherawalla Jul 13, 2026
c84872e
chore: gitignore docs/wire-captures (raw device logs kept local, not …
alichherawalla Jul 13, 2026
0391730
fix(ci): unbreak the CI pipeline (secrets-in-if startup failure) + cl…
alichherawalla Jul 13, 2026
48702d0
chore(ci): local Sonar scan skips (not fails) when project uses serve…
alichherawalla Jul 13, 2026
a70781a
fix(settings): move 'Stay in the loop' card below Off Grid AI PRO, ab…
alichherawalla Jul 13, 2026
c2e874c
docs(test): comprehensive release checklist — 45 -> 185 rows (gap-aud…
alichherawalla Jul 13, 2026
b42b8d6
docs(test): move permission-prompt rows to first-use (mic->Voice, pho…
alichherawalla Jul 13, 2026
043c8e3
test(spec): UI-behavior spec-tests for onboarding-desktop-link, scan-…
alichherawalla Jul 13, 2026
a377838
fix(models,onboarding): make 3 device-reported bugs pass their spec-t…
alichherawalla Jul 13, 2026
59977b7
Merge pull request #541 from off-grid-ai/fix/onboarding-scan-litert-d…
alichherawalla Jul 13, 2026
8f36e23
fix(downloads): queued downloads survive an app kill/restart
alichherawalla Jul 13, 2026
7e65286
refactor(models): one owned device-aware litert download-warning deci…
alichherawalla Jul 13, 2026
bb180e1
fix(downloads): restore dispatches queued items without awaiting slot…
alichherawalla Jul 13, 2026
ebbc585
docs(test): checklist rows for queued-kill survival, device-aware lit…
alichherawalla Jul 13, 2026
b4bfeac
fix(generation): stop mid-turn no longer wedges the chat ('LLM servic…
alichherawalla Jul 13, 2026
65a802b
fix(capabilities): a selected-but-not-loaded GGUF shows its real Tool…
alichherawalla Jul 13, 2026
6364644
feat(models): remote selection in the Models sheet carries a cloud ma…
alichherawalla Jul 13, 2026
291f2b6
fix(boot): download-state recovery no longer blocks first paint
alichherawalla Jul 13, 2026
5bffd1c
fix(generation): stopped turn is never mislabeled 'No response / inco…
alichherawalla Jul 13, 2026
046e5ad
docs(gaps): GPU-selected-but-CPU report, manager-sheet residency desi…
alichherawalla Jul 13, 2026
fdf29b5
docs(gaps): reload is a drifted second load path (drops thinking+GPU+…
alichherawalla Jul 13, 2026
074751a
docs(gaps): mic loader during background STT download misreads as app…
alichherawalla Jul 13, 2026
1ffdc06
test(generation): RED — a send racing a settings reload silently lose…
alichherawalla Jul 13, 2026
3baa327
test(generation): RED — GPU-selected load that lands on CPU is a sile…
alichherawalla Jul 13, 2026
396bea2
fix(models): loaded-state publishes atomically after capability detec…
alichherawalla Jul 13, 2026
e34cdcc
test: delete reloadWithSettings (dead drifted second load path) and t…
alichherawalla Jul 13, 2026
0cd5755
test(models): all-branch unit layer under describeGpuFallback + captu…
alichherawalla Jul 13, 2026
d706636
docs: close the reload-drift gaps entry (root cause corrected: readin…
alichherawalla Jul 13, 2026
2e7b238
docs(claude): integration tests are the standard; failing mockist tes…
alichherawalla Jul 13, 2026
dcda950
test(models): RED — manager sheet is the residency surface (RAM chip …
alichherawalla Jul 13, 2026
d918320
test(generation): RED rendered journey — stale text failure card must…
alichherawalla Jul 13, 2026
8ab8f97
fix(generation): clear the stale text failure card at the dispatch se…
alichherawalla Jul 13, 2026
b0fe0e6
docs: checklist row 193 (stale failure card cleared on new attempt) +…
alichherawalla Jul 13, 2026
077dc91
feat(models): manager sheet is the residency surface — per-row RAM ch…
alichherawalla Jul 13, 2026
53c83b3
Merge stale-failure-card fix: card clears at generation dispatch (age…
alichherawalla Jul 13, 2026
e3f53bd
test(models): RED — Home picker tags a 2.89GB model 'may not fit' on …
alichherawalla Jul 13, 2026
12581f0
fix(models): picker fit hint uses the ONE owned device budget (fileEx…
alichherawalla Jul 13, 2026
935e9f5
test(voice): RED — background STT download must not render the mic as…
alichherawalla Jul 13, 2026
f9354fa
fix(models): Android GPU init timeout 8s -> 25s (Adreno 735 first-loa…
alichherawalla Jul 13, 2026
5f33937
test: advance fake timers past the raised 25s Android GPU init timeout
alichherawalla Jul 13, 2026
4b767c6
fix(voice): background STT download renders a determinate download ri…
alichherawalla Jul 13, 2026
89d6fa7
docs: checklist row 192 (mic download ring, not loader) + close the I…
alichherawalla Jul 13, 2026
4468910
Merge mic-loader fix: background STT download renders a download ring…
alichherawalla Jul 13, 2026
6b0c5ac
fix(generation): a stopped turn is never labeled 'Reply cut off at th…
alichherawalla Jul 13, 2026
264664c
fix(tools): never auto-inject search_knowledge_base — only user-toggl…
alichherawalla Jul 13, 2026
2888929
fix(generation): tool path displays the runtime's reasoning instead o…
alichherawalla Jul 13, 2026
acb0f54
fix(settings): Model Loading label 'Conservative' -> 'Lean' so the 3-…
alichherawalla Jul 13, 2026
45604e3
test(memory): migrate the residency suite off the removed picker In-M…
alichherawalla Jul 14, 2026
95a1d08
test(projects): guard that a real project never auto-injects search_k…
alichherawalla Jul 14, 2026
e1ea199
fix(models): reload waits for the old context's memory to be reclaime…
alichherawalla Jul 14, 2026
75439fd
fix(models): show the load spinner ON the selected model row, not a b…
alichherawalla Jul 14, 2026
4f1a936
fix(models): read the model's real trained context (arch-prefixed GGU…
alichherawalla Jul 14, 2026
12db7dc
fix(models): load spinner shows on the just-tapped row, not the previ…
alichherawalla Jul 14, 2026
8530521
fix(memory): GPU-aware text RAM estimate so Aggressive doesn't overco…
alichherawalla Jul 14, 2026
a2dc2c9
fix(models): selection highlight follows the just-tapped model during…
alichherawalla Jul 14, 2026
f540bf7
fix(models): one owner for activeModelId (select intent) + clear it o…
alichherawalla Jul 14, 2026
41128bb
fix(generation): thinking toggle applies to the current turn (no off-…
alichherawalla Jul 14, 2026
f6ec772
fix(models): show the load spinner on the active row during a no-tap …
alichherawalla Jul 14, 2026
68d2e0c
fix(vision): pair the mmproj to its model, not the first one in the dir
alichherawalla Jul 14, 2026
dc67edc
fix(vision): match mmproj on the quant-stripped model stem (quant-ind…
alichherawalla Jul 14, 2026
f42105c
fix(vision): don't trust a persisted mismatched mmproj — self-heal th…
alichherawalla Jul 14, 2026
d7d059e
fix(vision): refuse a non-belonging mmproj instead of pairing the clo…
alichherawalla Jul 14, 2026
a8ccee5
fix(downloads): show a wrench (repair), not an eye, for a vision mode…
alichherawalla Jul 14, 2026
347fc42
feat(chat): surface 'vision file missing → repair' instead of a bare …
alichherawalla Jul 14, 2026
ad01759
fix(models): wrench (not eye) for the repair action on the model cata…
alichherawalla Jul 14, 2026
dbcdf1d
fix(vision): keep the repair signal on dead-link cleanup + gate image…
alichherawalla Jul 14, 2026
0d20107
test(vision): pin the image-send gate — only a working vision model a…
alichherawalla Jul 14, 2026
c815752
refactor(vision): one strict model<->projector matching rule (SOLID/DRY)
alichherawalla Jul 14, 2026
5cb2f3e
fix(downloads): one shared projector per model across quants (no re-d…
alichherawalla Jul 14, 2026
cd924b0
fix(chat): one shared vision gate for BOTH send and resend
alichherawalla Jul 14, 2026
91ae918
fix(vision): heal the durable vision flag from the catalog → wrench s…
alichherawalla Jul 14, 2026
615f010
feat(settings): add Transcription (STT) + Text to Speech (TTS) to Mod…
alichherawalla Jul 14, 2026
8ba4504
fix(models): one reactive source for 'currently loaded' (loaded/unloa…
alichherawalla Jul 14, 2026
7f87e97
fix(generation): Stop keeps the shown partial (both engines) + stop t…
alichherawalla Jul 14, 2026
38cee0e
chore(diag): [STOP-SM] trace the keep-partial decision (convId + show…
alichherawalla Jul 14, 2026
3c31df3
fix(generation): Stop keeps a reasoning-only partial (stopped mid-THI…
alichherawalla Jul 14, 2026
1e5e2b7
fix(downloads): repair-in-progress uses the theme color, not warning …
alichherawalla Jul 14, 2026
e04d12b
fix(generation): never discard shown output on ERROR either (llama/li…
alichherawalla Jul 14, 2026
d57c986
fix(voice): chat-mode STT is dictation-to-the-input-box on every engi…
alichherawalla Jul 14, 2026
2d3ad38
fix(reasoning): strip bare <|channel>thought opener (empty thought → …
alichherawalla Jul 14, 2026
b3d9406
fix(image): read steps/cfg/guidance fresh from store, not a stale cal…
alichherawalla Jul 14, 2026
a6c0777
fix(thinking): derive Thinking capability from chat_template, not Jin…
alichherawalla Jul 14, 2026
5674d2f
chore(lint): clear pre-push blockers across the branch
alichherawalla Jul 14, 2026
d57a3fe
chore(lint): alias inner useAppStore require in HomeScreen.test to cl…
alichherawalla Jul 14, 2026
c221b08
test: right-size harness model + drop mockists of superseded behavior
alichherawalla Jul 14, 2026
ec97a55
chore(lint): drop symbols orphaned by the deleted useModelLoading sel…
alichherawalla Jul 14, 2026
6933e0a
test(memory): eviction-premise tests declare the heavy 4GB model expl…
alichherawalla Jul 14, 2026
f500ec1
test(gpu): widen reload waits — reload now includes the device-critic…
alichherawalla Jul 14, 2026
cc1be68
refactor(models): break the ModelsManagerSheet<->useResidentRows impo…
alichherawalla Jul 14, 2026
eab99f6
fix(memory): changing the load mode ejects every resident (Lean now t…
alichherawalla Jul 14, 2026
6ca8c9f
fix(generation): remote tool turn resets generating state on error (s…
alichherawalla Jul 14, 2026
7201365
fix(chat): stack message attachments vertically (voice note above ima…
alichherawalla Jul 14, 2026
314e3f7
chore(lint): trim desync-fix comment to keep generationServiceHelpers…
alichherawalla Jul 14, 2026
e57b7aa
chore(lint): condense two historical comments to keep generationServi…
alichherawalla Jul 14, 2026
3a6d745
chore(lint): condense catch comment — generationServiceHelpers now at…
alichherawalla Jul 14, 2026
930140c
ci+chore: fix CI gates — Node 22, pro submodule in typecheck/architec…
alichherawalla Jul 14, 2026
b18e65f
fix(memory)+ci: remove unwired survival-floor cluster; run CI jest se…
alichherawalla Jul 14, 2026
f3c5ca1
test(audio): assert voice thinking-block width on the constraining wr…
alichherawalla Jul 14, 2026
118eef1
fix(tools): cap tool result fed to the model (~24k chars) so a bad to…
alichherawalla Jul 14, 2026
a4c10e1
chore(pro): bump submodule to voice-mode fixes (PR #22: image render,…
alichherawalla Jul 14, 2026
c0c1570
ci: run tests on Node 24 (node:sqlite) + drop mockist textDownloadPro…
alichherawalla Jul 14, 2026
e0b0a26
ci: run tests on Node 26, not 24 (node:sqlite teardown segfault)
alichherawalla Jul 14, 2026
b3f6808
test(hardening): make project-chat ordering test deterministic (contr…
alichherawalla Jul 14, 2026
7280b52
Merge pull request #510 from off-grid-ai/refactor/parse-once-boundary
alichherawalla Jul 15, 2026
a4635d4
chore(scripts): detect wired iOS device via tunnelState, not transpor…
alichherawalla Jul 15, 2026
cc773e1
Merge pull request #547 from off-grid-ai/chore/ios-device-tunnelstate…
alichherawalla Jul 15, 2026
e66b716
docs(readme): Pro pricing is $69 lifetime and $49 annual
alichherawalla Jul 15, 2026
2546f37
docs(readme): rewrite Pro section to match the website (voice, person…
alichherawalla Jul 15, 2026
fdc0219
fix(downloads): iOS text retry no longer requires a downloadId (silen…
alichherawalla Jul 15, 2026
d275e35
fix(downloads): iOS retry marks the entry queued immediately (feedback)
alichherawalla Jul 15, 2026
8d2e00a
fix(downloads): failed downloads count on the badge + Download Manager
alichherawalla Jul 15, 2026
dc4c954
fix(ios): release the audio session before the image picker (voice-mo…
alichherawalla Jul 15, 2026
3a8ea6f
fix(ios): close image viewer before the Save confirmation (chat input…
alichherawalla Jul 15, 2026
4fac164
chore(ios): give Debug builds the ai.offgridmobile.dev bundle id
alichherawalla Jul 15, 2026
bcfb359
fix(ios): ios:device launches the bundle it just built, not a hardcod…
alichherawalla Jul 15, 2026
4175b0b
fix(chat): route voice-mode + through the unified attach handler
alichherawalla Jul 15, 2026
7d7461d
test(downloads): rendered red-flow for iOS text-retry re-issue + queu…
alichherawalla Jul 15, 2026
2294cb2
chore(chat): avoid no-void lint in handleSaveImage (use .catch)
alichherawalla Jul 15, 2026
c422fbd
review(gitar): restore audio session on picker cancel + name the view…
alichherawalla Jul 15, 2026
79578e5
review(coderabbit): drop mockist + test; parse devicectl JSON for the…
alichherawalla Jul 15, 2026
68b1302
Merge pull request #548 from off-grid-ai/fix/ios-download-retry-and-f…
alichherawalla Jul 15, 2026
30aa953
fix(uat): don't recurse submodules on the tag-refresh fetch
alichherawalla Jul 15, 2026
9245f54
fix(uat): exclude fastlane/README.md from the dirty-tree gate
alichherawalla Jul 15, 2026
f485259
Merge pull request #554 from off-grid-ai/fix/uat-tag-fetch-and-followups
alichherawalla Jul 15, 2026
9fa8731
chore(beta): 0.0.103-beta.3 (build 1784092396) [skip ci]
alichherawalla Jul 15, 2026
8c602eb
test(models): harden managerSheetResidency timeouts against CI-load f…
alichherawalla Jul 15, 2026
f916294
Merge pull request #555 from off-grid-ai/fix/postrelease-flake-and-ve…
alichherawalla Jul 15, 2026
bbd65ea
Merge offgrid/dev into locket-pro (core catch-up)
dishit-wednesday Jul 15, 2026
617f548
chore(locket): bump pro submodule to caught-up pin + mock its native …
dishit-wednesday Jul 15, 2026
d79d489
chore(locket): repoint pro pin to the LATEST recorder branch catch-up
dishit-wednesday Jul 15, 2026
3b76a7c
fix(whisper): restore fallback-recorder cancel in forceReset (dropped…
dishit-wednesday Jul 16, 2026
41765af
fix(whisper): scope forceReset's recorder-cancel to the realtime sess…
dishit-wednesday Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
121 changes: 121 additions & 0 deletions .dependency-cruiser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* dependency-cruiser — the STANDING GATE for the architectural boundaries we keep
* re-establishing by hand in review (layering, engine DIP, dead code, cycles, dep hygiene).
*
* AGGRESSIVE by design: rules are strict and at `error`. It sees the IMPORT GRAPH, not values —
* so it enforces "a screen may not import a concrete engine service" (the SO2/SO4 class, at the
* bad import) and "utils/services may not import UI" (the DR1 backward-layering class), but it
* does NOT catch the `engine === 'litert'` VALUE branch (an ESLint no-restricted-syntax rule
* guards that) or DRY drift / logic bugs. Complements the hygiene standard; does not replace it.
*
* The tree is CLEAN — zero violations, no baseline file. Every rule is fully enforced with no
* exceptions; any new violation fails `npm run depcruise` (CI + pre-push). If you ever must adopt
* a new strict rule onto legacy debt, baseline it (`depcruise --output-type baseline >
* .dependency-cruiser-known-violations.json` + run the gate with `--ignore-known`) and burn it
* down — never regenerate a baseline to hide a fresh violation.
*/
module.exports = {
forbidden: [
// ── Architecture: layering + DIP ──────────────────────────────────────────
{
name: 'no-circular',
severity: 'error',
comment: 'Import cycles make load order undefined and desync-prone. Break the cycle (import the concrete module, not the barrel; extract shared state down a layer).',
from: {},
to: { circular: true },
},
{
name: 'no-backward-layering-core',
severity: 'error',
comment: 'Core (utils/services/stores/types/constants/config) must not import UI (screens/components/navigation). If a screen owns logic the core needs, move the logic DOWN (see DR1: parseModelOutput → utils).',
from: { path: '^src/(utils|services|stores|types|constants|config)/' },
to: { path: '^src/(screens|components|navigation)/' },
},
{
name: 'utils-stay-pure',
severity: 'error',
comment: 'src/utils is the zero-IO pure layer — it must not depend on services or stores. Pure logic here is unit-testable without mocking I/O (hygiene §A).',
from: { path: '^src/utils/' },
to: { path: '^src/(services|stores)/', pathNot: '^src/utils/' },
},
{
name: 'engine-dip-no-concrete-in-ui',
severity: 'error',
comment: 'UI (screens/components) must depend on the engine ABSTRACTION (services/engines), never a concrete engine service (services/litert, services/llm). Branching on a concrete engine in a caller is the DIP violation we keep fixing (SO2/SO4). Route through services/engines.',
from: { path: '^src/(screens|components)/' },
to: { path: '^src/services/(litert|llm)(/|\\.|$)' },
},
{
name: 'components-are-leaf-ui',
severity: 'error',
comment: 'Reusable components must not import screens or navigation — a leaf UI piece depending on a whole screen inverts the dependency and creates cycles. Lift shared bits into components/ or pass them in as props.',
from: { path: '^src/components/' },
to: { path: '^src/(screens|navigation)/' },
},
// ── Dead code ─────────────────────────────────────────────────────────────
{
name: 'no-orphans',
severity: 'error',
comment: 'Orphan module (no importers, imports nothing relevant) — dead code. Confirm with grep, then delete (the standing dead-code gate that retires the manual recon).',
from: {
orphan: true,
pathNot: [
'\\.(d\\.ts)$',
'(^|/)index\\.(ts|tsx)$', // barrel/entry files
'^src/types/', // type barrels are legitimately import-only
'^src/(bootstrap|shims|config)/', // wiring/shim/config shells reached outside the graph
],
},
to: {},
},
// ── Test / build hygiene ──────────────────────────────────────────────────
{
name: 'not-to-test-from-prod',
severity: 'error',
comment: 'Production code (src) must not import test files or test utilities — that ships test-only code (and its mocks) into the app bundle.',
from: { path: '^src/', pathNot: '\\.(test|spec)\\.[jt]sx?$' },
to: { path: '(\\.(test|spec)\\.[jt]sx?$|^(__tests__|__mocks__)/)' },
},
{
name: 'not-to-dev-dep',
severity: 'error',
comment: 'Production code (src) must not import a devDependency — it will be missing in the release bundle. Move the package to dependencies, or the import out of src.',
from: { path: '^src/', pathNot: '\\.(test|spec)\\.[jt]sx?$' },
to: { dependencyTypes: ['npm-dev'], pathNot: '(@types/|typescript$)' },
},
{
name: 'no-phantom-deps',
severity: 'error',
comment: 'Imports a package that is not declared in package.json (a phantom/transitive dependency) — it breaks the moment the transitive graph changes. Declare it explicitly.',
from: {},
to: {
dependencyTypes: ['unknown', 'undetermined', 'npm-no-pkg', 'npm-unknown'],
// Known non-resolvable-by-cruiser but LEGITIMATE: whisper.rn IS declared (^0.5.5) but the
// `.rn` name defeats cruiser's resolver; @offgrid/pro is the private open-core submodule
// wired via metro haste through the ONE bootstrap loader (loadProFeatures) — intentional,
// not a phantom dep. Neither is real debt, so exclude rather than baseline.
pathNot: '(^whisper\\.rn(/|$)|^@offgrid/pro(/|$))',
},
},
{
name: 'no-deprecated-core',
severity: 'warn',
comment: 'Depends on a deprecated Node core module (e.g. punycode) — will break on a future runtime. Replace it.',
from: {},
to: { dependencyTypes: ['core'], path: '^(punycode|domain|constants|sys|_linklist|_stream_wrap)$' },
},
],
options: {
doNotFollow: { path: 'node_modules' },
tsPreCompilationDeps: true,
tsConfig: { fileName: 'tsconfig.json' },
enhancedResolveOptions: {
exportsFields: ['exports'],
conditionNames: ['import', 'require', 'node', 'default', 'types'],
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
},
reporterOptions: {
text: { highlightFocused: true },
},
},
};
33 changes: 32 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
module.exports = {
root: true,
extends: '@react-native',
extends: [
'@react-native',
// SonarJS: Sonar-grade bug/smell detection in normal lint — free, local, and it covers
// PRO too (pro has no cloud Sonar project; a private cloud project is paid-by-LOC). Most
// rules stay at `error` (recommended default) as a forward guard; the handful already
// tripped on legacy code are `warn` below with a logged burn-down (GAPS_BACKLOG).
'plugin:sonarjs/recommended-legacy',
],
plugins: [
'react-native',
'react',
'react-hooks',
'sonarjs',
],
env: {
jest: true,
Expand All @@ -29,6 +37,10 @@ module.exports = {
'no-empty': 'error',
'no-else-return': 'error',
'prefer-template': 'error',
// Dead-branch killers (the "AI leftover" class) — untyped, cheap, high signal, zero current hits.
'no-unreachable': 'error',
'no-constant-condition': ['error', { checkLoops: false }],
'no-constant-binary-expression': 'error',
complexity: ['error', 20],
'max-lines-per-function': ['error', 350],
'max-lines': ['error', 500],
Expand All @@ -43,6 +55,20 @@ module.exports = {
'react-native/no-color-literals': 'error',
'react-native/no-raw-text': 'error',
'react-native/no-single-element-style-arrays': 'error',

// SonarJS — every rule stays at the recommended `error` (a real forward guard on new code)
// EXCEPT the two handled here:
// - no-duplicate-string OFF: it fights RN styling — 'space-between'/'center'/'row' and color
// literals repeat by design across StyleSheet objects; a constant per style value is noise,
// not clarity. The one low-value SonarJS rule for this codebase.
// - the rest are `warn` (already tripped on legacy core; burn-down in docs/GAPS_BACKLOG.md,
// ratchet each back to `error` as its count hits zero).
'sonarjs/no-duplicate-string': 'off',
'sonarjs/prefer-single-boolean-return': 'warn',
'sonarjs/no-nested-template-literals': 'warn',
'sonarjs/no-collapsible-if': 'warn',
'sonarjs/prefer-immediate-return': 'warn',
'sonarjs/no-duplicated-branches': 'warn',
},
overrides: [
{
Expand All @@ -56,6 +82,11 @@ module.exports = {
'react-native/no-inline-styles': 'off',
'react-native/no-raw-text': 'off',
'react-native/no-color-literals': 'off',
// Duplicate test bodies (identical arrange/act across cases) are acceptable and clearer
// than over-DRYing tests; the real-bug SonarJS rules (mischeck, unused-collection, etc.)
// stay ON for tests — they caught a tautology assertion + a dead collection here.
'sonarjs/no-identical-functions': 'off',
'sonarjs/cognitive-complexity': 'off',
},
},
],
Expand Down
82 changes: 75 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: 'npm'

- name: Setup Java
Expand All @@ -39,15 +39,29 @@ jobs:

typecheck:
runs-on: ubuntu-latest
env:
PRO_SUBMODULE_PAT: ${{ secrets.PRO_SUBMODULE_PAT }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Check out pro submodule (private; skipped on open-core forks)
# tsc sees __tests__/pro/* which import @offgrid/pro/*; without the submodule those resolve
# to nothing (TS2307) and typecheck fails. Pull it when the PAT is present (this org's CI);
# forks without the secret skip it and tsconfig excludes the pro paths.
if: ${{ env.PRO_SUBMODULE_PAT != '' }}
env:
PRO_PAT: ${{ env.PRO_SUBMODULE_PAT }}
run: |
git config --global url."https://x-access-token:${PRO_PAT}@github.com/".insteadOf "https://github.com/"
git submodule update --init --recursive pro
git config --global --unset url."https://x-access-token:${PRO_PAT}@github.com/".insteadOf || true

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: 'npm'

- name: Install dependencies
Expand All @@ -56,8 +70,51 @@ jobs:
- name: Type check
run: npx tsc --noEmit

architecture:
# Standing gates: dependency-cruiser (layering, engine DIP, import cycles, orphans — 0 violations,
# no baseline) + knip (dead files/exports/types/deps — 0 issues). Both fail on anything NEW.
runs-on: ubuntu-latest
env:
PRO_SUBMODULE_PAT: ${{ secrets.PRO_SUBMODULE_PAT }}

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Check out pro submodule (private; skipped on open-core forks)
# knip/depcruise scan pro-importing code; without the submodule their reachability graph is
# incomplete (false unused/orphan reports). Pull it when the PAT is present; forks skip it.
if: ${{ env.PRO_SUBMODULE_PAT != '' }}
env:
PRO_PAT: ${{ env.PRO_SUBMODULE_PAT }}
run: |
git config --global url."https://x-access-token:${PRO_PAT}@github.com/".insteadOf "https://github.com/"
git submodule update --init --recursive pro
git config --global --unset url."https://x-access-token:${PRO_PAT}@github.com/".insteadOf || true

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Dependency-cruiser (architecture gate)
run: npm run depcruise

- name: knip (dead-code gate)
run: npm run knip

test:
runs-on: macos-latest
env:
# Exposed at the job level so the step `if:` below can read it: the `secrets` context
# is NOT allowed inside an `if:` condition (GitHub rejects the whole workflow at
# startup — a 0-second failure), but the `env` context IS. This one line being an
# `if: ${{ secrets.* }}` is what took the entire CI pipeline down.
PRO_SUBMODULE_PAT: ${{ secrets.PRO_SUBMODULE_PAT }}

steps:
- name: Checkout code
Expand All @@ -68,9 +125,9 @@ jobs:
# pro/ submodule so the pro-dependent suites (TTS/MCP/audio) run against the REAL
# package — a green run then actually exercises pro, not a stub. Forks without the
# secret skip this: proExists=false and jest runs the open-core suite instead.
if: ${{ secrets.PRO_SUBMODULE_PAT != '' }}
if: ${{ env.PRO_SUBMODULE_PAT != '' }}
env:
PRO_PAT: ${{ secrets.PRO_SUBMODULE_PAT }}
PRO_PAT: ${{ env.PRO_SUBMODULE_PAT }}
run: |
git config --global url."https://x-access-token:${PRO_PAT}@github.com/".insteadOf "https://github.com/"
git submodule update --init --recursive pro
Expand All @@ -79,7 +136,13 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
# Node 26. The RAG/knowledge-base suites run a REAL in-memory database via node's
# built-in `node:sqlite` (__tests__/harness/sqliteFake.ts). That module is absent on
# Node 20 and flag-gated on Node 22. On Node 24 it loads but its native teardown
# SEGFAULTs the process under jest's --forceExit (exit 139) — reproduced locally: the
# full suite exits 139 on Node 24 and exits 0 on Node 26. Node 26's node:sqlite fixes
# that teardown crash, so the test runtime is pinned to 26.
node-version: '26'
cache: 'npm'

- name: Setup Java
Expand All @@ -92,7 +155,7 @@ jobs:
run: npm ci

- name: Run Jest tests
run: npx jest --coverage --forceExit
run: npx jest --coverage --forceExit --runInBand

- name: Install Android NDK
# Configuring the native modules (op-sqlite etc.) needs the pinned NDK; the macOS
Expand Down Expand Up @@ -147,7 +210,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
cache: 'npm'

- name: Setup Java
Expand All @@ -173,3 +236,8 @@ jobs:

- name: Build Android Release
run: cd android && ./gradlew assembleRelease

# NOTE: SonarCloud runs via Automatic Analysis (SonarCloud-side, on the public core project) —
# no CI job. A CI scan would only add coverage import, which Codecov (in the `test` job) already
# does, so it'd be redundant complexity. Pro is scanned locally by eslint-plugin-sonarjs (free,
# private, no leak). See docs/GAPS_BACKLOG.md.
16 changes: 16 additions & 0 deletions .github/workflows/release-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,22 @@ jobs:
git commit -m "chore: update AltStore source for v${{ env.VERSION }} [skip ci]"
git push

# Announce the iOS release in Slack: the GitHub release link + its notes. iOS doesn't
# generate release-notes.md itself (Android creates the shared vX.Y.Z release with notes),
# so pull the notes from the release body best-effort. Fail-soft: notify script always
# exits 0. Needs secret SLACK_WEBHOOK_URL (an Incoming Webhook, channel-bound).
- name: Announce release in Slack
if: success()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
PRODUCT: Off Grid AI Mobile (iOS)
VERSION: ${{ env.VERSION }}
NOTES_FILE: release-notes.md
run: |
gh release view "v${VERSION}" --json body -q .body > release-notes.md 2>/dev/null || true
node scripts/notify-slack-release.mjs

- name: Cleanup keychain
if: always()
run: |
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,15 @@ jobs:
ORG_GRADLE_PROJECT_OFFGRID_UPLOAD_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ORG_GRADLE_PROJECT_OFFGRID_UPLOAD_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: bundle exec fastlane android release

# Announce the release in Slack: the GitHub release link + the notes generated above.
# Fail-soft (notify script always exits 0), so a Slack outage never fails a release.
# Needs secret SLACK_WEBHOOK_URL (an Incoming Webhook, channel-bound). No secret => no-op.
- name: Announce release in Slack
if: success()
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
PRODUCT: Off Grid AI Mobile (Android)
VERSION: ${{ env.NEW_VERSION }}
NOTES_FILE: release-notes.md
run: node scripts/notify-slack-release.mjs
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,9 @@ docs/TRACTION_KNOWLEDGE_BASE.md
/image-*.png
# Local marketing drafts (not part of the app)
marketing/

# Personal device-test commentary (raw voice, not for the repo)
docs/DEVICE_SESSION_COMMENTARY.md

# Device wire-capture logs (raw, large, session-specific — kept locally, not versioned)
docs/wire-captures/
3 changes: 3 additions & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ if [ -n "$PUSHED_JS" ]; then

echo "▶ JS/TS tests (related to changed files)..."
echo "$PUSHED_JS" | tr '\n' '\0' | xargs -0 npx jest --findRelatedTests --passWithNoTests

echo "▶ Architecture gate (dependency-cruiser)..."
npm run depcruise
fi

if [ -n "$PUSHED_SWIFT" ]; then
Expand Down
Loading