Skip to content

Dense memory and final-summary recovery

What changed

A complete, non-repetitive memory is accepted up to the existing 12,000-character storage ceiling. The preferred 7,000-character size and smaller writing targets are guidance, not rejection limits: an otherwise valid 7,807-character result saves immediately without another paid call. Only a complete draft above 12,000 characters gets a compression pass over that draft. Incomplete or repetitive drafts still retry from original evidence. The two-call allowance, successful coverage cursor, pause-after-three-failures policy, and accepted-output billing are unchanged.

Story summaries use soft length guidance instead of strict sentence and bullet quotas. Provider safety caps allow 2,048 tokens per episode and 4,096 tokens for the final merge, leaving room above the requested lengths to finish important facts. A truly truncated final merge retries once with a shorter target, using the same complete episode summaries and source evidence. It does not repeat the episode calls. The retry uses the existing provider resolution, cancellation signal, billing checks, and shared per-job attempt allowance. A second failure preserves the previous summary and coverage.

These changes have deterministic regression coverage. Live model compliance must be checked separately; a passing mocked test does not establish that every model follows the prompt.

Prepared remote testing sessions

Sign in as Gorgy on testing. These are private synthetic fixtures, not copies of customer conversations. Errors were seeded to expose the Retry controls; they are not evidence of an actual provider failure.

  • Memory: 174 old records: 6,874-character saved memory, ten messages, three eligible pending assistant replies, and three seeded failures. The memory passes the repetition detector, so this exercises updating existing memory rather than clearing a repetitive seed.
  • Summary: long Chinese merge: 60 Chinese messages, a 3,471-character previous summary, 24,000-token trigger, and 12,000-token recent tail.

Both select google/gemini-2.5-flash-lite. In Memory & Summary → Settings, confirm this model for both systems and choose an available Yumina or BYOK source. Keep Layers disabled for these tests so the results refer to the intended system.

Memory test

  1. Open the memory fixture, then Memory & Summary → Memory. Confirm the seeded failed state, three failures, and three pending replies.
  2. Click Retry once. It processes a bounded batch, not the entire history. Initially it processes one exchange because this fixture is paused after repeated size failures.
  3. Expect a saved, complete memory; the failure clears and the pending count falls from three to two. A model may produce acceptable output on the first attempt, so a visible retry is not guaranteed.
  4. Verify continuity: 江珩 returned the 青铜钥匙 to 林澄; the return promise is now completed; 南桥 remains closed and traffic must use 北桥. Routine completed inspections should be condensed instead of retaining 174 entries.
  5. The latest reply mentions 蓝灯亮起. That fact must not appear in memory yet. Memory intentionally excludes the newest assistant reply to support regenerating it safely.
  6. Click Retry again to process the remaining eligible exchanges. Expect zero pending replies. The newest reply still remains excluded.
  7. Close/reopen or refresh the panel. The saved result and pending count should remain consistent.

For a real paused chat, use Retry to preserve old memory and catch up from its coverage cursor. Regenerate is a separate rebuild operation. Do not Clear memory as a prerequisite. Deploying a fix does not reset existing production pause counters.

Summary test

  1. Open the summary fixture, then Memory & Summary → Summary.
  2. Click Compress. This uses the previous summary and new episode summaries, exercising the incremental final merge. Wait for a saved result; a started/queued message is not success.
  3. Expect all three headings: Story So Far, Major Open Threads, and Important Past Events, with complete Chinese prose and concise lists. Confirm names, unresolved promises, and current constraints against the visible conversation.
  4. Confirm the error clears, update time advances, and some older raw context is compacted. Original messages remain available in chat; compression does not delete them.
  5. Regenerate separately tests a rebuild from the available history. It can reuse matching completed episode checkpoints. A large history may exceed the rebuild safety limit; use Compress for bounded progress instead.

Both model output and upstream routing vary. Real truncation is not guaranteed in these fixtures. The forced-truncation regression test proves one retry, cancellation, billing, and shared-budget behavior without deliberately spending tokens until the provider fails.

Developer verification

Run from the repository root on the testing branch, which includes the isolated test runner:

powershell
node packages/server/scripts/test-local.mjs src/lib/story-summary-prompts.test.ts src/lib/session-memory-prompts.test.ts src/lib/session-memory-batch.test.ts src/lib/summary-episode-recovery.test.ts src/lib/session-memory-billing.test.ts
pnpm build
pnpm typecheck

For deployed memory calls, inspect PostHog summary_job events for the test session, system=session-memory, and memory_revision=2026-09-12-soft-size-targets. Complete results above 7,000 and at or below 12,000 characters should be accepted without a size retry; telemetry reports acceptance_limit=12000. A result above 12,000 still needs recovery. Failed candidates must not move coverage or incur an accepted-memory debit.

For summary calls, inspect saved state and usage together: episode calls precede the merge; a truncated merge or complete result above the 12,000-character storage safety ceiling may be followed by one smaller merge. Complete accepted text must retain its ending; oversized text must never be silently sliced and billed. Failed final text must not be persisted. A healthy deployment alone does not prove this live model path passed.