Skip to content

feat(memory-ingest+search): Phase 4C — synthesize-only consolidation for SUM#12

Draft
moralespanitz wants to merge 1 commit intoexperiment/phase2-combined-stackfrom
feature/exp-sum-synthesize-only
Draft

feat(memory-ingest+search): Phase 4C — synthesize-only consolidation for SUM#12
moralespanitz wants to merge 1 commit intoexperiment/phase2-combined-stackfrom
feature/exp-sum-synthesize-only

Conversation

@moralespanitz
Copy link
Copy Markdown

Summary

Periodic synthesis that builds summary memories alongside originals, never archiving. Targets BEAM SUM — currently 1/6 across N=3 with EXP-08 (scheduled-consolidation), 2/6 without it. EXP-08 actively hurt SUM because executeConsolidation soft-deletes cluster members; this PR keeps everything and tags summaries instead.

Why this differs from EXP-08

EXP-08 (PR #10) calls executeConsolidation which calls softDeleteMemory on cluster members — removes facts BEAM SUM/IE/TR questions need. This PR uses the lower-level primitives (findConsolidationCandidates, synthesizeCluster) directly and skips the archive step entirely. Originals are preserved.

Three behavioral changes

  1. Synthesis trigger in ingest path: per-user_id turn counter; when current % summarySynthesisTurnInterval === 0, synthesize.
  2. Summary tagging: produced summaries get metadata.fact_role: 'summary' and metadata.summary_of: [original_fact_ids].
  3. Down-weight for non-SUM queries: applySummaryDownweight reduces summary scores by summaryDownweightFactor (default 0.5) UNLESS query is summarization-style ("summarize", "give me an overview", etc.) — keeps summaries from diluting non-SUM retrieval.

Scope (581 insertions, 9 files)

  • src/services/summary-synthesis.ts (new, 136 LOC)
  • src/services/summary-downweight.ts (new, 79 LOC) — also includes isSummarizationStyleQuery keyword detector
  • src/services/__tests__/summary-synthesis.test.ts (new, 305 LOC, 21 tests passing)
  • src/config.ts (+25): three new flags + env loaders + allowlist
  • src/services/search-pipeline.ts (+24): wire down-weight stage
  • src/services/memory-ingest.ts (+5): synthesis call site
  • src/services/memory-service-types.ts (+4): IngestRuntimeConfig fields
  • src/app/runtime-container.ts (+2): CoreRuntimeConfig field
  • src/db/repository-types.ts (+2): 'summary_of' in RESERVED_METADATA_KEYS

Critical test assertion

"does NOT soft-delete cluster members (the whole point vs EXP-08)" — explicitly verifies softDeleteMemory is never called from this code path.

New config keys (defaults-off)

summarySynthesisEnabled: boolean = false
summarySynthesisTurnInterval: number = 30
summaryDownweightFactor: number = 0.5

Notes

  • Branch baseline does NOT include EXP-08 (PR feat(memory-ingest): EXP-08 — scheduled consolidation every N turns #10) — counters are independent. If both flags ever enable simultaneously, the per-user_id turn counters live in different modules.
  • Pre-existing failure on config-singleton-audit.test.ts is on baseline 709242f, NOT introduced by this PR (verified by stashing and re-running).

Test plan

  • Typecheck clean
  • 21 new tests pass (including no-soft-delete assertion)
  • Pre-existing tests not regressed
  • Stage-7 dryrun with this enabled — measure SUM lift from EXP-08's 1/6 baseline

Adds a synthesize-only consolidation path that re-uses the existing
clustering and synthesis primitives (findConsolidationCandidates +
synthesizeCluster) but does NOT call softDeleteMemory on cluster
members. The LLM-synthesized summary is stored alongside the
originals, tagged with metadata.fact_role: 'summary' and
metadata.summary_of: [original_fact_ids].

Targets BEAM SUM (1/6 across 19 sprint-2 iters with EXP-08 archive-
style consolidation; 2/6 without). EXP-08 actively hurt SUM because
archiving cluster members removed the underlying facts SUM questions
need to retrieve. Synthesizing alongside originals preserves those
facts for TR / IE / etc. while still giving SUM-style queries a
compact, retrievable answer.

Trigger: per-user_id ingest turn counter (in-memory, not persisted).
On every summarySynthesisTurnInterval ingested turn, the system
clusters and synthesizes. Errors are logged and swallowed — ingest is
never blocked.

Retrieval awareness: a new search-pipeline stage (after current-
state-ranking, before instruction-boost) multiplies summary results'
score by summaryDownweightFactor (default 0.5) for non-SUM-style
queries so summaries don't dilute regular retrieval. A small keyword
detector (isSummarizationStyleQuery) skips the down-weight for
queries like "summarize", "summary of", "what did we discuss",
"give me an overview", "recap", "tl;dr".

Defaults-off behind summarySynthesisEnabled. New config keys:
- summarySynthesisEnabled: false
- summarySynthesisTurnInterval: 30
- summaryDownweightFactor: 0.5

All three live in INTERNAL_POLICY_CONFIG_FIELDS (experimental tuning
flags). summary_of is added to RESERVED_METADATA_KEYS.

Existing consolidation primitives (findConsolidationCandidates and
synthesizeCluster) are cleanly separable from the archive step in
executeConsolidation — both are already exported and pure with
respect to soft-deletion. No refactor of consolidation-service.ts
was needed.

Files changed:
- src/services/summary-synthesis.ts        (new, 136 LOC)
- src/services/summary-downweight.ts       (new,  79 LOC)
- src/services/__tests__/summary-synthesis.test.ts (new, 305 LOC, 21 tests)
- src/config.ts                            (+25)
- src/services/search-pipeline.ts          (+24)
- src/services/memory-ingest.ts            (+4 trigger calls + 1 import)
- src/services/memory-service-types.ts     (+4)
- src/app/runtime-container.ts             (+2)
- src/db/repository-types.ts               (+2)

Tests (21 passed): flag-off no-op; triggers exactly twice across 60
turns at interval 30; "originals NOT soft-deleted" (the critical
EXP-08-vs-EXP-SUM assertion); summary metadata shape; per-user
counter independence; error swallowing; null synthesis skipped;
non-positive interval guard; isSummarizationStyleQuery classifier
table; down-weight reduces summary score below regular peer for
non-SUM queries; down-weight skipped for "summarize" queries;
no-op when factor >= 1; no-op when no summary results present.

Typecheck: npx tsc --noEmit 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.

1 participant