Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
809bf5a
fix(preview): an ogcapture URL is parsed, not sliced, so Windows find…
alichherawalla Aug 12, 2026
69f5c13
test(preview): the drive letter, the POSIX path, and the escaping in …
alichherawalla Aug 12, 2026
3046ac9
chore(llama): one owner for the engine version, moved to b10369
alichherawalla Aug 12, 2026
5e165a8
fix(sidebar): a row's hover colour comes from the text token, not the…
alichherawalla Aug 12, 2026
f2b47c1
refactor(chat): one decoder and one content rule for an image-bearing…
alichherawalla Aug 12, 2026
501118f
fix(chat): a turn's attachments are read from the turn, not from the …
alichherawalla Aug 12, 2026
84c7e8b
feat(models): Nemotron 3.5 Lightning and Muse Glimmer, tagged new + c…
alichherawalla Aug 12, 2026
610e633
chore(llama): package.json owns the engine version, and both platform…
alichherawalla Aug 12, 2026
bbe6e41
docs(gaps): MTP belongs in @offgrid/models, not in one app's screens
alichherawalla Aug 12, 2026
051302b
fix(chat): the thinking controls come from the loaded template, not f…
alichherawalla Aug 12, 2026
0b3f418
fix(models): one size rule, so a card stops printing the same file in…
alichherawalla Aug 12, 2026
f04edbb
fix(imagegen): a generated image previews on Windows
alichherawalla Aug 13, 2026
511fd2d
fix(files): every attachment failed on Windows, and sharp took the blame
alichherawalla Aug 13, 2026
33bb2a0
fix(chat): a streamed reply is stored under the id its peers already …
alichherawalla Aug 13, 2026
79bb06f
feat(chat): a file still on its way draws a loader on its own turn
alichherawalla Aug 13, 2026
6ec7b84
feat(chat): publish and render generation phases
alichherawalla Aug 13, 2026
8e05797
fix(chat): preview synced image attachments
alichherawalla Aug 13, 2026
d37f0f3
fix(chat): present synced generation results
alichherawalla Aug 14, 2026
bbe3cc7
fix(chat): tighten consecutive tool rows
alichherawalla Aug 14, 2026
ae5883a
fix(chat): align live image state and markdown rendering
alichherawalla Aug 14, 2026
282523d
refactor(models): consume shared catalog package
alichherawalla Aug 14, 2026
075917a
fix(desktop): resolve static analysis findings
alichherawalla Aug 14, 2026
b960f6b
refactor(chat): split message presentation sections
alichherawalla Aug 14, 2026
e1cbac5
test(desktop): verify runtime boundaries through behavior
alichherawalla Aug 14, 2026
76b370a
fix(coverage): keep DB report on service code
alichherawalla Aug 14, 2026
834bdf5
fix(imagegen): preserve the active stream on busy requests
alichherawalla Aug 14, 2026
20b7187
fix(llm): apply the loaded thinking dialect everywhere
alichherawalla Aug 14, 2026
d93b750
fix(chat): close synced lifecycle review gaps
alichherawalla Aug 14, 2026
5299706
fix(capture): reject malformed capture URLs
alichherawalla Aug 14, 2026
1438e3f
docs(windows): state runtime revision parity
alichherawalla Aug 14, 2026
86ecb01
fix(coverage): keep renderer code out of DB reports
alichherawalla Aug 14, 2026
996cd58
fix(chat): one rhythm for reasoning and tool rows in a turn
alichherawalla Aug 15, 2026
52eb21f
test(build): teach the whisper fake the load command the script gates on
alichherawalla Aug 15, 2026
184a49b
test(packaging): name the pro renderer by what it delivers
alichherawalla Aug 15, 2026
046387c
fix(chat): an attached image is a thumbnail, not a full-width preview
alichherawalla Aug 16, 2026
98030a8
Revert "fix(chat): an attached image is a thumbnail, not a full-width…
alichherawalla Aug 16, 2026
3e33a29
fix(chat): an attached picture fills its bubble and crops, like WhatsApp
alichherawalla Aug 16, 2026
4302f54
fix(chat): only a filling picture gets a filling click target
alichherawalla Aug 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,9 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: '24' # node:sqlite (used by integration tests) is available unflagged
# @offgrid/sync is a file: dependency on the SIBLING shared monorepo ("file:../shared/packages/sync").
# It arrived with the sync work and CI never provisioned it, so every job that installs would fail on the
# same missing module. actions/checkout refuses a path outside the workspace, so it lands inside and is
# moved up one level - exactly where the file: specifier points.
# Shared packages are file: dependencies on the SIBLING shared monorepo. CI must provision it before
# installing Desktop. actions/checkout refuses a path outside the workspace, so it lands inside and is
# moved up one level - exactly where the file: specifiers point.
#
# Matching branch first, main as the fallback, the same shape as the pro checkout above: a PR that changes
# the app and the shared package together must be tested against the package it expects.
Expand All @@ -93,7 +92,7 @@ jobs:
- name: Put shared beside this checkout
run: |
if [ ! -d _shared ]; then
echo "::error::off-grid-ai/shared was not checked out - @offgrid/sync cannot resolve. Check CI_CROSS_REPO_TOKEN."
echo "::error::off-grid-ai/shared was not checked out - shared packages cannot resolve. Check CI_CROSS_REPO_TOKEN."
exit 1
fi
rm -rf ../shared
Expand All @@ -107,6 +106,7 @@ jobs:
# later gates run against dependencies nobody committed. A drifted lock should stop the build.
npm --prefix ../shared ci
npm --prefix ../shared/packages/sync run build
npm --prefix ../shared/packages/models run build
- run: npm ci
# Hard gates: types + the full test suite.
- name: Typecheck (core)
Expand Down
12 changes: 7 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ jobs:
- name: Build llama-server (pinned deployment target)
run: |
command -v cmake >/dev/null || brew install cmake
MACOS_DEPLOYMENT_TARGET=13.0 LLAMA_REF=b9838 bash scripts/build-llama.sh
MACOS_DEPLOYMENT_TARGET=13.0 bash scripts/build-llama.sh
otool -l resources/bin/llama/llama-server | awk '/LC_BUILD_VERSION/{f=1} f&&/minos/{print "[ci] bundled engine minos="$2; exit}'
# Rebuild the one-shot Whisper CLI instead of trusting the committed LFS
# payload. The production script disables OpenMP/BLAS auto-discovery and
Expand Down Expand Up @@ -189,7 +189,7 @@ jobs:
with:
node-version: '20'
cache: 'npm'
# `@offgrid/sync` is a file: dependency on the shared monorepo, so it must sit BESIDE this
# Shared packages are file: dependencies on the shared monorepo, so it must sit BESIDE this
# checkout before `npm ci` runs - an install cannot resolve a path that does not exist, and the
# typecheck inside `npm run build` then fails on every import of it. `ci.yml` has done this since
# the sync work landed; this workflow did not, which is why no release has built since 2026-07-30.
Expand Down Expand Up @@ -218,7 +218,7 @@ jobs:
- name: Put shared beside this checkout
run: |
if [ ! -d _shared ]; then
echo "::error::off-grid-ai/shared was not checked out - @offgrid/sync cannot resolve. Check CI_CROSS_REPO_TOKEN."
echo "::error::off-grid-ai/shared was not checked out - shared packages cannot resolve. Check CI_CROSS_REPO_TOKEN."
exit 1
fi
rm -rf ../shared
Expand All @@ -228,6 +228,7 @@ jobs:
# drifted lock stops the release instead of resolving a graph nobody committed.
npm --prefix ../shared ci
npm --prefix ../shared/packages/sync run build
npm --prefix ../shared/packages/models run build
- name: Install dependencies
run: npm ci
# Stamp the resolved version into package.json so electron-builder picks the
Expand Down Expand Up @@ -426,7 +427,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: '3.12'
# `@offgrid/sync` is a file: dependency on the shared monorepo, so it must sit BESIDE this
# Shared packages are file: dependencies on the shared monorepo, so it must sit BESIDE this
# checkout before `npm ci` runs - an install cannot resolve a path that does not exist, and the
# typecheck inside `npm run build` then fails on every import of it. `ci.yml` has done this since
# the sync work landed; this workflow did not, which is why no release has built since 2026-07-30.
Expand Down Expand Up @@ -458,7 +459,7 @@ jobs:
shell: bash
run: |
if [ ! -d _shared ]; then
echo "::error::off-grid-ai/shared was not checked out - @offgrid/sync cannot resolve. Check CI_CROSS_REPO_TOKEN."
echo "::error::off-grid-ai/shared was not checked out - shared packages cannot resolve. Check CI_CROSS_REPO_TOKEN."
exit 1
fi
rm -rf ../shared
Expand All @@ -468,6 +469,7 @@ jobs:
# drifted lock stops the release instead of resolving a graph nobody committed.
npm --prefix ../shared ci
npm --prefix ../shared/packages/sync run build
npm --prefix ../shared/packages/models run build
- name: Install dependencies
run: npm ci
- name: Fetch Windows native binaries (llama/whisper/sd/ffmpeg)
Expand Down
42 changes: 42 additions & 0 deletions docs/GAPS_BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1181,3 +1195,31 @@ 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.
2 changes: 1 addition & 1 deletion docs/WINDOWS_SUPPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ at the bottom.
| -------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Windows CI build | 🟢 | `.github/workflows/windows-build.yml` (`windows-2022`) builds + packages a branch artifact; `release.yml`'s `build-win` job publishes the installer + updater feed to the release. Verified by installing a build on real Windows hardware. |
| Native-module compile (node-gyp) | 🟢 | Pinned toolchain: `windows-2022` (VS 2022) + Python 3.12. `windows-latest`/VS 2026 + Python 3.13 break node-gyp 11 — documented in the workflow. Covers `better-sqlite3-multiple-ciphers`, `node-llama-cpp`, `sharp`. |
| Windows runtime binaries fetch | 🟢 | `scripts/fetch-win-binaries.ps1` pulls win64 `llama-server` / `whisper-cli` / `sd-cli` / `ffmpeg` (+ DLLs) from upstream GitHub releases at build time. **`llama-server` is pinned to `b9838`** (byte-for-byte parity with the macOS engine); `whisper-cli` / `sd-cli` / `ffmpeg` resolve dynamically from their latest upstream releases. Repo LFS binaries are macOS-only and skipped (`lfs: false`). Fails loud if `llama-server.exe` is missing. |
| Windows runtime binaries fetch | 🟢 | `scripts/fetch-win-binaries.ps1` pulls win64 `llama-server` / `whisper-cli` / `sd-cli` / `ffmpeg` (+ DLLs) from upstream GitHub releases at build time. **`llama-server` is pinned by `package.json` `offgrid.llamaRef`**. macOS compiles it; Windows gets the same-revision prebuilt, so bytes can differ. `whisper-cli` / `sd-cli` / `ffmpeg` resolve dynamically from their latest upstream releases. Repo LFS binaries are macOS-only and skipped (`lfs: false`). Fails loud if `llama-server.exe` is missing. |
| NSIS installer | 🟢 | `electron-builder.yml` → `win.executableName`, `nsis` block (desktop shortcut, uninstall name). Untested end-to-end. |
| Code signing | 🟡 | Optional via `WIN_CSC_LINK` / `WIN_CSC_KEY_PASSWORD` secrets; **unset → unsigned build → SmartScreen will warn** on install. No cert configured yet. |
| Auto-update | 🟢 | `electron-updater` is cross-platform (`src/main/updater.ts`); `release.yml`'s `build-win` job publishes `latest.yml` (stable) / `beta.yml` (nightly) to the release, so Windows installs self-update like macOS. |
Expand Down
2 changes: 1 addition & 1 deletion e2e/devices-sync.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ test.describe('Devices surface — pro tier', () => {
await page.getByRole('button', { name: 'Rescan network' }).click()
await expect(page.getByRole('button', { name: 'Rescan network' })).toBeEnabled()
await expect(page.getByText('Synthetic Android').first()).toBeVisible()
await expect(page.getByText('Scanning LAN and Nearby routes.')).toHaveCount(0)
await expect(page.getByText('Scanning WiFi and Nearby routes.')).toHaveCount(0)
await expect(page.getByRole('alert')).toHaveCount(0)

const timestamp = new Date().toISOString()
Expand Down
Loading