Skip to content

feat: adaptive sleep advisory — memory-health-driven scheduling hints (Sprint F)#104

Merged
salishforge merged 1 commit into
mainfrom
feat/adaptive-sleep-scheduling
Apr 18, 2026
Merged

feat: adaptive sleep advisory — memory-health-driven scheduling hints (Sprint F)#104
salishforge merged 1 commit into
mainfrom
feat/adaptive-sleep-scheduling

Conversation

@salishforge
Copy link
Copy Markdown
Owner

Summary

ROADMAP Phase 2 adaptive sleep scheduling. Completes Phase 2.

MemForge is intentionally designed without a built-in scheduler (CLAUDE.md architectural rule). This PR instead exposes a read-only advisory that external orchestrators (cron, control plane, dashboards) can use to decide when to trigger a sleep cycle.

`MemoryManager.sleepAdvisory(agentId)` signals

Signal Source Semantics
`hot_backlog` `count(hot_tier)` Raw, un-embedded, non-searchable events; draining is the primary sleep purpose.
`contradiction_rate` `reflections.contradictions` last 30d Rising contradictions signal knowledge drift → Phase 2.5 conflict resolution needed.
`revision_debt` warm rows with `confidence < 0.4` Low-confidence rows need Phase 3 LLM revision.
`time_since_last_sleep` `agents.last_sleep_cycle` (v2.4) Periodic maintenance when any activity has occurred.
`stability` (inverse) `graduated / warm_count` High stability → frequent sleep is wasted LLM spend; clamps overall urgency down.

Overall urgency = max of the four active signals, then clamped to `'low'` if stability ceiling exceeded. `recommended = urgency >= 'medium'`.

Thresholds (all tunable via env)

  • `SLEEP_ADVISORY_HOT_BACKLOG_LOW` / `MEDIUM` / `HIGH` = 25 / 100 / 500
  • `SLEEP_ADVISORY_CONTRADICTION_HIGH` = 0.20 (0.10 medium, 0.05 low are inline)
  • `SLEEP_ADVISORY_REVISION_DEBT_MEDIUM` = 50 (20 low)
  • `SLEEP_ADVISORY_MAX_AGE_HOURS` = 24
  • `SLEEP_ADVISORY_STABILITY_CEILING` = 0.80

Public surfaces

  • `GET /memory/:id/sleep/advisory` (read-scoped)
  • `memforge_sleep_advisory` MCP tool
  • `sleepAdvisory()` on TS `MemForgeClient` + `ResilientMemForgeClient`
  • `sleep_advisory()` on Python equivalents
  • OpenAPI route documented with full response schema

No schema migration — all required columns already present.

Tests (`tests/sleep-advisory.test.ts`, 281 lines, 7 cases)

  1. No activity → `'none'`, recommended false.
  2. 600 hot rows → `hot_backlog` urgency `'high'`.
  3. 2/10 reflections w/ contradictions (ratio 0.20) → urgency `'high'`.
  4. 60 low-confidence warm rows → `revision_debt` urgency `'medium'`.
  5. `last_sleep_cycle` 25h ago + activity → urgency `'medium'`.
  6. 85/100 graduated + 600 hot → stability ceiling clamps to `'low'`.
  7. Custom `hotBacklogHigh=50` override → 60 hot rows → urgency `'high'`.

All use explicit timestamps, no sleeps.

Test plan

  • CI type-check / lint / build pass
  • CI test-integration (Node 20 + 22) runs the new suite
  • Existing sleep cycle + budgeting tests unaffected

Phase 2 scorecard (closed out after this merge)

ROADMAP Phase 2 adaptive sleep scheduling. MemForge is intentionally
designed without a built-in scheduler; this PR instead exposes a
read-only advisory that external orchestrators (cron, control plane,
dashboards) can use to decide when to trigger a sleep cycle.

MemoryManager.sleepAdvisory(agentId) evaluates five memory-health
signals and returns a structured recommendation:

- hot_backlog        — unconsolidated hot_tier rows per agent
- contradiction_rate — reflections with open contradictions / total
                       reflections in the last 30 days
- revision_debt      — warm rows below confidence threshold
- time_since_last_sleep — uses agents.last_sleep_cycle (v2.4)
- stability          — inverse signal; clamps urgency down when the
                       agent's warm tier is highly graduated
                       (stable knowledge base → frequent sleep is
                       wasted LLM spend)

Overall urgency = max of the four active signals, then clamped to
'low' if the stability ceiling is exceeded. recommended = urgency
is 'medium' or 'high'.

Thresholds are tunable via env vars (SLEEP_ADVISORY_HOT_BACKLOG_*,
SLEEP_ADVISORY_CONTRADICTION_HIGH, SLEEP_ADVISORY_REVISION_DEBT_MEDIUM,
SLEEP_ADVISORY_MAX_AGE_HOURS, SLEEP_ADVISORY_STABILITY_CEILING) with
sensible defaults.

Public surfaces:
- GET /memory/:id/sleep/advisory (read-scoped)
- memforge_sleep_advisory MCP tool
- TS SDK: sleepAdvisory() on MemForgeClient + ResilientMemForgeClient
- Python SDK: sleep_advisory() on both clients
- OpenAPI route with full response schema

No schema migration. Existing columns (agents.last_sleep_cycle,
warm_tier.confidence, warm_tier.graduated, reflections.contradictions)
cover all signals.

Tests (tests/sleep-advisory.test.ts, 281 lines, 7 cases):
1. No activity → urgency 'none', recommended false.
2. 600 hot rows → hot_backlog urgency 'high'.
3. 2/10 reflections w/ contradictions (ratio 0.20) → urgency 'high'.
4. 60 low-confidence warm rows → revision_debt urgency 'medium'.
5. last_sleep_cycle 25h ago + activity → urgency 'medium'.
6. 85/100 graduated + 600 hot → stability ceiling clamps to 'low'.
7. Custom hotBacklogHigh=50 override → 60 hot rows → urgency 'high'.

Completes Phase 2 of the ROADMAP: namespaces (C), cold tier search
(D), memory budgeting (E), adaptive scheduling (F).
@salishforge salishforge merged commit a5e77b7 into main Apr 18, 2026
12 checks passed
@salishforge salishforge deleted the feat/adaptive-sleep-scheduling branch April 18, 2026 04:09
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