Skip to content

feat(mental-models): cron-scheduled refresh via the maintenance loop#2377

Draft
nicoloboschi wants to merge 1 commit into
mainfrom
feat/mental-model-scheduled-refresh
Draft

feat(mental-models): cron-scheduled refresh via the maintenance loop#2377
nicoloboschi wants to merge 1 commit into
mainfrom
feat/mental-model-scheduled-refresh

Conversation

@nicoloboschi

Copy link
Copy Markdown
Collaborator

What

Adds a third, independent way to refresh a mental model: on a cron schedule, alongside the existing auto (trigger.refresh_after_consolidation) and manual (POST .../refresh) paths. Uses the existing background MaintenanceLoop ticker — no new worker process.

Why

Today a mental model only refreshes when consolidation runs or when someone calls the endpoint by hand. For models that should track a slowly-changing corpus, a fixed cadence ("rebuild this every night at 03:00") is the natural trigger.

How

  • Schema — new refresh_cron: str | None on MentalModelTrigger (UTC, standard 5-field cron), validated with croniter. Independent of refresh_after_consolidation.
  • Discovery routine — new PG-only public.mental_models_with_cron() (migration f4d1c2b3a5e6) returns every cron-scheduled model across all tenant schemas in one round-trip, excluding models that already have a refresh_mental_model op in flight. Mirrors banks_needing_consolidation and is EXCEPTION-guarded against schemas that vanish mid-scan (like c7e9f1a3b5d2).
  • Maintenance jobMaintenanceLoop._run_scheduled_mm_refresh evaluates cron due-ness in Python (cron arithmetic isn't expressible in plain SQL: a fire is due when the most recent cron boundary at/before now is later than last_refreshed_at), then refreshes a due model only when it is stale (new memories in its scope since the last refresh). A schedule that fires while nothing changed costs a cheap staleness query, not an LLM call.
  • Config — new static HINDSIGHT_API_MENTAL_MODEL_REFRESH_TICK_SECONDS (the check cadence; the schedule itself is per-model; 0 disables). Disabled by default in the test suite.

All three refresh paths converge on the existing refresh_mental_model async op — no new worker handler.

Tests

tests/test_mental_model_scheduled_refresh.py (deterministic, no LLM):

  • refresh_cron round-trips through create → get.
  • Routine returns cron models, excludes cron-less and in-flight ones.
  • Due + stale → refreshed; due but not stale → skipped; not-due → skipped even when stale.

Plumbing

  • Regenerated OpenAPI spec + Python/TS/Rust/Go clients.
  • Synced the control-plane trigger type in lib/api.ts.
  • Documented the new flag in configuration.md (follows the RECONCILE_INTERVAL_SECONDS precedent of documenting server-static maintenance knobs there rather than in .env.example).

Notes for reviewers

  • UTC cron evaluation (documented on the field).
  • A due-but-fresh model is re-checked each tick (cheap staleness query) until new memories arrive — last_refreshed_at only advances on an actual refresh.

Mental models could be refreshed two ways: automatically after
consolidation (trigger.refresh_after_consolidation) or manually via the
refresh endpoint. This adds a third, independent path — refresh on a cron
schedule — reusing the existing background MaintenanceLoop ticker.

- New trigger.refresh_cron field (UTC, 5-field cron) on MentalModelTrigger,
  validated with croniter.
- New PG-only discovery routine public.mental_models_with_cron() that
  returns cron-scheduled models across all tenant schemas in one round-trip
  and excludes models with an in-flight refresh (mirrors
  banks_needing_consolidation; EXCEPTION-guarded against vanished schemas).
- MaintenanceLoop._run_scheduled_mm_refresh: evaluates cron due-ness in
  Python (cron arithmetic isn't expressible in SQL), then refreshes a due
  model only when it is stale (new memories in scope since last refresh), so
  a scheduled tick never burns an LLM call on unchanged content.
- New static config HINDSIGHT_API_MENTAL_MODEL_REFRESH_TICK_SECONDS (the
  check cadence; 0 disables). Disabled by default in tests.

Regenerated OpenAPI + clients; synced the control-plane trigger type.
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.

1 participant