Move retention constants and math to a focused src/retention.ts module:
- All half-life, reinforcement, dormancy constants
- TYPE_FACTOR, SOURCE_FACTOR, USER_IMPORTANCE_FACTOR
- RETENTION_TYPE_MAX (renamed from TYPE_MAX)
- calculateInitialStrength, calculateEffectiveHalfLife,
calculateRetentionStrength, calculateDormantDays,
calculateEffectiveAgeDays, reinforceMemory
No behavior changes. retention.ts imports only types from types.ts.
Workspace-memory.ts still owns storage, consolidation, and rendering.
- Remove SAFETY_CRITICAL_FACTOR = 6.0 from workspace-memory.ts
- Remove safetyFactor from calculateInitialStrength() - all memories now
fade according to the same rules
- Remove safetyCritical bypass from applyTypeMaxCaps() - safetyCritical
entries compete normally under TYPE_MAX caps
- Preserve safetyCritical?: boolean in LongTermMemoryEntry type for
backward compatibility (no producer sets it to true)
- Update memory-diag to show deprecation warning instead of capacity alert
- Update tests: add backward-compatibility fixture test, deprecation
strength test, normal cap competition test
- Update docs/architecture.md, RELEASE_NOTES.md, CHANGELOG.md,
docs/configuration.md
Phase 1.5 complete: safetyCritical is now a deprecated field with no
active behavior. Safety rules belong in user-controlled agent.md files.
P0.1 - Fix dormant effective age formula:
- Use overlap logic: only apply dormancy to entry's lifetime
- Formula: activeDays + dormantOverlapDays * 0.25
- calculateDormantDays now returns total days (not excess past grace)
- Test: 28 dormant days → 17.5 effective days
P0.2 - Remove hard stale pruning:
- Remove isPrunableByAge from enforcement
- Remove rejected_stale from accounting reasons
- Elimination now by cap competition only
P0.3 - Integrate reinforcement:
- Call reinforceMemory in dedupe absorption path
- Call reinforceMemory in promotion duplicate path
- Update retentionClock on reinforcement
A1 - Retention clock reset on reinforcement
A4 - Fix tests to encode correct formula
Wave 2c - Dormant workspace tracking:
- Add lastActivityAt to WorkspaceMemoryStore
- Implement calculateDormantDays with 14-day grace period
- Wire dormant days into retention-strength calculation
Wave 3 - Reinforcement:
- Add lastReinforcedSessionID to LongTermMemoryEntry
- Implement reinforceMemory with guards (same-session, 1hr interval, max 6)
- Set retentionClock on memory creation in extractors.ts and plugin.ts
Tests: 219 → 222, all pass
Problem: clearPendingMemories() and recordPromotionRejections() would
incorrectly clear or mutate owned entries during global unowned promotion.
Fixes:
1. clearPendingMemories() now respects owner/unowned scope:
- global clearUnowned only clears unowned same-key entries
- owned same-key entries are preserved
- explicit global clear-all-by-key fallback still works
2. recordPromotionRejections() now has includeUnownedOnly option:
- global unowned rejection only increments/exhausts unowned entries
- owned same-key entries are preserved
3. Added regression tests:
- global unowned clear keeps owned same-key entries
- global unowned rejection only exhausts unowned same-key entries
Tests: 182 pass, 0 fail
Problem: CI test "updateJSON serializes writes across separate node processes"
was failing with expect 100 but got 89/97. The root cause was isLockStale()
being too aggressive - it could mistakenly delete locks held by other processes.
Fixes:
1. isLockStale() now uses mtime only - fresh locks are never stale
2. Added heartbeat mechanism during lock hold to support long updaters
3. Removed PID check that was unreliable in CI/containers
4. Fixed ENOENT race when lock is released between EEXIST and stat
Tests: 180 pass, 0 fail
- Add docs/superpowers/plans/ to .gitignore
- Remove tracked plan files from git
- Update docs/architecture.md:
- Change primary extraction format from XML to 'Memory candidates:'
- Mark XML format as legacy/deprecated
- Fix hot session state injection example
Wave 3 of memory quality optimization plan.
- Add good memory examples in buildCompactionPrompt()
- Add bad memory examples to skip (test counts, commit hashes, etc.)
- Add prompt assertions in tests to prevent regression
- Emphasize 'useful if a new agent opens this workspace next week'
Wave 2 of memory quality optimization plan.
- 5 accepted cases: durable facts that should be kept
- 7 rejected cases: noise that should be filtered
- Parser-level regression guard (zero API call)
- All cases pass against current extractors.ts
Architecture review fixes:
- Promotion accounting: only clear pending memories that survived
workspace memory normalization/cap limits. Use retainedKeys from
the returned normalized store instead of attemptedKeys.
- Shared sessionID extraction: add sessionIDFromEventProperties()
helper and use it in both session.compacted and session.deleted,
fixing the previous gap where session.deleted only read info.id.
- Strengthen compaction refresh test: seed workspace memory before
first transform so firstSystem1 is non-empty, then assert
refreshed system[1] preserves existing entries AND contains
promoted memories.