June 13, 2026

Vue foundation, whole-instance snapshots, per-source memory profiles

The biggest push is TKT-952, the Vue frontend migration: a pnpm workspace skeleton, a shared `@chalie/shared` package with SCSS theme port preserving `[data-the

The biggest push is TKT-952, the Vue frontend migration: a pnpm workspace skeleton, a shared @chalie/shared package with SCSS theme port preserving [data-theme], TS-strict base + ESLint flat config + Prettier, and a placeholder barrel so the exports-map resolves. Two Vue apps scaffold on top — @chalie/interface (App + smoke HomeView with theme/WebSocket/backend-ready status, base /next/) and @chalie/brain (placeholder, base /brain-next/). Supporting pieces land as typed modules: Pinia stores/composables/base UI, a typed WebSocketService with the TKT-950 liveness watchdog, a typed ApiClient with AuthError/HttpError (upload now also throws HttpError on any non-2xx so 413s surface cleanly), and a PlatformAdapter web impl that guards navigator.mediaDevices for non-secure contexts. The Flask API serves the Vue interface build at /next/ (additive coexistence with legacy static, /next/ 301 canonicalization, SPA history fallback), the modern Sass compiler API is enabled to clear Dart Sass’s legacy-js-api deprecation, and a real Playwright E2E harness covers boot/theme/api/ws against a live Chalie.

TKT-949 ships the whole-instance Import/Export Time-Machine, replacing Brain’s old backup section. A new snapshot_service.py WAL-folds chalie.db / mcp_tools.sqlite / skills.sqlite into consistent single-file clones, bundles key material, document store, user skills, session secret and VERSION, and writes a per-artifact manifest (kind + arcname + sha256); a password yields a real AES-256 zip via pyzipper, otherwise plain deflate. Restore is two-phase (stage → apply at boot) so a half-finished swap can never corrupt a live instance, with rollback + quarantine on any failure and a schema-downgrade guard reusing a new SchemaConvergenceService.column_set. The HTTP layer streams the zip on POST /api/snapshot/export and stages uploads on POST /api/snapshot/import (lifting the global upload cap for that route only); the frontend exposes export/import behind a destructive-restore confirmation. The companion launcher fix (TKT-949) catches the intended exit-42 restart code with || _EXIT=$? so the restart loop survives set -euo pipefail after an import-triggered restore.

TKT-926 replaces the binary channel != 'user' memory gate with per-source profiles (services/source_profiles.py): user = full pipeline, dmn = HEAVY (own episodes + facts), external-agent:* = first-class, delegate:*/skills_building/scheduled = muted. Episode extraction is gated by profile.extract_episodes, fact extraction runs per-channel with fact_extraction:<channel> provenance, consolidation generalises to every episode-producing channel, and the geo/pattern windows + their MAX(id) cursors honour the user-activity allowlist so dmn/delegate rows no longer masquerade as user activity. The decay janitor protects the full HEAVY allowlist, episode recall is channel-agnostic so dmn/external-agent memories surface in user turns, and the scheduled path persists the instruction alongside the output via a two-stage delegate on a daemon thread so the poll transaction commits the claim cleanly. Gate: 1466 passed / 0 failed / 137 deselected.

TKT-950 closes the half-open WebSocket gap: behind a reverse proxy, an idle socket can be dropped at the TCP layer without a close frame, so the existing exponential-backoff reconnect never runs. A client-side liveness watchdog stamps _lastInboundAt on every inbound frame and polls every 30s, forcing a reconnect when no frame has arrived for >90s (1.5x the server ping); the stale socket’s handlers are detached first so its delayed onclose can’t double-reconnect. The tab-refocus handler now calls ensureAlive() instead of trusting isConnected, and a separate fix wires an onConnect callback from the socket’s onopen so the connection store reflects the open event immediately rather than waiting for the first ~60s keep-alive ping.

Brain UI gets TKT-587 alignment polish: sidebar-brand pinned to --topbar-h (was overhanging the 56px topbar by ~14.6px), panel-header h2s flex-centred via :has(svg) so inline icons align with heading text while text-only headers stay in normal block flow, back-button chevrons rotated to point left with the missing .back-btn class added, personality slider labels lifted out of the pole/slider row, errors-list timestamps top-aligned, memory-controls filter-tabs bottom margin dropped inside the row, skill-content preview switched to the defined --bg-surface-2, and stray undefined --text-1 / --bg-1 vars replaced. The provider picker grid now spans full container width (cap dropped, tile min-width 150→180, gap 10→12), and the reactive provider-setup wizard replaces the platform-tabs form with a curated-grid → host auto-fill → progressive API-key reveal → live model fetch flow that reuses the existing POST /providers/list-models. A curated in-process catalog replaces the 4651-line models.dev JSON dump, the dead catalog-id-as-platform branch in list_models() is removed, and docs (02) describe the wizard + GET /providers/catalog.

TKT-557 redacts raw GPS from the chat-prompt telemetry block: location joins _TELEMETRY_HIDDEN_GROUPS so only the resolved location_name scalar surfaces, and the 16-AMBIENT-AWARENESS privacy note is scoped to the chat prompt while naming the background geo-pattern pass as the one model-facing consumer of coordinates. The old Brain backup blueprint and its export/import/GitHub-backup services are removed in favour of the new snapshot service, with security fixes folded in along the way (zip-slip path traversal guard in import, secure_filename in upload, DOM-XSS fix replacing innerHTML href injection with textContent, prompt() replaced by a proper modal for the GitHub passphrase, and GitHub Releases swapped for the Contents API so backups are committed to the repo). A brittle-test cleanup drops the two registry-metadata tests for save_graph/save_pattern (no unique behavioural coverage; dispatch already exercises them end-to-end), removing a now-dead AbilityRegistry import each test was the sole consumer of.

Docs catch up to behaviour: 04-ARCHITECTURE replaces the user-only episode-gate claim with the per-source memory-profiles section (allowlist model, per-source table, cross-channel recall, write-time provenance) and updates the Consolidate/Decay/Geo/Pattern background-cognition steps to honour the user-activity allowlist; 13-MESSAGE-FLOW rewrites the scheduled path as the two-stage flow and scopes episode encoding to episode-producing channels.

  • TKT-952 Vue migration lands: pnpm workspace, @chalie/shared with SCSS theme port, TS-strict + ESLint flat + Prettier, two Vue apps (@chalie/interface at /next/, @chalie/brain placeholder), typed ApiClient/WebSocketService/Pinia stores/PlatformAdapter, modern Sass compiler API, and a Playwright E2E harness against a real Chalie

  • TKT-949 Import/Export Time-Machine: snapshot_service WAL-folds chalie.db / mcp_tools.sqlite / skills.sqlite into per-artifact-manifested zip (AES-256 with password), two-phase stage-then-apply restore with rollback + quarantine and a schema-downgrade guard reusing SchemaConvergenceService.column_set

  • TKT-949 launcher fix: capture exit-42 with || _EXIT=$? so the restart loop survives set -euo pipefail after an import-triggered restore

  • TKT-926 per-source memory profiles replace the binary channel != 'user' gate (user full, dmn HEAVY, external-agent:* first-class, delegate:*/skills_building/scheduled muted) with per-channel episode/fact/geo/pattern/decay handling and a two-stage scheduled flow on a daemon thread; 1466/0/137 gate

  • TKT-950 WS liveness watchdog stamps _lastInboundAt on every inbound frame and force-reconnects after >90s of silence (1.5x server ping), with handlers detached to prevent double-reconnect; onConnect callback now reflects socket open immediately

  • TKT-587 Brain UI alignment polish (sidebar height, panel-header icon alignment, back-button chevrons, sliders, timestamps, control spacing) plus full-width provider picker grid and reactive provider-setup wizard with curated in-process catalog replacing the 4651-line models.dev dump