Synapse v2.1.0 — "iOS redesign + close-out"¶
The flagship item deferred from 2.0.0 — the complete native iOS redesign — plus the backend/frontend cleanup batch that closes out the items 2.0.0's own audit flagged as follow-up. No schema migrations.
Added¶
- Track iOS 2.1 — complete native redesign (flagship): the iOS app is rebuilt from the ground up in native SwiftUI, targeting parity with the desktop experience.
- Fase A — design foundation (ADR-0088): a brand-aligned design system
(
ios/Synapse/DesignSystem/) ported token-for-token from the desktop theme (accent#2563eb, per-type jewel tones, light/dark parity, never pure black), a native 5-tab shell (Home · Wiki · Chat · Graph · More), and a documented graph-rendering spike (WKWebView sigma embed vs native Canvas/SpriteKit — both consume the same server-side FA2 coordinates, so I2 holds either way). - Fase B — core surfaces: Home, Wiki reading, Chat (streaming + citations), and Search rebuilt as real API-backed screens against the 2.0.0 backend (error envelope, SSE).
- Fase C — parity & GA: Graph, Review queue, Sources/Activity, and Settings brought to parity; the legacy pre-redesign theme retired; TestFlight distribution wired up.
- Retry-with-context (ADR-0085 §4): retrying a "did not converge" ingest run now injects the prior run's validation-failure diagnostics into the first regeneration attempt, instead of starting the retry blind.
- Coverage ratchet:
pytest-covnow enforces afail_under = 82floor in CI (measured baseline). - Focus-trap for
CascadeDeleteModal,CommandPalette, andResearchTopicDialog— Tab now cycles within the dialog and focus returns to the trigger on close, matching the pattern already used elsewhere. - Lazy Italian locale: the Italian locale is now a separate, dynamically-imported chunk
(
dist/assets/it-*.js, ~70kB) instead of a static bundle import — English-only sessions no longer download it.
Fixed¶
- Web wikilink navigation:
[[slug|Display Text]]piped wikilinks — the format the ingest LLM actually emits — carry the page slug, not its title, indata-wikilink. The click handler only ever checked a title-keyed index, so these links always failed with "page not found" even when the target existed. It now falls back toGET /pages/by-slug/{slug}before giving up. OllamaProvidersilent empty responses on reasoning models:complete()didn't account for thethinkingfield some reasoning-capable models (e.g. qwen3.5) emit alongsidecontent— a response could come back empty if the token budget was consumed by reasoning output before any content was generated.- CI E2E artifact upload was silently broken: the Playwright run step passed
--reporter=liston the CLI, which replaces rather than merges withplaywright.config.ts's configured[["list"], ["html", ...]]reporter array — so the HTML report never ran andfrontend/playwright-report/(the only path previously uploaded) was empty on every prior flake investigation. Fixed by dropping the override and also uploadingfrontend/test-results/, where the actual per-test trace files live. - CSP dark-theme KaTeX flake, resolved: after an initial pattern fix, the dark-theme test
still flaked under CI-only timing variance. Root cause:
playwright.config.tscarries a suite-wide 30s default per-test timeout, independent of the 10s default per-assertion timeout — so widening an individualexpect(...)call's timeout alone did nothing once it exceeded the outer ceiling. Fixed with an explicittest.setTimeout(60_000)in the test body, raising the ceiling itself.
Changed¶
GraphViewer.tsxsplit further, 1588→1075 lines;HomeDashboard.tsxsplit for the first time, 3119→812 lines — both extractions are move-only, with the sigma-mount and in-place graphology-diff effects (I2/I3) kept intact.- Backend layering: two remaining lazy
app.mainimports inapp/projects.pyreplaced withruntime_statebridge accessors. - Seven frontend API clients migrated to the shared
ApiError/checkResponsepattern; several store/view pairs (ingest, research, import-schedule) moved from timer-driven polling to fetch-on-SSE-event with poll as a fallback; run lists and search views gained Skeleton/EmptyState treatment.
Known follow-ups¶
- iOS wikilink tap-to-navigate (draft PR #114, unmerged): SwiftUI's
Text(AttributedString)does not reliably dispatch link taps to.environment(\.openURL, ...)in this app's view hierarchy — root-caused via live Simulator instrumentation, not an app-logic bug. Fix (LinkableText.swift, aUITextView-backedUIViewRepresentablebridge) is implemented but not yet confirmed working live. Needs a manual tap-to-navigate check before merge. - ADR-0083 parity harness: not re-run against the 1.9.4 review-create block-loop migration —
requires
/Applications/LLM Wiki.app(not installed on this machine) or an approved alternative gold fixture. Pending for three releases now.