Skip to content

fix(routines): hot-enable on LLM key save — resolve chat agent live per run#483

Merged
Weegy merged 2 commits into
mainfrom
fix/473-routines-hot-enable
Jul 11, 2026
Merged

fix(routines): hot-enable on LLM key save — resolve chat agent live per run#483
Weegy merged 2 commits into
mainfrom
fix/473-routines-hot-enable

Conversation

@SecurID

@SecurID SecurID commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #473

Problem

Routines was the only feature that couldn't hot-enable when the LLM key is saved: the kernel wired it once at boot behind if (graphPool && orchestrator), capturing the orchestrator by reference. On a fresh install (key entered post-boot via the Setup Wizard) routines stayed dark until a manual restart — the boot log literally said "then restart to enable routines". This was the last manual step in the first-run flow (follow-up from #471).

Approach — chat's live-resolution pattern

  • initRoutines / RoutineRunner take a getOrchestrator live resolver (closure over serviceRegistry.get('chatAgent')?.raw, mirroring the chat routes' getChatAgentBundle) instead of a captured instance. The boot gate is now graphPool-only; stores, router, manage_routine tool, cron registration, and catch-up all initialize at boot regardless of key state.
  • Zero hot-enable trigger code: the moment the key save reactivates the orchestrator plugin and republishes chatAgent@1, the next cron fire just works. A key rotation (reactivate publishes a new bundle) is picked up the same way; each run pins the instance current at fire time.
  • Keyless fires record an error run naming the missing key — consistent with One-click deploy (Render + Fly.io) — and the four first-run fixes it surfaced #471's "fresh installs say what's missing". The chat-agent check runs before the sender lookup because pre-key the channel plugins (requires: chatAgent@^1) are dark too, and the old order would report a misleading no proactive sender. The pause/delete re-read now runs first, so a raced fire on a just-paused row stays a silent skip.
  • POST /:id/trigger returns a synchronous 503 routines.chat_unavailable instead of a fire-and-forget 202 whose run is guaranteed to fail; the web-ui maps the code to a catalog message (EN/DE) instead of rendering the raw error.
  • Removed the stale "restart to enable routines" log line and updated the kernel design comment that documented this exact gap.

Tests

7 new (all green, full middleware suite 4018 pass / 0 fail; web-ui typecheck, vitest, i18n:check pass):

  • keyless fire → error run with the structured message, asserting the chat check precedes the sender lookup
  • hot-enable flip: fire fails keyless, resolver starts returning an agent, next fire succeeds on the same scheduler entry — the Routines don't hot reload on startup #473 behavior pin
  • key rotation: consecutive fires use the instance current at fire time
  • trigger route: 503 while unavailable, 404 precedence, 503→202 flip on the same router instance (router-level analog of chatSessionsRouterGraceful.test.ts)

Notes

  • Fully independent of One-click deploy (Render + Fly.io) — and the four first-run fixes it surfaced #471 (zero file overlap; verified git diff main...feat/one-click-deploy is empty for every touched file).
  • Accepted edges (by design): a high-frequency routine accumulates one error run per fire while keyless; a fire in the seconds-wide window between chatAgent publish and channel-sender re-registration records one UnknownChannelError run and self-heals on the next fire.

🤖 Generated with Claude Code

SecurID and others added 2 commits July 10, 2026 16:01
…er run

Routines was the last feature needing a manual restart on first-run: the
kernel wired it at boot behind `if (graphPool && orchestrator)`, capturing
the orchestrator by reference, so a key saved via the Setup Wizard never
reached it (the boot log literally said "then restart to enable routines").

Adopt chat's live-resolution pattern (issue #473):

- initRoutines/RoutineRunner take `getOrchestrator` (live resolver over
  the ServiceRegistry) instead of a captured `orchestrator`; the boot gate
  is graphPool-only. The next cron fire after key save just works — no
  trigger code, no re-init; a key rotation swaps to the new bundle too.
- Keyless fires record an `error` run naming the missing key, checked
  before the sender lookup — pre-key the channel senders are dark too and
  would otherwise mask the cause with 'no proactive sender'. The pause
  re-read now runs first so a raced fire on a paused row stays a skip.
- POST /:id/trigger returns a synchronous 503 `routines.chat_unavailable`
  instead of a 202 whose background run is guaranteed to fail; the web-ui
  maps the code to a catalog message (EN/DE) naming the fix.

7 new tests: keyless error run + check ordering, hot-enable flip, key
rotation across instances, trigger 503→202 flip (router-level analog of
chatSessionsRouterGraceful).

Fixes #473

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Weegy Weegy enabled auto-merge (squash) July 11, 2026 04:55
@Weegy Weegy merged commit 106eb22 into main Jul 11, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Routines don't hot reload on startup

2 participants