Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
278 commits
Select commit Hold shift + click to select a range
6f5e433
test(sync): prove offline convergence on relaunch
alichherawalla Jul 27, 2026
69f16cc
fix(sync): propagate non-destructive project deletes
alichherawalla Jul 27, 2026
02278bf
docs(sync): record resolved project deletion
alichherawalla Jul 27, 2026
9ced2a5
fix(sync): label development entitlement honestly
alichherawalla Jul 27, 2026
8f40da8
docs(sync): clarify development device status
alichherawalla Jul 27, 2026
41e0e5a
test(sync): approve desktop pairing in app
alichherawalla Jul 27, 2026
1b29700
feat(sync): recover device discovery without relaunch
alichherawalla Jul 27, 2026
2fb00fd
fix(sync): preserve pairing and message reasoning
alichherawalla Jul 27, 2026
53c0b61
feat(sync): register transferred model packages
alichherawalla Jul 27, 2026
a45ba81
test(sync): bound model package journey
alichherawalla Jul 27, 2026
dfc16b1
feat(sync): bridge mobile clipboard natively
alichherawalla Jul 27, 2026
562f11d
feat(sync): reconcile trusted devices
alichherawalla Jul 27, 2026
e339d6e
test(clipboard): prove attributed mobile history
alichherawalla Jul 27, 2026
4d041e2
fix(clipboard): preserve remote device attribution
alichherawalla Jul 27, 2026
9deceba
feat(sync): give knowledge documents stable identity
alichherawalla Jul 28, 2026
d125902
docs(sync): record knowledge document contract
alichherawalla Jul 28, 2026
b51d80a
feat(sync): integrate knowledge document transfer
alichherawalla Jul 28, 2026
9f67d2b
docs(sync): record knowledge document convergence
alichherawalla Jul 28, 2026
3dd8ad4
test(sync): automate physical iOS knowledge checks
alichherawalla Jul 28, 2026
d539644
test(sync): prove saved device management journey
alichherawalla Jul 28, 2026
36c2d41
test(sync): prove clipboard permission guidance
alichherawalla Jul 28, 2026
6513b33
fix(sync): render portable tool artifacts
alichherawalla Jul 28, 2026
9e305d3
fix(rag): share short document chunking
alichherawalla Jul 28, 2026
b68e2b5
fix(sync): surface knowledge transfer failures
alichherawalla Jul 28, 2026
2bf64a6
feat(sync): advertise a persistent device name
alichherawalla Jul 28, 2026
46df6bb
refactor(sync): consume shared transfer queue
alichherawalla Jul 28, 2026
b6ff258
fix(metro): resolve shared RAG build directly
alichherawalla Jul 28, 2026
5762f0a
feat(sync): connect nearby Apple devices
alichherawalla Jul 28, 2026
9aa9eb8
docs(sync): record Apple proximity readiness
alichherawalla Jul 28, 2026
5a567a1
docs(sync): align the post-proximity roadmap
alichherawalla Jul 28, 2026
f9412d8
docs(sync): inventory mobile replicated data
alichherawalla Jul 28, 2026
87f8507
docs(sync): narrow corpus and cap device mesh
alichherawalla Jul 28, 2026
5cd9ede
feat(sync): stabilize message attachment identities
alichherawalla Jul 28, 2026
ff2bea6
feat(sync): persist portable record provenance
alichherawalla Jul 28, 2026
cda022d
feat(sync): surface personal mesh routes
alichherawalla Jul 28, 2026
fa566a9
feat(sync): expose ambient media consent
alichherawalla Jul 28, 2026
02edb64
feat(sync): add portable shared file mutations
alichherawalla Jul 28, 2026
fb3924c
feat(sync): expose received shared files
alichherawalla Jul 28, 2026
621dc12
feat(sync): watch opted-in iOS screenshots
alichherawalla Jul 28, 2026
3eba7c2
feat(sync): enforce ambient sharing consent
alichherawalla Jul 28, 2026
f02cf77
feat(sync): promote mobile setup and recovery
alichherawalla Jul 28, 2026
dd8c4e0
docs(sync): record mobile recovery gate
alichherawalla Jul 28, 2026
7f7026a
test(sync): keep offline devices discoverable from home
alichherawalla Jul 28, 2026
bdc5000
test(sync): exercise consumer control center journeys
alichherawalla Jul 28, 2026
6bc71b9
docs(sync): record consumer control center split
alichherawalla Jul 28, 2026
b6cb7c8
chore(sync): consume authenticated reconnect recovery
alichherawalla Jul 28, 2026
e5aeced
chore(sync): consume home spacing fix
alichherawalla Jul 28, 2026
ec4784c
chore(sync): consume completed activity archive
alichherawalla Jul 28, 2026
3da4276
test(sync): prove the synced files journey
alichherawalla Jul 28, 2026
e01a58c
docs(sync): record the synced files library
alichherawalla Jul 28, 2026
c8aec41
test(sync): retain completed transfer activity
alichherawalla Jul 28, 2026
4f4a638
refactor(sync): share transfer activity policy
alichherawalla Jul 28, 2026
7a03743
refactor(sync): share device control state
alichherawalla Jul 28, 2026
a8e6615
fix(sync): report available native routes
alichherawalla Jul 28, 2026
a6a3d1f
fix(sync): use factual route capabilities
alichherawalla Jul 28, 2026
9173268
fix(sync): report screenshot source availability
alichherawalla Jul 28, 2026
b51b42b
test(sync): verify shared control center journeys
alichherawalla Jul 28, 2026
464f7c8
fix(sync): keep discovery callbacks alive
alichherawalla Jul 28, 2026
3c2b639
test(sync): exercise real knowledge document journey
alichherawalla Jul 28, 2026
932438a
test(sync): observe real clipboard outcomes
alichherawalla Jul 28, 2026
199333a
test(sync): use real licensed device journey
alichherawalla Jul 28, 2026
e7b61f6
test(sync): require real navigation
alichherawalla Jul 28, 2026
791ad64
test(sync): keep acceptance at integration boundary
alichherawalla Jul 28, 2026
0b093b3
fix(sync): keep mesh counts consistent
alichherawalla Jul 28, 2026
f601472
fix(sync): use Unix clipboard timestamps
alichherawalla Jul 28, 2026
b070ca5
feat(sync): consume shared pairing lifecycle
alichherawalla Jul 28, 2026
4dfbabc
test(sync): assert one pairing cancel action
alichherawalla Jul 28, 2026
85e6164
feat(sync): consume bilateral mesh revocation
alichherawalla Jul 28, 2026
62708c8
docs(sync): record bilateral mesh eviction
alichherawalla Jul 28, 2026
9882250
test(sync): preserve clipboard origin through native echoes
alichherawalla Jul 28, 2026
dc9cee8
docs(sync): record shared clipboard ownership
alichherawalla Jul 28, 2026
212bf2d
test(sync): align journeys with shared lifecycle
alichherawalla Jul 28, 2026
7b129fe
docs(sync): record app-ready regression gate
alichherawalla Jul 28, 2026
3d587c1
fix(sync): refresh nearby discovery deterministically
alichherawalla Jul 29, 2026
2d8d8a8
feat(sync): surface shared runtime projections
alichherawalla Jul 29, 2026
ab40a4b
feat(sync): consume shared pairing and repair contracts
alichherawalla Jul 29, 2026
f9a02d2
fix(sync): format pairing code entry
alichherawalla Jul 29, 2026
4083d0a
fix(sync): retain nearby session callbacks safely
alichherawalla Jul 29, 2026
d255847
feat(sync): surface sharing approvals from home
alichherawalla Jul 29, 2026
c42f4b4
fix(sync): show connected transport route
alichherawalla Jul 29, 2026
3d4299f
feat(sync): surface file completion notifications
alichherawalla Jul 29, 2026
23a0167
feat(sync): expose explicit file sharing
alichherawalla Jul 29, 2026
aa7282d
feat(sync): simplify mobile files and activity
alichherawalla Jul 29, 2026
b21d2cd
refactor(sync): compact mobile sync surfaces
alichherawalla Jul 29, 2026
706c417
fix(sync): refine mobile sync controls
alichherawalla Jul 29, 2026
3991d21
feat(sync): add portable directory access
alichherawalla Jul 29, 2026
7980c52
feat(sync): enable ambient downloads
alichherawalla Jul 29, 2026
a9fbbb6
fix(sync): resolve iOS directory bookmarks
alichherawalla Jul 29, 2026
56164c4
feat(sync): expose clipboard search
alichherawalla Jul 29, 2026
fa612eb
fix(sync): retain offline clipboard delivery
alichherawalla Jul 29, 2026
9139c6f
feat(sync): prepare mobile entitlement boundaries
alichherawalla Jul 29, 2026
b57c99d
chore(sync): advance mobile Pro registry adapter
alichherawalla Jul 29, 2026
1d9dd73
feat(sync): bridge restricted pairing entitlement
alichherawalla Jul 29, 2026
50b9acb
feat(sync): expose restricted entitlement bootstrap
alichherawalla Jul 29, 2026
f355ca4
feat(onboarding): introduce Pro Sync
alichherawalla Jul 29, 2026
635aed8
fix(ios): resolve shared pairing crypto modules
alichherawalla Jul 29, 2026
4d878d2
fix(sync): gate clipboard observation by entitlement
alichherawalla Jul 29, 2026
5e2eb1b
fix(sync): gate user entry behind Pro
alichherawalla Jul 29, 2026
e48286d
fix(sync): gate notification entry behind Pro
alichherawalla Jul 29, 2026
e20e058
fix(pro): normalize pasted license keys
alichherawalla Jul 29, 2026
d981143
fix(pro): log sanitized license validation
alichherawalla Jul 29, 2026
70123de
refactor(license): render shared activation outcomes
alichherawalla Jul 29, 2026
448cb6a
fix(license): revalidate entitlement lifecycle
alichherawalla Jul 30, 2026
42e82d0
refactor(sync): use authoritative device registry
alichherawalla Jul 30, 2026
993c07e
fix(sync): register the mobile installation
alichherawalla Jul 30, 2026
9a45945
fix(sync): require credential authority
alichherawalla Jul 30, 2026
af07931
chore(sync): advance Pro installation binding
alichherawalla Jul 30, 2026
5d29031
fix(sync): use licensed installation identity
alichherawalla Jul 30, 2026
14183b1
chore(sync): trace clipboard delivery sources
alichherawalla Jul 30, 2026
2544721
fix(sync): stop public core minting a second device identity
alichherawalla Jul 30, 2026
931bc51
feat(sync): keep the mesh reachable in the background on both platforms
alichherawalla Jul 30, 2026
835b4e9
docs(sync): Personal Mesh test matrix across macOS, iOS and Android
alichherawalla Jul 30, 2026
3745bb6
docs(sync): correct the Personal Mesh gap list
alichherawalla Jul 30, 2026
db86c79
docs(sync): close PM5 - key revocation is out of scope
alichherawalla Jul 30, 2026
d043df0
chore(sync): bump pro for the pairing-code sheet
alichherawalla Jul 30, 2026
ff8e7c4
docs(sync): log the missing receive-side consent policy
alichherawalla Jul 30, 2026
2e5eda7
docs(sync): close PM9 - same-owner devices auto-accept
alichherawalla Jul 30, 2026
8fed5c8
feat(sync): emit streaming updates so Pro can mirror a reply live
alichherawalla Jul 30, 2026
7b9165d
docs(sync): log two Android ambient-sharing gaps found on device
alichherawalla Jul 30, 2026
95ef04f
feat(sync): show a reply as it generates on another device
alichherawalla Jul 30, 2026
15c6254
fix(chats): order Home's recent chats by last activity
alichherawalla Jul 30, 2026
c9eb7f0
chore(sync): bump pro for chat-stream diagnostics
alichherawalla Jul 30, 2026
da7e69e
chore(chat): log whether core emits a streaming update
alichherawalla Jul 30, 2026
29d228e
chore(sync): bump pro for state-sync readiness log
alichherawalla Jul 30, 2026
6871a93
refactor(chat): let pro observe streaming state instead of emitting it
alichherawalla Jul 30, 2026
e381b13
docs: hand off the Personal Mesh sync session
alichherawalla Jul 31, 2026
4c487d5
feat(ui): add a shared Accordion and collapse the sharing sections in…
alichherawalla Jul 31, 2026
a98a976
fix(pro): fill the Pro screen once you have paid, and drop the icon t…
alichherawalla Jul 31, 2026
2d1ffeb
refactor(chat): render peer previews from the shared projection
alichherawalla Jul 31, 2026
7710991
fix(ui): keep the model-loading bar moving while the model loads
alichherawalla Jul 31, 2026
72e6488
fix(chat): a reload-model tap that fails now says so
alichherawalla Jul 31, 2026
20442fd
fix(chat): do not offer a model reload when no model is selected
alichherawalla Jul 31, 2026
5157aa5
feat(models): one resolver for which text model is selected
alichherawalla Jul 31, 2026
53b2348
refactor(chat): read the active text model from one hook, not a priva…
alichherawalla Jul 31, 2026
6790a16
fix(models): the picker's spinner follows the service, not the tap
alichherawalla Jul 31, 2026
09382f0
fix(models): one owner for which text model to load
alichherawalla Jul 31, 2026
2ad00f9
refactor(chats): take the list preview line from shared
alichherawalla Jul 31, 2026
42c083c
docs: record what landed today and what is left
alichherawalla Jul 31, 2026
18c1238
feat(sync): Android screenshot capture and MediaStore downloads
alichherawalla Jul 31, 2026
487499f
chore: point at today's pro sync work
alichherawalla Jul 31, 2026
8d34f57
test(chat): teach the ChatScreen stub the model-selection seam
alichherawalla Jul 31, 2026
40ec0a5
test(models): one place defines the model-selection stub
alichherawalla Jul 31, 2026
64ea5d7
chore: merge pro main (CI core-ref fix, TTS load-anyway, voice attach…
alichherawalla Jul 31, 2026
b48ef72
refactor(chat): move the tool-call surfaces out of ChatMessage
alichherawalla Jul 31, 2026
0c69e3d
refactor(settings): bring SettingsScreen inside the caps
alichherawalla Jul 31, 2026
ab891ef
feat(ui): one screen header, matching Settings and Model Settings
alichherawalla Jul 31, 2026
cd26d0f
feat(sync): send and restore the tools a turn was offered
alichherawalla Jul 31, 2026
978c038
feat(projects): paste text straight into a knowledge base
alichherawalla Jul 31, 2026
d226ff1
fix(sync): Android does not offer a LAN route it cannot dial
alichherawalla Jul 31, 2026
024aa56
docs: hand off the mesh session with what is verified and what is not
alichherawalla Jul 31, 2026
6f7111b
docs: Android to macOS over LAN is verified on device
alichherawalla Jul 31, 2026
b5ca2d8
downloads: share what is actually in the folder, and fix the Pro lice…
alichherawalla Jul 31, 2026
1586c0a
screenshots: read every new row, not just the newest one
alichherawalla Jul 31, 2026
4248d93
knowledge base: call it Text
alichherawalla Jul 31, 2026
c9ff4dd
tests: the Downloads card and the model a phone can send
alichherawalla Jul 31, 2026
f588f19
chore: bump pro to the model transfer, home card and screenshot work
alichherawalla Jul 31, 2026
7d2359c
sync: native file hashing and a notification coalescer
alichherawalla Jul 31, 2026
d1425f8
tests: follow the pairing flow and the pro gate as they are now
alichherawalla Aug 1, 2026
305dbfa
tests: point the moved licensing tests at pro, and fix a stub that ar…
alichherawalla Aug 1, 2026
298ce82
docs: a line-by-line sync test sheet for this build
alichherawalla Aug 2, 2026
6a6c4b3
docs: quote the sync checklist row whose expected result contains a c…
alichherawalla Aug 2, 2026
36eb7c2
chat: local wall-clock times, and tool results above the answer
alichherawalla Aug 2, 2026
40060f6
chore: bump pro to the model transfer job owner
alichherawalla Aug 3, 2026
4e25e04
tests: the transfer sheet needs a navigation container now that it li…
alichherawalla Aug 3, 2026
68ad9be
sync: the phones move a payload natively, sealed frame by frame
alichherawalla Aug 3, 2026
ca127d3
sync: the phones can stop a payload they are still sending
alichherawalla Aug 3, 2026
1c65b55
test: two devices can pair in the harness, so a transfer can be teste…
alichherawalla Aug 3, 2026
8682c54
chore: lint the physical-sync scripts, drop a flag nothing read, and …
alichherawalla Aug 3, 2026
fc9287e
sync: both phones resume a payload instead of starting it again
alichherawalla Aug 3, 2026
3ac770b
chat: messages read in the order they were written
alichherawalla Aug 3, 2026
4215191
test(sync): follow the mesh for licence capacity, and record that For…
alichherawalla Aug 4, 2026
178091a
test(sync): licence capacity follows the mesh, and freeing a seat is …
alichherawalla Aug 4, 2026
69caf26
test(sync): the status line is one word, and a peer is announced only…
alichherawalla Aug 4, 2026
eb09cd5
test(sync): an advertised peer announces a LAN address, because loopb…
alichherawalla Aug 4, 2026
64c2639
test(sync): one place sets up a licensed phone, and a paired peer hol…
alichherawalla Aug 4, 2026
5bec84b
test(sync): the model transfer journey runs end to end again
alichherawalla Aug 4, 2026
64994a3
test(sync): the saved-device journeys run again, and a pairing that l…
alichherawalla Aug 4, 2026
416e906
test(sync): a paired peer holds an installation, and the pairing that…
alichherawalla Aug 4, 2026
8789715
test(sync): a device joining a licence registers itself, and the stat…
alichherawalla Aug 4, 2026
e45faf6
test(sync): the ambient sharing journey follows the approval to Notif…
alichherawalla Aug 4, 2026
905b130
test(sync): the clipboard journeys run again, and the phone registers…
alichherawalla Aug 4, 2026
0ec3ea0
test(sync): two of the three persistence journeys run again
alichherawalla Aug 4, 2026
e129744
test(sync): the knowledge-document journey pairs without an accept step
alichherawalla Aug 4, 2026
264d0b2
test(sync): the RN transport pairing test uses a real code and a real…
alichherawalla Aug 4, 2026
ac89aa0
test(sync): cover the two branches today's eviction fix added
alichherawalla Aug 4, 2026
0ed23fa
test(pro): the Pro detail suite runs again, and says why four of it s…
alichherawalla Aug 4, 2026
e77ccb8
docs: the eviction ordering is the normal flow, not a rare race
alichherawalla Aug 4, 2026
bb71bb5
test(sync): the eviction transaction is covered to the line
alichherawalla Aug 4, 2026
b28d664
test(sync): cover the eviction's local half to the line, and say why …
alichherawalla Aug 4, 2026
de72124
test(sync): cover the identity migration and the repair decision
alichherawalla Aug 4, 2026
282f98a
test(sync): cover the remembered roster, the file the user picked, an…
alichherawalla Aug 4, 2026
70e82d9
test(sync): cover making a transferred file appear where the user exp…
alichherawalla Aug 4, 2026
8f6636d
test(sync): two phones talking with no network between them
alichherawalla Aug 4, 2026
ae8b76c
test(sync): the two roads to a checksum must meet, and what the phone…
alichherawalla Aug 4, 2026
6fd437d
test(sync): the fast path a large file takes, end to end
alichherawalla Aug 4, 2026
61d8062
test(sync): sharing a folder on the platform that will not let you sh…
alichherawalla Aug 4, 2026
0501d16
test(knowledge): pasting a page you copied straight into a knowledge …
alichherawalla Aug 4, 2026
ee3b460
test(sync): what this device calls itself, and what it must not decide
alichherawalla Aug 4, 2026
3d2e74b
test(sync): type the remote-stream fixture against the shared preview
alichherawalla Aug 4, 2026
4432686
test(sync): check the stream fixture against the shared type at its s…
alichherawalla Aug 4, 2026
c38be4d
test(sync): a screenshot reaching your other devices as you take it
alichherawalla Aug 4, 2026
927d7ff
test(sync): a saved device seen but not dialable has to say so
alichherawalla Aug 4, 2026
b7d7f45
test(pro): entering a licence key is a transaction, and it has to be …
alichherawalla Aug 4, 2026
2f13841
test(harness): let a test free a seat the way another device does
alichherawalla Aug 4, 2026
105330f
test(sync): what this phone accepts, and its record of what arrived
alichherawalla Aug 4, 2026
7985f78
test(sync): the ambient rules, the queue, and the answers already given
alichherawalla Aug 4, 2026
bdae098
test(sync): a file arriving on the phone lands only once it is whole
alichherawalla Aug 4, 2026
223df25
test(sync): the list that says a file actually arrived
alichherawalla Aug 4, 2026
1994f50
test(sync): the licence's record of the mesh, from the phone
alichherawalla Aug 4, 2026
4ad07cf
test(licensing): the licence this phone holds, and what happens to it…
alichherawalla Aug 4, 2026
62ee53d
test(sync): the record of transfers that finished, and the format it …
alichherawalla Aug 4, 2026
4597a43
test(sync): the devices this phone trusts, and the secrets that prove it
alichherawalla Aug 4, 2026
43e1cdd
test(sync): read every trust document this app has ever written
alichherawalla Aug 4, 2026
46f449f
test(sync): a model arriving on this device, over and over
alichherawalla Aug 4, 2026
0747b7d
test(sync): two devices agreeing about the licence they share
alichherawalla Aug 4, 2026
04e192d
test(sync): standing consent to share a file, and every way to withdr…
alichherawalla Aug 4, 2026
909d612
test(sync): the edges of the sharing record it keeps
alichherawalla Aug 4, 2026
221a9e4
test(sync): await the refusal, so a lint warning is not a hole
alichherawalla Aug 4, 2026
9529bb0
test(sync): which of this phone's files exist to be shared at all
alichherawalla Aug 4, 2026
129a284
test: fix ten failing tests that were asserting things the code never…
alichherawalla Aug 4, 2026
53065f9
test: five more failures that were the tests, not the app
alichherawalla Aug 4, 2026
6028cf2
test: three date assertions built with the formatter the app dropped
alichherawalla Aug 4, 2026
a4da5d5
test(rag): run the knowledge-base round-trip on real SQLite
alichherawalla Aug 4, 2026
6775eff
test: three failures where the code moved and the tests did not
alichherawalla Aug 4, 2026
1b5dd3a
test(licensing): make the seat-replacement test reach Keygen at all
alichherawalla Aug 5, 2026
d945db8
test(sync): hold the offline-eviction journey open with its cause on …
alichherawalla Aug 5, 2026
3724841
test(sync-ui): cover the receiving scope, where one switch means two …
alichherawalla Aug 5, 2026
93922a4
test: drop two unused declarations the lint gate rejected
alichherawalla Aug 5, 2026
5158194
test: drop the now-unused Keychain import
alichherawalla Aug 5, 2026
14107f7
chore(arch): stop the orphan gate flagging a util that only pro imports
alichherawalla Aug 5, 2026
5227ec6
test(sync-ui): cover every way an explicit file share can end
alichherawalla Aug 5, 2026
5f0ff1e
test: type the pro-absent guard with jest's own callback type
alichherawalla Aug 5, 2026
3dc7ef1
chore(dead-code): retire eleven exports nothing consumed
alichherawalla Aug 5, 2026
ab27207
chore: drop imports the removed pass-through was the only user of
alichherawalla Aug 5, 2026
e939822
fix(android): backport java.time so API 24 does not crash
alichherawalla Aug 5, 2026
62c6c30
test(android): pass the resume offset the blob channel now takes
alichherawalla Aug 5, 2026
2382ae7
ci: check out the shared monorepo so @offgrid/sync resolves
alichherawalla Aug 5, 2026
dbe6076
test: make a dead button impossible on the Activity list
alichherawalla Aug 5, 2026
bca6097
test: make a pro suite that cannot load its module fail instead of pa…
alichherawalla Aug 5, 2026
2e7fbac
test: cover the preview under a shared file
alichherawalla Aug 5, 2026
140f56b
ci: one job instead of four, and fix the shared provisioning that bro…
alichherawalla Aug 5, 2026
9d4b51c
chore: drop the Provit dependency, keeping the device-driving capabil…
alichherawalla Aug 5, 2026
0f31d2a
ci: keep the shared install fallback, at the root where it can help
alichherawalla Aug 5, 2026
a1c9531
test(e2e): a device harness on WDA and adb, validated on both phones
alichherawalla Aug 5, 2026
954051d
test(e2e): target testIDs on both platforms, and stop hiding them
alichherawalla Aug 5, 2026
6b67328
test(e2e): a two-device layer, and the mesh journey it exists for
alichherawalla Aug 5, 2026
1fe4bad
chore: tell knip about the e2e harness's real dependencies
alichherawalla Aug 5, 2026
d86ef28
chore: point at the pushed mobile-pro commits, and the iOS device scr…
alichherawalla Aug 5, 2026
ec266a5
test: address the review, including a real hole in the dead-button sweep
alichherawalla Aug 5, 2026
56364c7
fix(sync): seek absolutely when resuming an upload, instead of skipping
alichherawalla Aug 5, 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
  •  
  •  
  •  
10 changes: 10 additions & 0 deletions .dependency-cruiser.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ module.exports = {
'(^|/)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
// Core utilities whose only importers are in the private pro/ submodule. The cruiser scans src
// alone, so a module used exclusively by pro reads as an orphan even though deleting it would
// break the build - coalesce is imported by pro/sync/fileTransferService and
// pro/sync/ambientShareService. Same reasoning as the @offgrid/pro exception below: not real
// debt, so excluded rather than baselined. Confirm with grep in pro/ before adding to this list.
'^src/utils/coalesce\\.ts$',
],
},
to: {},
Expand Down Expand Up @@ -106,6 +112,10 @@ module.exports = {
},
],
options: {
// Keep file-linked workspace packages under node_modules so dependency-cruiser can
// classify them from this package.json instead of treating their real paths as
// undeclared source files outside the mobile project.
preserveSymlinks: true,
doNotFollow: { path: 'node_modules' },
tsPreCompilationDeps: true,
tsConfig: { fileName: 'tsconfig.json' },
Expand Down
15 changes: 14 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,22 @@ module.exports = {
'sonarjs/no-duplicated-branches': 'warn',
},
overrides: [
{
files: ['scripts/physical-sync/**/*.mjs'],
parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
},
},
{
// Relax structural rules in test files — large test suites and helpers are acceptable
files: ['__tests__/**/*', '*.test.ts', '*.test.tsx', 'jest.setup.ts'],
files: [
'__tests__/**/*',
'scripts/physical-sync/__tests__/**/*.mjs',
'*.test.ts',
'*.test.tsx',
'jest.setup.ts',
],
rules: {
'max-lines': 'off',
'max-lines-per-function': 'off',
Expand Down
222 changes: 110 additions & 112 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,106 +8,20 @@ on:
branches:
- main

# ONE job, not four.
#
# lint, typecheck, architecture and test used to be separate jobs, and the only thing that made them
# separate was the gate at the end - all four repeated the SAME seven setup steps (checkout, pro
# submodule, node, java, two shared checkouts, install). That duplication was not just slow, it was
# the bug: the shared-provisioning step was wrong in all four copies at once, and one of the four
# (lint) had silently lost its `env: PRO_SUBMODULE_PAT`, so it never provisioned shared at all.
#
# Setup happens once here and every gate runs as a step against it. Each gate carries
# `if: ${{ !cancelled() && steps.install.outcome == 'success' }}`, so a red gate does NOT hide the
# ones after it - the whole list still reports, which is the one thing the four separate checks were
# genuinely good at. The trade-off is wall-clock: the gates no longer run in parallel.
jobs:
lint:
runs-on: macos-latest

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

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

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'

- name: Install dependencies
run: npm ci

- name: Install SwiftLint
run: brew install swiftlint

- name: Lint
run: npm run lint

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: '22'
cache: 'npm'

- name: Install dependencies
run: npm ci

- 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:
ci:
runs-on: macos-latest
env:
# Exposed at the job level so the step `if:` below can read it: the `secrets` context
Expand All @@ -121,9 +35,11 @@ jobs:
uses: actions/checkout@v4

- name: Check out pro submodule (private; skipped on open-core forks)
# When the PRO_SUBMODULE_PAT secret is present (this org's CI), pull the private
# 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
# When the PRO_SUBMODULE_PAT secret is present (this org's CI), pull the private pro/
# submodule so the pro-dependent suites run against the REAL package — a green run then
# actually exercises pro, not a stub. It also matters to the other gates: tsc sees
# __tests__/pro/* importing @offgrid/pro/* (TS2307 without it), and knip/depcruise need it
# or their reachability graph is incomplete and reports false orphans. Forks without the
# secret skip this: proExists=false and jest runs the open-core suite instead.
if: ${{ env.PRO_SUBMODULE_PAT != '' }}
env:
Expand All @@ -136,12 +52,12 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
# 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 26, pinned by the TEST gate and now shared by all of them. 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.
node-version: '26'
cache: 'npm'

Expand All @@ -151,24 +67,105 @@ jobs:
distribution: 'temurin'
java-version: '17'

- name: Install JS dependencies
# @offgrid/sync and @offgrid/rag are file: dependencies on the SIBLING shared monorepo
# ("file:../shared/packages/sync", "file:../shared/packages/rag"). They arrived with the sync
# work and nothing in CI ever provisioned them, so every gate failed on the same missing module
# wearing different hats: TS2307 in typecheck, 26 no-phantom-deps errors in the architecture
# gate, and 260 jest suites unable to resolve <workspace>/../shared/packages/sync/src/index.ts.
#
# actions/checkout refuses a path outside the workspace, so it lands inside and is moved up one
# level - which is exactly where the file: specifiers and jest's moduleNameMapper both point.
#
# Matching branch first, main as the fallback: a PR that changes the app and the shared package
# together has to be tested against the package it expects, not against main's copy.
- name: Check out the shared monorepo
if: ${{ env.PRO_SUBMODULE_PAT != '' }}
id: shared_branch
continue-on-error: true
uses: actions/checkout@v4
with:
repository: off-grid-ai/shared
token: ${{ env.PRO_SUBMODULE_PAT }}
path: _shared
ref: ${{ github.head_ref || github.ref_name }}
persist-credentials: false
- name: Fall back to shared main
if: ${{ env.PRO_SUBMODULE_PAT != '' && steps.shared_branch.outcome != 'success' }}
uses: actions/checkout@v4
with:
repository: off-grid-ai/shared
token: ${{ env.PRO_SUBMODULE_PAT }}
path: _shared
persist-credentials: false
- name: Put shared beside this checkout
if: ${{ env.PRO_SUBMODULE_PAT != '' }}
run: |
rm -rf ../shared
mv _shared ../shared
# Install at the WORKSPACE ROOT, not inside packages/sync. shared is an npm-workspaces
# monorepo: the lock file and the build tool (tsup) live at the root, and packages/sync
# declares neither. Installing inside the member failed `npm ci` (no lock file there), fell
# through to `npm install`, pulled the member's five runtime deps, and left the build to die
# on `sh: 1: tsup: not found` (exit 127) — which took every gate down with it.
#
# A LOCKED install, and no fallback. `npm ci || npm install` was here to survive a drifted lock, but
# that just moves the failure: an unlocked install resolves a different graph and the later gates run
# against dependencies nobody committed. The drift it papered over was real (five packages committed to
# shared without regenerating its lock) and is fixed at the source; if it recurs, this should stop.
npm --prefix ../shared ci
# Both packages this app consumes, because npm resolves each file: dep to its dist/ (jest
# reads src/, but tsc and metro read dist/). rag was never built at all before.
npm --prefix ../shared/packages/sync run build
npm --prefix ../shared/packages/rag run build

- name: Install dependencies
id: install
run: npm ci

- name: Install SwiftLint
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: brew install swiftlint

# ── Gates. Every one runs even when an earlier one failed, so a single red gate cannot hide
# the rest; the job still fails if any of them do.
- name: Lint
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: npm run lint

- name: Type check
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: npx tsc --noEmit

# 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.
- name: Dependency-cruiser (architecture gate)
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: npm run depcruise

- name: knip (dead-code gate)
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: npm run knip

- name: Run Jest tests
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: npx jest --coverage --forceExit --runInBand

- name: Install Android NDK
# Configuring the native modules (op-sqlite etc.) needs the pinned NDK; the macOS
# runner doesn't ship it, so the android test task failed at configure with
# "Failed to install ndk;...". Install the version android/build.gradle forces.
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: |
SDKMANAGER="$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager"
yes | "$SDKMANAGER" "ndk;27.1.12297006" >/dev/null

- name: Run Android tests
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: cd android && ./gradlew :app:testDebugUnitTest --rerun-tasks

- name: Run iOS tests
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: |
cd ios && xcodebuild test \
-workspace OffgridMobile.xcworkspace \
Expand All @@ -178,6 +175,7 @@ jobs:
2>&1 | (xcpretty 2>/dev/null || cat)

- name: Upload coverage to Codecov
if: always()
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
Expand Down Expand Up @@ -207,6 +205,6 @@ jobs:
# (see rules.md → On-device testing & verification). Docs-only / JS-only PRs don't need it.

# 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.
# no CI job. A CI scan would only add coverage import, which Codecov 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.
31 changes: 5 additions & 26 deletions App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import { useDebugLogsStore } from './src/stores/debugLogsStore';
import { initDebugLogFile, appendDebugLine } from './src/utils/debugLogFile';
import { loadProFeatures } from './src/bootstrap/loadProFeatures';
import { checkProStatus } from './src/services/proLicenseService';
import { hydrateDownloadStore } from './src/services/downloadHydration';
import { initActiveDownloadPersistence } from './src/services/activeDownloadPersistence';
import { restoreQueuedDownloads } from './src/services/restoreQueuedDownloads';
Expand Down Expand Up @@ -200,7 +199,7 @@
})().catch((error) => {
logger.error('[App] Download-state recovery failed:', error);
});
}, [setDownloadedModels, setDownloadedImageModels]);

Check warning on line 202 in App.tsx

View workflow job for this annotation

GitHub Actions / ci

React Hook useCallback has a missing dependency: 'reattachTextDownloadRecovery'. Either include it or remove the dependency array

const initializeApp = useCallback(async () => {
try {
Expand Down Expand Up @@ -261,32 +260,12 @@
// Initialize RAG database tables
ragService.ensureReady().catch((err) => logger.error('Failed to initialize RAG service on startup', err));

// Read the cached Pro entitlement before Pro features load. checkProStatus
// returns the Keychain cache immediately and fires a background Keygen
// revalidation so the next launch stays fresh.
//
// Pro is optional: a failure here (keychain locked, no network) must never
// abort app init or hang the splash screen, so it is isolated and only logs.
let isPro = false;
try {
isPro = await checkProStatus();
} catch (proError) {
logger.error('[App] Pro check failed, continuing without entitlement:', proError);
}

try {
// Load pro features — only activates if the keychain entitlement is set
// (or in dev, where loadProFeatures force-unlocks).
await loadProFeatures(isPro);

// Reconcile the persisted Pro flag with the actual entitlement on every
// boot. Setting it to the resolved value (not only ever true) means a
// cleared/expired license also flips it back to false — previously it
// only ever went true, so a stale persisted true stuck forever.
// DEV builds force-unlock for local testing, unless the Settings
// "Turn off Pro (DEV)" toggle is set. Never force-unlocks in release.
const devUnlock = __DEV__ && !useAppStore.getState().devProDisabled;
useAppStore.getState().setHasRegisteredPro(isPro || devUnlock);
// Register the private Pro entitlement provider before the first status
// read, then activate only the capabilities that entitlement permits.
// loadProFeatures separately projects cached credential access from a
// Debug developer unlock; Sync reconciliation owns device admission.
await loadProFeatures();
} catch (proError) {
logger.error('[App] Pro feature load failed, continuing without Pro:', proError);
}
Expand Down
Loading
Loading