Skip to content

feat(mental-models): scheduled full refresh for delta models - #5674

Merged
nicoloboschi merged 3 commits into
mainfrom
feat/scheduled-full-refresh
Oct 16, 2026
Merged

nicoloboschi merged 3 commits into
mainfrom
feat/scheduled-full-refresh

Conversation

@nicoloboschi

@nicoloboschi nicoloboschi commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Scheduled full refresh

Closes #4340.

A delta mental model can now be rewritten from scratch — once on demand, or on its own schedule — without stopping being a delta model. Delta keeps unchanged sections byte-for-byte; over months of small edits a document drifts from what a fresh write would say. This gives users the "edit incrementally, rewrite weekly" pattern the issue asks for.

What changes

One-off full refresh — POST /mental-models/{id}/refresh takes an optional body {"mode": "full"}.

  • The stored trigger.mode is untouched; the next plain refresh is a delta again.
  • A full refresh that fails keeps the previous content (the old advice, clear + refresh, emptied the model first).
  • A full submit that folds into an already-queued plain refresh upgrades that one to full (_upgrade_queued_refresh_to_full), so "full" is never silently dropped by dedupe.

Scheduled full refresh — new trigger.full_refresh_cron (UTC, 5-field).

  • Independent of refresh_after_consolidation and refresh_cron — combine with either.
  • Runs inside the existing cron scan (MaintenanceLoop._run_scheduled_mm_refresh): same croniter due check, tenant routing, skip_if_in_flight, automatic=True (so the min-interval floor and the failure pause apply).
  • Comes due against a new mental_models.last_full_refreshed_at, not last_refreshed_at (every delta moves that one, so a weekly full refresh would never fire on a model that deltas daily).
  • Its gate is "a refresh ran since the last full one", not staleness: drift left by past edits is still there on a quiet bank, and with no refresh since the last full one there is nothing to fix.
  • When both crons are due, one refresh runs, in full mode.

Migration d4f7a2c9e1b5 — the column (PG + Oracle), a partial index on the new predicate, and mental_models_with_cron() recreated to also return full-refresh-only models (guarded by execute_unless_owned; the loop reads the new columns optionally so an operator-owned routine keeps working).

Surfaces

  • Control plane: "Scheduled full refresh" switch + cron presets (Sundays / Monthly) under Incremental in the trigger form; "Full refresh now" in both action menus with a token-cost confirm; "Last full refresh" in the detail view. 10 locales.
  • CLI: mental-model refresh --full, --trigger-full-refresh-cron.
  • Python/TS wrappers: mode="full" / { mode: "full" }. MCP trigger field. OpenAPI + all SDKs regenerated.
  • Docs: "Scheduled Full Refresh" section, full refresh on demand with tested examples (py/mjs/sh/go), the clear+refresh tip replaced.

Tests

  • api-slim: scheduler (due against own clock, skip with nothing to fix, not stale-gated, full wins over plain, routine discovery), fold upgrade, engine (full on a delta model keeps delta; empty scope still stamps), HTTP body.
  • System story test_39_full_refresh.py: a delta model's full refresh through the client — a dropped override would hit the unscripted delta-merge step and fail.
  • Control-plane form round-trip, CLI trigger update, wrapper mapping on both sides.
  • Verified by hand in the UI against a real LLM: delta edit → scheduler full refresh at the next cron mark → no further runs while nothing changed; "Full refresh now" from list and detail.

A delta mental model can now be rewritten from scratch without leaving delta mode:

- POST /mental-models/{id}/refresh takes an optional {"mode": "full"} for a one-off
  full refresh. The stored trigger is untouched, and a failed full refresh keeps the
  previous content (unlike clear + refresh). A full submit that folds into a queued
  plain refresh upgrades it.
- trigger.full_refresh_cron: an independent schedule, combinable with
  refresh_after_consolidation or refresh_cron. It runs in the existing cron scan,
  comes due against a new mental_models.last_full_refreshed_at, and is skipped when no
  refresh ran since the last full one. Min interval and failure pause apply as usual.
- Migration d4f7a2c9e1b5: the column, a partial index, and mental_models_with_cron()
  returning the full-refresh schedule (owned-routine safe).
- Control plane: scheduled full refresh row in the trigger form, 'Full refresh now'
  with a token-cost confirm, 'Last full refresh' in the detail view, 10 locales.
- CLI (refresh --full, --trigger-full-refresh-cron), Python/TS wrappers (mode),
  MCP trigger field, regenerated OpenAPI + SDKs, docs with tested examples.
- Tests: api-slim (scheduler, fold upgrade, engine, HTTP), system story 39,
  control-plane form, CLI, wrapper mapping on both sides.
… automatic triggers are listed

- http.py: min_refresh_interval_seconds, last_refresh_failed_at and the page trigger
  descriptions now list full_refresh_cron alongside the other automatic triggers.
- memory_engine: _REFRESH_AUTOMATIC_KEY comment no longer claims to be the only
  payload key a fold reconciles.
- docs: rate-limit, failure-pause and keep_trace paragraphs name full_refresh_cron.
- test: say why _queued_payload reads async_operations directly.
- regenerated OpenAPI, TS types and docs skill.
- migration d4f7a2c9e1b5 now follows b2429c65cc20 (main gained a migration; one head).
- regenerate bank-template-schema.json for trigger.full_refresh_cron (verify-generated-files).
- test_refresh_outcome_metadata: the refresh stub accepts the new mode kwarg the
  worker handler now passes.
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.

Feature Request: Configure Delta Refreshes and Scheduled Full Rebuilds for Mental Models in the Web UI

1 participant