You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
_memory_integrity_check in src/forge_loop/control/doctor.py reports the
memory store's breakdown (decisions/active, rejected_paths, episodes, skills/procedural) and returns PASS whenever the store opens cleanly. It has no visibility into the prunable-memory backlog — the active episodic items
that are not load-bearing and accumulate monotonically (one per merged/failed
issue) until they drown the load-bearing decisions in boot context (the failure
named by parent epic #426).
Concrete example: a loop that has run 300 issues holds ~600 non-load-bearing
episodes alongside ~30 decisions. doctor today prints episodes=600 and returns PASS — the maestro resuming after context loss
gets a green light and no signal that compaction is overdue. The prunable
backlog must become an explicit, inspectable health signal so compaction can be
gated on something visible.
Dependency
is_load_bearing_memory(item) is the pure predicate added by sibling #427
(same epic #426). "Prunable" = active items where is_load_bearing_memory(item)
is False. This ticket consumes that predicate; it does not define it.
A named module-level threshold constant (e.g. PRUNABLE_MEMORY_WARN_THRESHOLD)
is declared once in doctor.py (no bare literal at the call site), matching the
existing named-constant convention (PASS/FAIL/WARN, *_REMEDIATION).
A named compaction remediation string constant (e.g. COMPACT_REMEDIATION) is
declared alongside the other *_REMEDIATION constants and suggests compacting
prunable memory.
_memory_integrity_check's detail line additionally reports the prunable count
(active items where is_load_bearing_memory(item) is False) alongside the
existing decisions/active / rejected_paths / episodes / skills/procedural
breakdown. The existing fields are preserved unchanged (do not rename or drop).
When prunable count > threshold: probe status is WARN, detail names
the prunable count, and remediation is the compaction remediation (non-None).
When prunable count <= threshold: behaviour is unchanged — status is PASS and remediation is None (only the detail string gains the new field).
The absent-store (WARN, not-applicable), corrupt-store (FAIL) and
unreadable-store (FAIL) branches are untouched and still behave as before.
The probe remains read-only: no writes to .forge, consistent with the
module-level invariant in doctor.py's docstring.
Test matrix
Add to tests/test_doctor_control_plane.py (the memory_integrity unit group
around test_reports_counts_on_populated_store, lines ~267-296):
Unit — backlog reported (PASS): seed a store with a few load-bearing items
and a sub-threshold number of prunable episodes; assert status == PASS, remediation is None, and detail contains the prunable-count field.
Unit — backlog crosses threshold (WARN): seed prunable episodes above
the threshold; assert status == WARN, detail names the prunable count and
mentions compaction, and remediation is the compaction constant (non-None).
Unit — boundary: exactly-at-threshold count stays PASS (proves the strict > comparison, not >=); threshold+1 flips to WARN.
Unit — existing fields preserved: the WARN detail still contains decisions/active=, rejected_paths=, episodes=, skills/procedural=.
Adversarial / sad-path: corrupt memory.db still returns FAIL (the new
prunable logic must run only on the clean-open path and never mask the
corrupt-store FAIL branch). Reuse the existing test_fails_cleanly_on_corrupt_db
pattern.
Integration:forge-loop doctor --json over a seeded above-threshold store
emits the memory_integrity object with status == "warn" and a non-null
remediation, and the human table renders the prunable count + remediation.
Part of epic: "Bound curated memory with a load-bearing-preserving compaction arc"
Extend the existing _memory_integrity_check in control/doctor.py so its detail
line additionally reports the prunable-memory count (active items where
is_load_bearing_memory is False) alongside the current
decisions/rejected/episodes/skills breakdown, and returns WARN with a compaction
remediation when that count crosses a threshold. One mechanism: enrich the
existing probe; no new probe, no new command. Primary acceptance: with prunable
count above the threshold the probe status is WARN and its detail names the
prunable count and suggests compaction; below it, behaviour is unchanged (PASS).
~60 net LOC incl. tests.
Customer story: A maestro resuming forge-loop after context loss can only
gate compaction on what it can see; this makes the prunable-memory backlog an
explicit, inspectable health signal in doctor.
Problem
_memory_integrity_checkinsrc/forge_loop/control/doctor.pyreports thememory store's breakdown (
decisions/active,rejected_paths,episodes,skills/procedural) and returns PASS whenever the store opens cleanly. It hasno visibility into the prunable-memory backlog — the active episodic items
that are not load-bearing and accumulate monotonically (one per merged/failed
issue) until they drown the load-bearing decisions in boot context (the failure
named by parent epic #426).
Concrete example: a loop that has run 300 issues holds ~600 non-load-bearing
episodes alongside ~30 decisions.
doctortoday printsepisodes=600and returns PASS — the maestro resuming after context lossgets a green light and no signal that compaction is overdue. The prunable
backlog must become an explicit, inspectable health signal so compaction can be
gated on something visible.
Dependency
is_load_bearing_memory(item)is the pure predicate added by sibling #427(same epic #426). "Prunable" = active items where
is_load_bearing_memory(item)is
False. This ticket consumes that predicate; it does not define it.is_load_bearing_memoryfromforge_loop.memory.exists before building on it. Do not re-implement the classification here —
coordinate ordering or block on Add a pure is_load_bearing_memory(item) predicate for curated memory #427. Flag in your PR which path you took.
Acceptance criteria
PRUNABLE_MEMORY_WARN_THRESHOLD)is declared once in
doctor.py(no bare literal at the call site), matching theexisting named-constant convention (
PASS/FAIL/WARN,*_REMEDIATION).COMPACT_REMEDIATION) isdeclared alongside the other
*_REMEDIATIONconstants and suggests compactingprunable memory.
_memory_integrity_check's detail line additionally reports the prunable count(active items where
is_load_bearing_memory(item)isFalse) alongside theexisting
decisions/active/rejected_paths/episodes/skills/proceduralbreakdown. The existing fields are preserved unchanged (do not rename or drop).
statusisWARN,detailnamesthe prunable count, and
remediationis the compaction remediation (non-None).statusisPASSandremediationisNone(only the detail string gains the new field).unreadable-store (FAIL) branches are untouched and still behave as before.
.forge, consistent with themodule-level invariant in
doctor.py's docstring.Test matrix
Add to
tests/test_doctor_control_plane.py(thememory_integrityunit grouparound
test_reports_counts_on_populated_store, lines ~267-296):and a sub-threshold number of prunable episodes; assert
status == PASS,remediation is None, anddetailcontains the prunable-count field.the threshold; assert
status == WARN,detailnames the prunable count andmentions compaction, and
remediationis the compaction constant (non-None).PASS(proves the strict>comparison, not>=); threshold+1 flips toWARN.decisions/active=,rejected_paths=,episodes=,skills/procedural=.memory.dbstill returnsFAIL(the newprunable logic must run only on the clean-open path and never mask the
corrupt-store FAIL branch). Reuse the existing
test_fails_cleanly_on_corrupt_dbpattern.
forge-loop doctor --jsonover a seeded above-threshold storeemits the
memory_integrityobject withstatus == "warn"and a non-nullremediation, and the human table renders the prunable count + remediation.
Out of scope
is_load_bearing_memory— that is Add a pure is_load_bearing_memory(item) predicate for curated memory #427.compact_episodic).This ticket only reports the backlog; the remediation is advisory text.
CHECK_NAMESentry, or a new CLIcommand. Enrich the existing
memory_integrityprobe only.forge-loop.yaml wiring) unless trivially aligned with an existing pattern.
File pointers
src/forge_loop/control/doctor.py—_memory_integrity_check(~L162-202);add the threshold + remediation constants near the existing
*_REMEDIATIONblock (~L49-58).
tests/test_doctor_control_plane.py—memory_integrityunit group (~L267-296),the
--jsonintegration test (~L445+), and the human-table test (~L554+).src/forge_loop/memory/__init__.py/src/forge_loop/memory/store.py/models.py— source ofis_load_bearing_memory(from Add a pure is_load_bearing_memory(item) predicate for curated memory #427); read-only here.src/forge_loop/memory/store.py—SqliteMemoryStore.list_active(kind=...)is how the probe already enumerates items; reuse it.
Original report