Skip to content
Draft
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
85e03c0
feat(chat): add cohort capability and capture archive contract
Git-on-my-level Jul 15, 2026
ba8b3f5
feat(chat): gate structured blocks in the local runtime
Git-on-my-level Jul 15, 2026
ad6820e
fix(chat): preserve admitted chat-first capability
Git-on-my-level Jul 15, 2026
3857a6b
feat(chat): search the local journal on demand
Git-on-my-level Jul 15, 2026
b0176b4
feat(chat): add cohort-gated proactive intent foundation
Git-on-my-level Jul 15, 2026
f782048
feat(desktop): add the cohort-gated chat-first shell
Git-on-my-level Jul 15, 2026
d24a4d8
feat(desktop): render chat blocks and capture archive
Git-on-my-level Jul 15, 2026
c358a71
feat(chat): add question actions and task workspace
Git-on-my-level Jul 15, 2026
50b336f
feat(chat): materialize proactive intents locally
Git-on-my-level Jul 15, 2026
f5887b4
feat(chat): add canonical goals and cold start
Git-on-my-level Jul 15, 2026
cc4ec99
feat(chat): add cohort-safe analytics and fixtures
Git-on-my-level Jul 15, 2026
5aea6ad
fix(chat): repair desktop chat-first integration
Git-on-my-level Jul 15, 2026
48ac20d
fix(chat): align navigation and materialization actors
Git-on-my-level Jul 15, 2026
23bfe65
fix(chat): complete journal result forwarding
Git-on-my-level Jul 15, 2026
f4cae81
fix(chat): preserve journal revision and deferral recovery
Git-on-my-level Jul 15, 2026
fa7a658
feat(chat): harden cohort-safe chat-first e2e
Git-on-my-level Jul 15, 2026
456226a
fix(chat): prepare local fixture with write batch
Git-on-my-level Jul 16, 2026
4570f1c
fix(memory): unify canonical cohort entitlement
Git-on-my-level Jul 17, 2026
f494a93
fix(chat): repair chat-first integration build
Git-on-my-level Jul 17, 2026
819ce85
fix(chat): enforce whitelist-derived chat-first projection
Git-on-my-level Jul 17, 2026
a5dabdb
fix(backend): clear chat-first typecheck gate
Git-on-my-level Jul 17, 2026
8a5c5d5
chore(api): regenerate desktop OpenAPI DTOs
Git-on-my-level Jul 17, 2026
1515ea7
docs(api): refresh app client OpenAPI contract
Git-on-my-level Jul 17, 2026
e328754
chore(api): regenerate TypeScript OpenAPI clients
Git-on-my-level Jul 17, 2026
afcd75d
feat: complete chat-first rich entity widgets
Git-on-my-level Jul 21, 2026
73fdbf4
chore: ratchet chat-first source baselines
Git-on-my-level Jul 21, 2026
2a1c653
chore: declare canonical goals route policy
Git-on-my-level Jul 21, 2026
3c6247a
fix: report interrupted pre-push checks
Git-on-my-level Jul 21, 2026
2974c93
fix: avoid force unwrap in capture routing test
Git-on-my-level Jul 21, 2026
0c68d94
refactor: extract chat-first runtime fixture
Git-on-my-level Jul 21, 2026
6a09314
chore: ratchet extracted chat runtime
Git-on-my-level Jul 21, 2026
f009541
fix: resolve CI failures across backend tests, Swift test, and PR pre…
Git-on-my-level Jul 21, 2026
df37b1b
fix: resolve chat bubble rebase conflict
Git-on-my-level Jul 21, 2026
614545f
chore: refresh rebased line-count baselines
Git-on-my-level Jul 21, 2026
aa17ebd
fix: align backend tests with canonical cohort gate
Git-on-my-level Jul 21, 2026
68f0e92
fix(desktop): encode chat-first deferral discriminator
Git-on-my-level Jul 21, 2026
9d5e4c3
style(desktop): apply pinned Swift formatting
Git-on-my-level Jul 21, 2026
de2de4d
fix(desktop): align static-contract tests with floating-bar typing re…
Git-on-my-level Jul 21, 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
32 changes: 32 additions & 0 deletions .github/scripts/preflight_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,9 @@ def run_owned(
started = time.monotonic()
started_wall = time.time()
phase = "starting"
last_phase = phase
child: subprocess.Popen[str] | None = None
received_signal: int | None = None

def write_status() -> None:
atomic_json(
Expand All @@ -166,13 +168,24 @@ def write_status() -> None:
"pid": os.getpid(),
"fingerprint": wanted_fingerprint,
"phase": phase,
"last_phase": last_phase,
"received_signal": received_signal,
"elapsed_seconds": round(time.monotonic() - started, 1),
"log": str(log_path),
"started_at_epoch": started_wall,
},
)

def forward_signal(signum: int, _frame: object) -> None:
nonlocal received_signal
received_signal = signum
print(
f"FAIL: preflight runner received signal {signal.Signals(signum).name} "
f"during phase={phase}; forwarding it to the child.",
file=sys.stderr,
flush=True,
)
write_status()
if child is not None and child.poll() is None:
try:
os.killpg(child.pid, signum)
Expand Down Expand Up @@ -210,10 +223,27 @@ def forward_signal(signum: int, _frame: object) -> None:
log.flush()
if line.startswith("==> "):
phase = line[4:].strip()
last_phase = phase
write_status()
exit_code = child.wait()
phase = "passed" if exit_code == 0 else "failed"
write_status()
if exit_code != 0:
if exit_code < 0:
child_failure = f"child terminated by {signal.Signals(-exit_code).name}"
else:
child_failure = f"child exited with status {exit_code}"
signal_note = (
f"; runner received {signal.Signals(received_signal).name}"
if received_signal is not None
else ""
)
print(
f"FAIL: preflight {child_failure} during phase={last_phase}{signal_note}; "
f"inspect {log_path}",
file=sys.stderr,
flush=True,
)
atomic_json(
result_path,
{
Expand All @@ -222,6 +252,8 @@ def forward_signal(signum: int, _frame: object) -> None:
"elapsed_seconds": round(time.monotonic() - started, 1),
"finished_at_epoch": time.time(),
"log": str(log_path),
"last_phase": last_phase,
"received_signal": received_signal,
},
)
return exit_code
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"files": {
"backend/database/conversations.py": 1580,
"backend/database/conversations.py": 1601,
"backend/database/users.py": 1943
},
"raise_justifications": {
"backend/database/conversations.py": "Public shared-chat uses the existing conversation storage format for bounded, safe transcript decoding; extraction would duplicate crypto/storage invariants."
"backend/database/conversations.py": "Chat-first capture archive filtering belongs beside the shared conversation query and cursor invariants; extraction would duplicate its ordered query contract."
},
"threshold": 1500
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"files": {
"desktop/macos/Desktop/Sources/Chat/AgentBridge.swift": 2209,
"desktop/macos/Desktop/Sources/Chat/AgentRuntimeProcess.swift": 4314
"desktop/macos/Desktop/Sources/Chat/AgentBridge.swift": 2189,
"desktop/macos/Desktop/Sources/Chat/AgentRuntimeProcess.swift": 4338
},
"raise_justifications": {
"desktop/macos/Desktop/Sources/Chat/AgentBridge.swift": "per-turn reasoningEffort threaded through both query variants",
"desktop/macos/Desktop/Sources/Chat/AgentRuntimeProcess.swift": "reasoningEffort added to the query wire message"
"desktop/macos/Desktop/Sources/Chat/AgentBridge.swift": "Named non-production fault bundle supplies an inert hermetic model token so fault injection reaches the configured remote 5xx backend without Firebase auth.",
"desktop/macos/Desktop/Sources/Chat/AgentRuntimeProcess.swift": "Chat-first evidence attachment stays with the owner-bound runtime bridge so run, attempt, and capability fences remain atomic."
},
"threshold": 1500
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"files": {
"desktop/macos/Desktop/Sources/FloatingControlBar/AgentPill.swift": 2375,
"desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarView.swift": 2853,
"desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarWindow.swift": 4842,
"desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarView.swift": 2856,
"desktop/macos/Desktop/Sources/FloatingControlBar/FloatingControlBarWindow.swift": 4843,
"desktop/macos/Desktop/Sources/FloatingControlBar/PushToTalkManager.swift": 2423,
"desktop/macos/Desktop/Sources/FloatingControlBar/RealtimeHubSession.swift": 1621
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"files": {
"desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift": 1926,
"desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift": 2055,
"desktop/macos/Desktop/Sources/MainWindow/Pages/AppsPage.swift": 3643,
"desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift": 4543,
"desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift": 4577,
"desktop/macos/Desktop/Sources/MainWindow/Pages/MemoriesPage.swift": 3298,
"desktop/macos/Desktop/Sources/MainWindow/Pages/TasksPage.swift": 5518,
"desktop/macos/Desktop/Sources/MainWindow/SidebarView.swift": 1570
},
"raise_justifications": {
"desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift": "Keeps all chat tool groups compact and collapsed while streamed steps increment, with an explicit expansion policy, fixed collapsed height, and regression coverage.",
"desktop/macos/Desktop/Sources/MainWindow/Components/ChatBubble.swift": "Chat-first blocks and persisted local evidence render at the existing chat-message resource boundary instead of creating a parallel transcript renderer.",
"desktop/macos/Desktop/Sources/MainWindow/Pages/AppsPage.swift": "Rebase reconciliation applies the pinned swift-format to the main-branch app-detail safety fixes; runtime behavior is unchanged.",
"desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift": "Home redesign: chat-as-home surface plus the 2nd-brain knows-list hub replace the wordmark/ribbon/WMN-card layout; a component split is tracked follow-up.",
"desktop/macos/Desktop/Sources/MainWindow/Pages/DashboardPage.swift": "Chat-first entry state remains colocated with the dashboard's canonical route selection and lifecycle synchronization.",
"desktop/macos/Desktop/Sources/MainWindow/Pages/MemoriesPage.swift": "Bridge memory search now awaits the active initial or pagination projection before refreshing, preventing an immediate post-navigation marker search from using stale lifecycle capability state.",
"desktop/macos/Desktop/Sources/MainWindow/Pages/TasksPage.swift": "The Load-more-tasks action reads displayTasks.last at execution time via loadMoreTapped() instead of force-unwrapping (an emptied list no longer crashes); merged with main binding first-load/loading/error/retry state to the selected To Do or Done view.",
"desktop/macos/Desktop/Sources/MainWindow/SidebarView.swift": "Strict concurrency imports annotate legacy AppKit image usage without changing runtime behavior."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"files": {
"desktop/macos/Desktop/Sources/Onboarding/OnboardingChatView.swift": 2197,
"desktop/macos/Desktop/Sources/Onboarding/OnboardingChatView.swift": 2199,
"desktop/macos/Desktop/Sources/Onboarding/OnboardingPagedIntroCoordinator.swift": 1714
},
"raise_justifications": {
"desktop/macos/Desktop/Sources/Onboarding/OnboardingChatView.swift": "Pinned swift-format normalizes line layout without changing this source's runtime behavior.",
"desktop/macos/Desktop/Sources/Onboarding/OnboardingChatView.swift": "Chat-first capability wiring remains with onboarding's shared chat surface so rollout state cannot diverge across entry points.",
"desktop/macos/Desktop/Sources/Onboarding/OnboardingPagedIntroCoordinator.swift": "Onboarding adopts the async-arriving auth name, cancels the local file import task in deinit, and carries the sharpened goal/task prompts on top of the terminal-scan-state helper."
},
"threshold": 1500
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"files": {
"desktop/macos/Desktop/Sources/ProactiveAssistants/Assistants/TaskExtraction/TaskAssistant.swift": 1742,
"desktop/macos/Desktop/Sources/ProactiveAssistants/ProactiveAssistantsPlugin.swift": 1641
"desktop/macos/Desktop/Sources/ProactiveAssistants/ProactiveAssistantsPlugin.swift": 1614
},
"raise_justifications": {
"desktop/macos/Desktop/Sources/ProactiveAssistants/Assistants/TaskExtraction/TaskAssistant.swift": "Task candidate context now carries only backend action-item IDs, while local SQLite/staged evidence remains id-less so the model cannot target it for a backend mutation.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"files": {
"desktop/macos/Desktop/Sources/Providers/ChatProvider.swift": 6214,
"desktop/macos/Desktop/Sources/Providers/ChatToolExecutor.swift": 2861
"desktop/macos/Desktop/Sources/Providers/ChatProvider.swift": 6723,
"desktop/macos/Desktop/Sources/Providers/ChatToolExecutor.swift": 3080
},
"raise_justifications": {
"desktop/macos/Desktop/Sources/Providers/ChatProvider.swift": "ChatTurnOwner reasoning-effort lane derivation",
"desktop/macos/Desktop/Sources/Providers/ChatToolExecutor.swift": "Already-granted permissions no longer reopen System Settings: screen-recording and full-disk-access requests gate their Settings/drag-card opens behind the granted result."
"desktop/macos/Desktop/Sources/Providers/ChatProvider.swift": "Chat-first uses the existing provider-owned generation, journal, and owner-fence lifecycle; extracting its canonical context cleanup would split that boundary.",
"desktop/macos/Desktop/Sources/Providers/ChatToolExecutor.swift": "Canonical goal reads and owner-scoped Rewind evidence remain beside the existing authorized tool dispatch and sharing-policy boundary."
},
"threshold": 1500
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
"files": {
"desktop/macos/Desktop/Sources/AuthService.swift": 3294,
"desktop/macos/Desktop/Sources/CloudConnectorFormAutomation.swift": 1678,
"desktop/macos/Desktop/Sources/DesktopAutomationBridge.swift": 4239,
"desktop/macos/Desktop/Sources/DesktopAutomationBridge.swift": 4261,
"desktop/macos/Desktop/Sources/MemoryExportService.swift": 1578,
"desktop/macos/Desktop/Sources/OmiApp.swift": 1645
},
"raise_justifications": {
"desktop/macos/Desktop/Sources/AuthService.swift": "Apple first-auth name capture signals observers (authNameDidUpdate) and persists the name to Firebase so it survives reinstalls.",
"desktop/macos/Desktop/Sources/DesktopAutomationBridge.swift": "Reach-error card gains a debug bridge action so the actionable failure surface is verifiable in-process.",
"desktop/macos/Desktop/Sources/DesktopAutomationBridge.swift": "Chat-first runtime automation remains in the owning desktop bridge so named-bundle UI verification keeps its lifecycle boundary.",
"desktop/macos/Desktop/Sources/MemoryExportService.swift": "Pinned swift-format normalizes line layout without changing this source's runtime behavior.",
"desktop/macos/Desktop/Sources/OmiApp.swift": "shared openMainAppWindow helper for menu bar + Open Omi shortcut"
"desktop/macos/Desktop/Sources/OmiApp.swift": "Side-by-side Omi Beta: legacy Omi Computer.app cleanup gains a stable-identity guard so the beta app can never terminate or delete a stable install."
},
"threshold": 1500
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"files": {
"desktop/macos/Desktop/Sources/Stores/TasksStore.swift": 3117
"desktop/macos/Desktop/Sources/Stores/TasksStore.swift": 3273
},
"raise_justifications": {
"desktop/macos/Desktop/Sources/Stores/TasksStore.swift": "Unsynced local_ task mutations skip backend calls that can only 404; auto-refresh clamps only the API page to the 500 cap while the local reload keeps every loaded row (no >500 truncation); undo-restore of a local-only task re-inserts one unsynced local row instead of fabricating a synced backend id + duplicate; retry-sync carries completed state; and undo-restore re-creates with the full field set."
"desktop/macos/Desktop/Sources/Stores/TasksStore.swift": "Unsynced local task mutations and Chat-first task-card rollback remain with the authoritative task persistence and owner-fence transitions."
},
"threshold": 1500
}
18 changes: 18 additions & 0 deletions .github/scripts/test_pr_preflight.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,24 @@ def test_different_input_is_rejected_while_active(self) -> None:
first.stdout.close()
self.assertEqual(first.wait(), 0)

def test_failure_reports_exit_status_and_last_phase(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
temp = Path(tmp)
command = [sys.executable, "-c", "print('==> deliberate-failure', flush=True); raise SystemExit(17)"]
process = self.run_runner(temp, command)
assert process.stdin is not None
process.stdin.close()
output = process.stdout.read() if process.stdout else ""
self.assertEqual(process.wait(), 17, output)
if process.stdout:
process.stdout.close()
self.assertIn("child exited with status 17", output)
self.assertIn("phase=deliberate-failure", output)
result = json.loads((temp / "test" / "result.json").read_text())
self.assertEqual(result["exit_code"], 17)
self.assertEqual(result["last_phase"], "deliberate-failure")
self.assertIsNone(result["received_signal"])


if __name__ == "__main__":
unittest.main()
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ PYTHON ?= $(shell bash -c 'source "$$(git rev-parse --show-toplevel)/scripts/dev
export PYTHON
DESKTOP_USER ?= alice
DESKTOP_APP_NAME ?=
CHAT_FIRST_E2E_ACTION ?= prepare
CHAT_FIRST_E2E_CASE ?= enabled
CHAT_FIRST_E2E_SECONDS ?= 86400

.PHONY: setup setup-main setup-hooks preflight runtime-image-source-closure runtime-image-smoke dev-check dev-up dev-status dev-summary dev-reset dev-down dev-logs dev dev-desktop dev-init dev-verify list-memory-scenarios seed-memory-scenario reset-memory-scenario desktop-run-local run-canonical-promotion
.PHONY: setup setup-main setup-hooks preflight runtime-image-source-closure runtime-image-smoke dev-check dev-up dev-status dev-summary dev-reset dev-down dev-logs dev dev-desktop dev-init dev-verify list-memory-scenarios seed-memory-scenario reset-memory-scenario desktop-run-local chat-first-e2e-fixture run-canonical-promotion

setup: setup-main setup-hooks
@echo "Worktree setup complete."
Expand Down Expand Up @@ -79,5 +82,8 @@ desktop-run-local:
PYTHON="$$PYTHON" bash scripts/dev-harness/desktop-run-local.sh "$(DESKTOP_USER)"; \
fi

chat-first-e2e-fixture:
PYTHON="$(PYTHON)" bash scripts/dev-harness/chat-first-e2e-fixture.sh "$(CHAT_FIRST_E2E_ACTION)" "$(CHAT_FIRST_E2E_CASE)" "$(CHAT_FIRST_E2E_SECONDS)"

run-canonical-promotion:
PYTHON="$$PYTHON" PYTHONPATH="scripts/dev-harness:backend$(if $(PYTHONPATH),:$(PYTHONPATH),)" "$$PYTHON" scripts/dev-harness/run-canonical-promotion.py "$(PROMOTION_USER)"
6 changes: 4 additions & 2 deletions backend/.env.template
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Stage templates (local harness / cloud dev): see backend/.env.{local-dev,offline,dev,prod}.template
# Copy one to backend/.env.<stage>, symlink backend/.env, set OMI_ENV_STAGE. See .envrc.example.

# Memory rollout (canonical MEMORY_* env)
# Canonical-memory maintenance controls. They never select a user, task
# system, or Chat-first shell: those are selected only by the code-owned
# CANONICAL_MEMORY_USERS whitelist in config/canonical_memory_cohort.py.
# Keep these values aligned with maintenance-job readiness requirements.
MEMORY_MODE=off
MEMORY_ENABLED_USERS=
# Canonical cohort: edit CANONICAL_MEMORY_USERS in utils/memory/memory_system.py (not env).
MEMORY_CANONICAL_PROMOTION_CRON_ENABLED=false
MEMORY_CANONICAL_PROMOTION_CRON_INTERVAL_HOURS=1
MEMORY_V3_GET_ENABLED=false
Expand Down
2 changes: 1 addition & 1 deletion backend/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ Serving STT provider/surface policy and canonical model order are owned exclusiv
- **nllb-translation** (`nllb_translation/`) — GPU translation service. Called by backend when `HOSTED_TRANSLATION_API_URL` is set and NLLB is selected.
- **backend-sync** (`main.py`, same image as backend) — Cloud Run admission service for `/v2/sync-local-files`. The server classifies whole batches: recordings no more than six hours old enter `sync-jobs` (fresh), while older or untrusted batches enter `sync-backfill` and the scale-to-zero **backend-sync-backfill** worker. Fresh keeps its bounded inline fallback; backfill never falls into fresh/inline capacity. Backfill defaults to one in-flight job per UID, four processed speech hours per UID/day, 555 processed speech hours globally/day, a 30-day lookback, and four queue workers. Live fair-use reads only `realtime + sync_fresh`; `sync_backfill` is separately metered. A 45-day Firestore content ledger protects transcription and usage side effects across job expiry and re-upload. Audio playback merges (`/v1/sync/audio/*`) follow the same pattern via queue `audio-merge` building 30-day MP3 artifacts under `playback/` (`AUDIO_MERGE_DISPATCH_MODE`) — per-part files plus one dense per-conversation `conversation.mp3` whose spans manifest + audio_files fingerprint are stamped on the conversation doc (`conversation_audio`); a fingerprint mismatch after late chunks re-enqueues the build. In production, account deletion requires `ACCOUNT_DELETION_DISPATCH_MODE=cloud_tasks` and complete Cloud Tasks bindings to enqueue opaque job IDs to queue `account-deletion`, which posts `/v1/users/account-deletion-wipes/run`; startup rejects inline or incomplete configuration, reconciliation only re-dispatches tasks so the OIDC handler is the sole wipe executor, and the post-deploy queue-drain window accepts the former sync OIDC audience only for legacy UID payloads. API success is returned only after the deletion marker is persisted and the wipe task is durably enqueued.
- **notifications-job** (`modal/job.py`) — Cron job, reads Firestore/Redis, sends push notifications and runs X connector sync. It has no canonical maintenance flags or Typesense secrets; its deploy workflow removes only those retired bindings and preserves unrelated notification/X-sync env.
- **memory-maintenance-job** (`modal/memory_maintenance_job.py`) — Cloud Run Job and sole host for canonical ST→LT maintenance (TTL → consolidation → promotion). Manual deploy via `.github/workflows/gcp_memory_maintenance_job.yml`; auto-dev on push to `main` via `gcp_memory_maintenance_job_auto_dev.yml`. Enablement is a multi-var contract (`MEMORY_MODE`, `MEMORY_ENABLED_USERS`, cron/fast-track/consolidation flags) enforced by `backend/scripts/validate-backend-runtime-env.py`; prod stays `MEMORY_MODE=off` until Gate 3.
- **memory-maintenance-job** (`modal/memory_maintenance_job.py`) — Cloud Run Job and sole host for canonical ST→LT maintenance (TTL → consolidation → promotion). Manual deploy via `.github/workflows/gcp_memory_maintenance_job.yml`; auto-dev on push to `main` via `gcp_memory_maintenance_job_auto_dev.yml`. Its multi-var runtime contract (`MEMORY_MODE`, `MEMORY_ENABLED_USERS`, cron/fast-track/consolidation flags) controls maintenance readiness only; user selection for canonical memory, tasks, and Chat-first is exclusively `CANONICAL_MEMORY_USERS` in `config/canonical_memory_cohort.py`.
- **monitoring** (`backend/charts/monitoring/`) — Prometheus, Grafana, Loki, Alloy, alerts, and HPA metric adapters for backend services.
- **agent-vm-reaper** (`backend/charts/agent-vm-reaper/`) — CronJob that deletes stale `omi-agent-*` GCE VMs left by desktop agent sandboxes.
- **backend-secrets** (`backend/charts/backend-secrets/`) — ExternalSecret and SecretStore resources that sync backend runtime secrets into GKE namespaces.
Expand Down
Loading