-
Notifications
You must be signed in to change notification settings - Fork 16
Sync feedback: Desktop chat, model, and preview fixes #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
809bf5a
69f5c13
3046ac9
5e165a8
f2b47c1
501118f
84c7e8b
610e633
bbe6e41
051302b
0b3f418
f04edbb
511fd2d
33bb2a0
79bb06f
6ec7b84
8e05797
d37f0f3
bbe3cc7
ae5883a
282523d
075917a
b960f6b
e1cbac5
76b370a
834bdf5
20b7187
d93b750
5299706
1438e3f
86ecb01
996cd58
52eb21f
184a49b
046387c
98030a8
3e33a29
4302f54
e22a158
9664273
83ca547
9773d51
94cbfd2
05412e6
dd7be6c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| [submodule "pro"] | ||
| path = pro | ||
| url = https://github.com/off-grid-ai/desktop-pro.git |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,20 @@ how to reproduce, and the fix direction. Close with evidence; never hide. | |
|
|
||
| ## OPEN | ||
|
|
||
| ### SYN-004 (P1) - Late-pair full graph is not verified between the real Desktop and Mobile apps | ||
|
|
||
| **Evidence (2026-08-13):** the production send paths now backfill state records, generated images, | ||
| message attachments, and knowledge documents when a device pairs after the data exists. Shared tests | ||
| prove byte-bounded anti-entropy. Desktop tests use the real service, SQLite, and temporary files. | ||
| Mobile tests use two real sync engines and prove exact file bytes and durable controls. These tests do | ||
| not start both actual apps and verify final receiver materialization and UI in one journey. | ||
|
|
||
| **Evidence required to close:** create a project, settings, chat text, enhanced prompt, reasoning, | ||
| completed tool, generated image, attachment, and knowledge document on device A before device B is | ||
| paired. Pair the real apps through the production connect path. Verify every record, relationship, | ||
| and byte on B, restart B, and verify that the same graph returns without duplicate transfers or | ||
| detached files. Run Desktop-to-Mobile and Mobile-to-Desktop on physical macOS, iOS, and Android. | ||
|
|
||
| ### DEF-001 (P1) - Replay capture control reports a state that is not factual | ||
|
|
||
| **Evidence (2026-08-08):** macOS Settings reported `Screen access: denied` and `Permission | ||
|
|
@@ -1181,3 +1195,77 @@ Independently: `0 failed` next to a logged `request.failed` is its own bug - the | |
| reaching Activity even for the file that DID have a row. | ||
|
|
||
| --- | ||
|
|
||
| ## MTP is a mobile feature, and it should be a `@offgrid/models` one | ||
|
|
||
| **Status:** open. Filed 2026-08-12. Deliberately NOT in the current PR - it is a package extraction, | ||
| not a fix, and folding it in would bloat a release branch that is about sync feedback. | ||
|
|
||
| **The gap.** MTP support exists on mobile and does not exist on desktop. Both apps consume the same | ||
| model layer, so a capability that lives in one app's screens is a rule with one home and two | ||
| audiences - the shape this repo keeps finding at the root of its bugs. | ||
|
|
||
| **Why it is a shared-package problem, not a desktop one.** Copying the mobile implementation across | ||
| would make it a rule with TWO homes, which is worse than having it once. The model layer is the owner: | ||
| whether a model needs MTP, which artefacts it implies, and how a device advertises support are all | ||
| properties of the MODEL, and every surface should be asking `@offgrid/models` rather than each app | ||
| deciding for itself. Desktop then gets it by consuming the package, and so does any future surface. | ||
|
|
||
| **Fix shape.** | ||
|
|
||
| 1. Move the MTP capability rule into `shared/packages/models` - one definition of what MTP is and which | ||
| models require it, keyed off the catalog entry, with no app-specific branching inside it. | ||
| 2. Mobile stops deciding and starts asking. Its current behaviour is the reference for what the rule | ||
| must produce, so it is the regression check that the extraction changed nothing. | ||
| 3. Desktop consumes the same rule. No `if (platform)` in either app: a platform that cannot serve MTP | ||
| reports that through the same interface, it does not get a special case at the call site. | ||
|
|
||
| **Watch for:** the mobile implementation almost certainly carries assumptions that are really about the | ||
| phone runtime rather than about MTP. Those belong on the platform adapter, not in the shared rule - and | ||
| the tell is any code in the extracted package that names a platform. | ||
|
|
||
| --- | ||
|
|
||
| ## Every non-image attachment syncs to desktop as "text" and previews blank | ||
|
|
||
| **Verdict:** fix-the-guard — the kinds exist, the write path never uses them. | ||
|
|
||
| Reproduced on hardware 16 Aug 2026: a message sent from iPhone with three attachments (a camera | ||
| photo, a library screenshot and `mobile.pdf`). On desktop the PDF arrives as a chip reading | ||
|
|
||
| ``` | ||
| mobile.pdf text | ||
| ``` | ||
|
|
||
| and opening it shows an empty viewer with only Download and Close - nothing renders. Android shows | ||
| the same message correctly, because it has its own renderer. | ||
|
|
||
| The attachment type already names the kinds: | ||
|
|
||
| ```ts | ||
| kind: 'text' | 'pdf' | 'docx' | 'image' | 'audio' | 'video' | ||
| ``` | ||
|
|
||
| but the write path in `src/renderer/src/components/MemoryChat.tsx` (~2940) only ever chooses between | ||
| two of them: | ||
|
|
||
| ```ts | ||
| if (isImage) { kind: 'image', code: a.path } | ||
| else if (a.text) { kind: 'text', code: a.text } | ||
| ``` | ||
|
Comment on lines
+1236
to
+1255
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Resolve the Markdown lint findings.
🧰 Tools🪛 markdownlint-cli2 (0.23.2)[warning] 1236-1236: Fenced code blocks should have a language specified (MD040, fenced-code-language) [warning] 1236-1236: Code block style (MD046, code-block-style) [warning] 1245-1245: Code block style (MD046, code-block-style) [warning] 1252-1252: Code block style (MD046, code-block-style) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| A PDF has no image path, so it falls through to `text` and renders `a.text` - which for a binary | ||
| document is empty. Hence a labelled-but-blank preview rather than an error. | ||
|
|
||
| **This is not PDF-specific.** Any non-image attachment takes the same branch. It was predicted from | ||
| the v0.0.103 diff for VOICE NOTES - "Mac classifies any non-image as kind: 'text', so a .wav arrives | ||
| as a paperclip chip labelled text, and clicking it opens a blank viewer" - and never reproduced until | ||
| now. A PDF and a voice note are the same bug. | ||
|
|
||
| **Why it matters:** the attachment did sync. The bytes are there (Download works). What is broken is | ||
| the classification and therefore the rendering, so the user sees a file they cannot open and | ||
| concludes the transfer failed. | ||
|
|
||
| **Fix:** classify by MIME/extension into the kinds the union already declares, carry `path` for | ||
| binary kinds rather than `text`, and render a PDF/document viewer for them. `src/main/mime.ts` | ||
| already exists and is the obvious source of truth. | ||
|
Comment on lines
+1249
to
+1271
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
rg -n -C 8 'kind:|a\.path|a\.text|describeAttachment|non-image' \
src/renderer/src/components/MemoryChat.tsx src/shared \
--glob '*.ts' --glob '*.tsx'Repository: off-grid-ai/OGAD Length of output: 44835 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- candidate files ---'
fd -i 'mime|attachment|viewer|document' src shared
printf '%s\n' '--- MemoryChat outline ---'
ast-grep outline src/renderer/src/components/MemoryChat.tsx --view compact
printf '%s\n' '--- shared attachment symbols ---'
rg -n -C 12 'attachmentKindFor|describeAttachment|type Attachment|interface Attachment|DocumentPane|setViewer|renderer' \
src/renderer/src src/shared src/main \
--glob '*.ts' --glob '*.tsx'Repository: off-grid-ai/OGAD Length of output: 616 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- candidate files ---'
fd -i 'mime|attachment|viewer|document' src
printf '%s\n' '--- MemoryChat outline ---'
ast-grep outline src/renderer/src/components/MemoryChat.tsx --view digest
printf '%s\n' '--- attachment helpers and viewers ---'
rg -n -C 16 'attachmentKindFor|describeAttachment|type Attachment|interface Attachment|DocumentPane|setViewer|renderer' \
src/renderer/src src/shared src/main \
--glob '*.ts' --glob '*.tsx'
printf '%s\n' '--- MIME implementation ---'
cat -n src/main/mime.tsRepository: off-grid-ai/OGAD Length of output: 50373 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- helper definitions ---'
rg -l 'attachmentKindFor|describeAttachment' src --glob '*.ts' --glob '*.tsx' |
while IFS= read -r file; do
printf '\n### %s\n' "$file"
rg -n -C 20 'attachmentKindFor|describeAttachment' "$file"
done
printf '%s\n' '--- relevant MemoryChat sections ---'
sed -n '770,860p;1780,1905p;4005,4070p;4135,4170p;5108,5148p' \
src/renderer/src/components/MemoryChat.tsx
printf '%s\n' '--- attachment processing API and call sites ---'
rg -n -C 18 'process.*attachment|processAttachment|extract.*text|read.*file|attachments.*process|kind:.*pdf|kind:.*docx|kind:.*audio|kind:.*video' \
src/main src/renderer/src src/shared \
--glob '*.ts' --glob '*.tsx'
printf '%s\n' '--- MIME implementation ---'
cat -n src/main/mime.tsRepository: off-grid-ai/OGAD Length of output: 50373 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- shared attachment implementation ---'
git ls-files | rg '(^|/)(attachment|mime|files-classify|sync).*\.tsx?$|package\.json$' |
head -200
rg -n -C 24 'function (attachmentKindFor|describeAttachment)|const (attachmentKindFor|describeAttachment)|export .*attachmentKindFor|export .*describeAttachment' . \
--glob '*.ts' --glob '*.tsx' \
--glob '!src/renderer/src/components/MemoryChat.tsx' \
--glob '!**/__tests__/**'
printf '%s\n' '--- viewer state and rendering ---'
rg -n -C 18 'const \[viewer|setViewer|viewer\.renderer|viewer\.kind|DocumentPane|AudioPane|TextPane|renderer ===' \
src/renderer/src/components/MemoryChat.tsx
printf '%s\n' '--- persisted attachment shapes ---'
rg -n -C 12 'type StoredAttachment|attachmentsOf|attMeta|attachments:' \
src/renderer/src/components/MemoryChat.tsx src/main src/shared \
--glob '*.ts' --glob '*.tsx'Repository: off-grid-ai/OGAD Length of output: 639 🏁 Script executed: #!/usr/bin/env bash
set -u
printf '%s\n' '--- package and workspace locations ---'
cat package.json
fd -H -t d 'sync|core' . --max-depth 4
git ls-files | rg '(^|/)(sync|core|shared)/|describeAttachment|attachmentKindFor'
printf '%s\n' '--- viewer state and rendering ---'
rg -n -C 18 'const \[viewer|setViewer|viewer\.renderer|viewer\.kind|DocumentPane|AudioPane|TextPane|renderer ===' \
src/renderer/src/components/MemoryChat.tsx || true
printf '%s\n' '--- persisted attachment shapes ---'
rg -n -C 12 'type StoredAttachment|attachmentsOf|attMeta|attachments:' \
src/renderer/src/components/MemoryChat.tsx src/main src/shared \
--glob '*.ts' --glob '*.tsx' || trueRepository: off-grid-ai/OGAD Length of output: 47686 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- processUpload return-shape verifier ---'
python3 - <<'PY'
from pathlib import Path
s = Path("src/main/files.ts").read_text()
for kind in ("image", "audio", "video", "pdf", "docx", "text"):
rows = [line.strip() for line in s.splitlines() if f"kind: '{kind}'" in line]
print(f"{kind}:")
for row in rows:
print(f" {row}")
PY
printf '%s\n' '--- file transport and sync hooks ---'
rg -n -C 20 'files:data-url|files:process|sync-shared-file|IncomingSharedFile|path.*uploads|uploads.*path|shared file|file.*sync' \
src/main/ipc.ts src/main/sync-shared-file.ts src/main/sync-mutation.ts \
src/renderer/src/lib/sync-hooks.ts src/renderer/src/components/MemoryChat.tsx \
--glob '*.ts' --glob '*.tsx'
printf '%s\n' '--- relevant source sections ---'
sed -n '1,180p' src/main/sync-shared-file.ts
sed -n '1980,2035p' src/main/ipc.ts
sed -n '1,220p' src/renderer/src/lib/sync-hooks.tsRepository: off-grid-ai/OGAD Length of output: 35572 Keep this backlog item open for binary attachments.
Persist and transfer binary files, preserve their paths, and add the missing video renderer before marking this item resolved. 🧰 Tools🪛 markdownlint-cli2 (0.23.2)[warning] 1252-1252: Code block style (MD046, code-block-style) 🤖 Prompt for AI Agents |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Replace the em dash with ASCII punctuation.
The Markdown guideline requires plain ASCII punctuation. Replace the em dash after
fix-the-guardwith a colon or an ASCII hyphen.As per coding guidelines: documentation must use plain ASCII punctuation without em dashes or curly quotes.
🤖 Prompt for AI Agents
Source: Coding guidelines