You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by as...@apache.org on 2026/10/16 04:28:40 UTC

(maka) branch main updated: refactor(runtime): centralize history-compact checkpoint currency validation (#5930) (#5965)

This is an automated email from the ASF dual-hosted git repository.

Astro-Han pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/maka.git


The following commit(s) were added to refs/heads/main by this push:
     new b0a385854d refactor(runtime): centralize history-compact checkpoint currency validation (#5930) (#5965)
b0a385854d is described below

commit b0a385854db28817fb2f62ab824b35ebcf0e61af
Author: Colafornia <co...@gmail.com>
AuthorDate: Fri Oct 16 12:28:35 2026 +0800

    refactor(runtime): centralize history-compact checkpoint currency validation (#5930) (#5965)
    
    Issue #5930 asks for one shared, directly testable decision for history-compaction checkpoint currency. Five call sites independently reassembled the two-layer rule — raw prefix identity plus a digest of the transition-folded effective view — with divergent snapshot timing and coverage; that duplication had already produced a live defect (#5929, fixed pointwise by #5931). This change moves the whole decision into `checkHistoryCompactCheckpointCurrency` in `history-compact-checkpoint.t [...]
    
    Fixes #5930
    
    Generated-by: Devin
    Co-Authored-By: Devin <15...@users.noreply.github.com>
---
 .../src/__tests__/conversation-copy.test.ts        |   6 +-
 .../__tests__/history-compact-checkpoint.test.ts   | 208 ++++++++++++++++++++-
 .../src/__tests__/history-compact-test-fixtures.ts |  42 +++++
 .../src/__tests__/history-compaction.test.ts       |  49 +++--
 .../__tests__/mid-turn-capacity-backend.test.ts    |  62 ++++++
 packages/runtime/src/ai-sdk-compaction.ts          |  55 ++----
 packages/runtime/src/ai-sdk-turn.ts                |  26 +--
 packages/runtime/src/context-budget.ts             |   6 +-
 packages/runtime/src/conversation-copy.ts          |  36 ++--
 packages/runtime/src/history-compact-checkpoint.ts |  65 +++++++
 packages/runtime/src/history-compaction.ts         |  76 +++++---
 packages/runtime/src/memory-extraction.ts          |   2 +-
 12 files changed, 499 insertions(+), 134 deletions(-)

diff --git a/packages/runtime/src/__tests__/conversation-copy.test.ts b/packages/runtime/src/__tests__/conversation-copy.test.ts
index 5824608ac0..a365090b7d 100644
--- a/packages/runtime/src/__tests__/conversation-copy.test.ts
+++ b/packages/runtime/src/__tests__/conversation-copy.test.ts
@@ -62,13 +62,11 @@ import {
 import {
   buildHistoryCompactCheckpoint,
   historyCompactSourceDigest,
+  isHistoryCompactContentEvent,
   matchHistoryCompactCheckpointPrefix,
   validateHistoryCompactCheckpointShape,
 } from '../history-compact-checkpoint.js';
-import {
-  isHistoryCompactContentEvent,
-  applyRuntimeEventHistoryCompact,
-} from '../history-compaction.js';
+import { applyRuntimeEventHistoryCompact } from '../history-compaction.js';
 import { RuntimeReadModel, type RuntimeReadModelSessionView } from '../runtime-read-model.js';
 import { buildToolOperationId } from '../runtime-commit-sink.js';
 import {
diff --git a/packages/runtime/src/__tests__/history-compact-checkpoint.test.ts b/packages/runtime/src/__tests__/history-compact-checkpoint.test.ts
index 05f615b6e3..2a1821ce98 100644
--- a/packages/runtime/src/__tests__/history-compact-checkpoint.test.ts
+++ b/packages/runtime/src/__tests__/history-compact-checkpoint.test.ts
@@ -26,19 +26,30 @@ import {
   canContinueHistoryCompactCheckpointForModel,
   canReplayHistoryCompactCheckpointForModel,
   canReplaceHistoryCompactCheckpoint,
+  checkHistoryCompactCheckpointCurrency,
   historyCompactCheckpointToModelMessage,
   historyCompactCheckpointToRuntimeEvent,
+  historyCompactSourceDigest,
   isProviderHistoryCompactCheckpoint,
   matchHistoryCompactCheckpointPrefix,
   validateHistoryCompactCheckpointShape,
+  type HistoryCompactCheckpoint,
 } from '../history-compact-checkpoint.js';
+import {
+  reduceEffectiveModelProjections,
+  type LoadedModelProjectionTransitions,
+} from '../model-projection-transition-ledger.js';
 import {
   loadHistoryCompactCheckpointsFromRunLedger,
   loadLatestHistoryCompactCheckpointFromRunLedger,
 } from '../history-compact-ledger.js';
 import { estimateRuntimeEventsTokens } from '../context-budget.js';
 import { applyRuntimeEventHistoryCompact } from '../history-compaction.js';
-import { sectionedSummary } from './history-compact-test-fixtures.js';
+import {
+  archiveTransitionFor,
+  EMPTY_PROJECTION_SNAPSHOT,
+  sectionedSummary,
+} from './history-compact-test-fixtures.js';
 
 // Satisfies the sectioned summary contract for marked-checkpoint fixtures.
 const STRUCTURED_SUMMARY = [
@@ -987,6 +998,201 @@ describe('history compact checkpoint', () => {
   });
 });
 
+/**
+ * The shared checkpoint-currency decision (#5930): raw prefix identity first,
+ * then the covered span folded through the CALLER-SUPPLIED projection snapshot
+ * and compared against coverage.effectiveSourceDigest. The function does not
+ * load transitions — the same event list judged under two different snapshots
+ * must be able to disagree.
+ */
+describe('checkHistoryCompactCheckpointCurrency', () => {
+  function checkpointOver(
+    covered: readonly RuntimeEvent[],
+    effectiveCovered?: readonly RuntimeEvent[],
+  ): HistoryCompactCheckpoint {
+    return buildHistoryCompactCheckpoint({
+      sessionId: 'session-1',
+      coveredRuntimeEvents: covered,
+      ...(effectiveCovered ? { effectiveCoveredRuntimeEvents: effectiveCovered } : {}),
+      summary: sectionedSummary('checkpoint summary'),
+    });
+  }
+
+  test('reports the raw-mismatch reason without touching the projection', () => {
+    const covered = [textEvent(0), textEvent(1), textEvent(2)];
+    const checkpoint = checkpointOver(covered);
+
+    for (const [name, events, reason] of [
+      ['shorter event list', [textEvent(0), textEvent(1)], 'coverage_miss'],
+      [
+        'different covered boundary identity',
+        [textEvent(0), textEvent(1), { ...textEvent(2), id: 'event-elsewhere' }],
+        'coverage_miss',
+      ],
+      [
+        'same identity but mutated covered content',
+        [textEvent(0), { ...textEvent(1), ts: textEvent(1).ts + 1 }, textEvent(2)],
+        'source_hash_mismatch',
+      ],
+    ] as const) {
+      const result = checkHistoryCompactCheckpointCurrency(
+        checkpoint,
+        events,
+        EMPTY_PROJECTION_SNAPSHOT,
+      );
+      assert.deepEqual(result, { status: 'raw_mismatch', reason }, name);
+    }
+  });
+
+  test('returns the prefix match when the effective view is unchanged', () => {
+    const events = Array.from({ length: 6 }, (_, index) => textEvent(index));
+    const checkpoint = checkpointOver(events.slice(0, 4));
+
+    const result = checkHistoryCompactCheckpointCurrency(
+      checkpoint,
+      events,
+      EMPTY_PROJECTION_SNAPSHOT,
+    );
+
+    assert.equal(result.status, 'current');
+    if (result.status !== 'current') return;
+    assert.equal(result.match.coveredEventCount, 4);
+    assert.deepEqual(result.match.coveredRuntimeEvents, events.slice(0, 4));
+    assert.deepEqual(result.match.successorRuntimeEvents, events.slice(4));
+  });
+
+  test('judges the covered span against the supplied snapshot, not the latest ledger', () => {
+    const result = toolResultEvent('event-result', 'RAW_COVERED_BODY');
+    const covered = [textEvent(0), result];
+    const transition = archiveTransitionFor(result, 'EFFECTIVE_REPLACEMENT_BODY');
+    // The checkpoint was minted AFTER the transition: its pinned digest
+    // describes the folded view.
+    const checkpoint = checkpointOver(
+      covered,
+      reduceEffectiveModelProjections(covered, [transition]).events,
+    );
+
+    // A snapshot taken before the transition still sees the raw body: the
+    // checkpoint is stale against it, even though the newest ledger state
+    // would reproduce the pin. The function consults only what it is given.
+    assert.deepEqual(
+      checkHistoryCompactCheckpointCurrency(checkpoint, covered, EMPTY_PROJECTION_SNAPSHOT),
+      {
+        status: 'effective_history_changed',
+      },
+    );
+    const currentSnapshot: LoadedModelProjectionTransitions = {
+      ...EMPTY_PROJECTION_SNAPSHOT,
+      transitions: [transition],
+    };
+    assert.equal(
+      checkHistoryCompactCheckpointCurrency(checkpoint, covered, currentSnapshot).status,
+      'current',
+    );
+  });
+
+  test('reports effective_history_changed when a transition drifted the covered view', () => {
+    const result = toolResultEvent('event-result', 'RAW_COVERED_BODY');
+    const covered = [textEvent(0), result];
+    // Minted before the transition: the pin describes the un-folded view.
+    const checkpoint = checkpointOver(covered);
+    const snapshot: LoadedModelProjectionTransitions = {
+      ...EMPTY_PROJECTION_SNAPSHOT,
+      transitions: [archiveTransitionFor(result, 'EFFECTIVE_REPLACEMENT_BODY')],
+    };
+
+    assert.deepEqual(checkHistoryCompactCheckpointCurrency(checkpoint, covered, snapshot), {
+      status: 'effective_history_changed',
+    });
+  });
+
+  test('reports effective_history_changed when no effective digest is pinned', () => {
+    const covered = [textEvent(0), textEvent(1)];
+    const checkpoint = checkpointOver(covered);
+    // A legacy record without the source block may carry no
+    // effectiveSourceDigest at all: raw identity still matches but there is
+    // nothing to judge content currency against.
+    const legacy = {
+      ...checkpoint,
+      source: undefined,
+      coverage: { ...checkpoint.coverage, effectiveSourceDigest: undefined },
+    } as HistoryCompactCheckpoint;
+
+    assert.equal(validateHistoryCompactCheckpointShape(legacy), true);
+    assert.deepEqual(
+      checkHistoryCompactCheckpointCurrency(legacy, covered, EMPTY_PROJECTION_SNAPSHOT),
+      {
+        status: 'effective_history_changed',
+      },
+    );
+  });
+
+  test('reports effective_history_changed when a covered event is hidden from the projection', () => {
+    // The covered span as recorded includes an event the compact projection
+    // does not count (model-hidden): after selecting the covered effective
+    // prefix fewer than eventCount events remain, and the prefix never
+    // reaches the hidden tail the raw match pins as its through event.
+    const hiddenTail = { ...textEvent(1), modelVisibility: 'hidden' as const };
+    const covered = [textEvent(0), hiddenTail];
+    const checkpoint = checkpointOver(covered);
+    assert.equal(
+      checkpoint.coverage.through.runtimeEventId,
+      hiddenTail.id,
+      'the raw match pins the hidden tail as through',
+    );
+
+    const currency = checkHistoryCompactCheckpointCurrency(
+      checkpoint,
+      covered,
+      EMPTY_PROJECTION_SNAPSHOT,
+    );
+    assert.equal(currency.status, 'effective_history_changed');
+  });
+
+  test('an unreadable transition target withholds the covered body from the digest', () => {
+    const result = toolResultEvent('event-result', 'RAW_COVERED_BODY');
+    const covered = [textEvent(0), result];
+    const checkpoint = checkpointOver(covered);
+    const withholding: LoadedModelProjectionTransitions = {
+      ...EMPTY_PROJECTION_SNAPSHOT,
+      unreadableTargets: new Set<string>(['event-result::tool_result']),
+    };
+
+    // The pinned digest described the readable body; withheld content can no
+    // longer be judged current, and the raw body must not come back.
+    assert.equal(
+      checkHistoryCompactCheckpointCurrency(checkpoint, covered, withholding).status,
+      'effective_history_changed',
+    );
+
+    // A checkpoint minted over the withheld view does stay current under the
+    // same snapshot — the fold really applies the snapshot's unreadable set.
+    const withheldCheckpoint = checkpointOver(
+      covered,
+      reduceEffectiveModelProjections(covered, [], withholding.unreadableTargets).events,
+    );
+    assert.equal(
+      checkHistoryCompactCheckpointCurrency(withheldCheckpoint, covered, withholding).status,
+      'current',
+    );
+  });
+
+  function toolResultEvent(id: string, body: string): RuntimeEvent {
+    return {
+      ...textEvent(1),
+      id,
+      role: 'tool',
+      author: 'tool',
+      content: {
+        kind: 'function_response',
+        id: 'call-1',
+        name: 'Read',
+        result: { body },
+      },
+    };
+  }
+});
+
 function textEvent(index: number): RuntimeEvent {
   return {
     id: `event-${index}`,
diff --git a/packages/runtime/src/__tests__/history-compact-test-fixtures.ts b/packages/runtime/src/__tests__/history-compact-test-fixtures.ts
index 3041927a36..6681aa7779 100644
--- a/packages/runtime/src/__tests__/history-compact-test-fixtures.ts
+++ b/packages/runtime/src/__tests__/history-compact-test-fixtures.ts
@@ -17,6 +17,48 @@
  * under the License.
  */
 
+import assert from 'node:assert/strict';
+import type { RuntimeEvent } from '@maka/core/runtime-event';
+import {
+  buildModelProjectionTransition,
+  type ModelProjectionTransition,
+} from '@maka/core/model-projection-transition';
+import { compatibilityToolResultProjection } from '../durable-tool-result-projection.js';
+import type { LoadedModelProjectionTransitions } from '../model-projection-transition-ledger.js';
+
 export function sectionedSummary(goal: string): string {
   return `## Goal\n${goal}\n\n## Progress\n- done\n\n## Next Steps\n1. continue\n\n## Critical Context\n- (none)`;
 }
+
+/** The empty transition view: folding through it is the identity. */
+export const EMPTY_PROJECTION_SNAPSHOT: LoadedModelProjectionTransitions = {
+  transitions: [],
+  unreadableTargets: new Set<string>(),
+  unscopedUnreadable: 0,
+};
+
+/**
+ * A model-projection transition that replaces a `function_response` event's
+ * tool-result projection with fixed text — the archive shape checkpoint
+ * currency tests fold covered spans through.
+ */
+export function archiveTransitionFor(
+  event: RuntimeEvent,
+  replacementText: string,
+): ModelProjectionTransition {
+  const content = event.content as Extract<RuntimeEvent['content'], { kind: 'function_response' }>;
+  const sourceProjection = compatibilityToolResultProjection(content, event.sessionId);
+  assert.ok(sourceProjection);
+  return buildModelProjectionTransition({
+    sessionId: event.sessionId,
+    target: {
+      runtimeEventId: event.id,
+      part: 'tool_result',
+      toolCallId: content.id,
+      toolName: content.name,
+    },
+    sourceProjection,
+    replacement: { version: 1, kind: 'text', text: replacementText },
+    now: 1,
+  });
+}
diff --git a/packages/runtime/src/__tests__/history-compaction.test.ts b/packages/runtime/src/__tests__/history-compaction.test.ts
index 86a45ab10e..e1772bcfcd 100644
--- a/packages/runtime/src/__tests__/history-compaction.test.ts
+++ b/packages/runtime/src/__tests__/history-compaction.test.ts
@@ -29,6 +29,11 @@ import {
 import { HistoryCompactSummarizerError } from '../history-compact-summarizer.js';
 import { testInvocationRecord } from './invocation-fixture.js';
 import { matchHistoryCompactCheckpointPrefix } from '../history-compact-checkpoint.js';
+import {
+  archiveTransitionFor,
+  EMPTY_PROJECTION_SNAPSHOT,
+} from './history-compact-test-fixtures.js';
+import type { LoadedModelProjectionTransitions } from '../model-projection-transition-ledger.js';
 
 describe('safe compaction prefix selection', () => {
   test('folds the largest immutable non-partial prefix, leaving the reserved tail', () => {
@@ -609,10 +614,12 @@ describe('plan context compaction', () => {
       call('call-c', 'cc', 'turn-1'),
       result('res-c', 'cc', 'turn-1'),
     ];
-    const identityFold = async (covered: readonly RuntimeEvent[]) => [...covered];
     // Coverage ends at `res-a`: the first fold's covered span contains it.
     const first = await planHistoryCompaction(
-      planInput({ orderedEvents: events, projectEffectiveCoverage: identityFold }),
+      planInput({
+        orderedEvents: events,
+        loadProjectionSnapshot: async () => EMPTY_PROJECTION_SNAPSHOT,
+      }),
     );
     assert.equal(first.decision, 'compacted');
     if (first.decision !== 'compacted') return;
@@ -622,7 +629,7 @@ describe('plan context compaction', () => {
       planInput({
         orderedEvents: longerEvents,
         previousCheckpoint: first.checkpoint,
-        projectEffectiveCoverage: identityFold,
+        loadProjectionSnapshot: async () => EMPTY_PROJECTION_SNAPSHOT,
         summarize: ({ newlyFoldedRuntimeEvents, previousCheckpoint }) => {
           seenNewlyFolded = newlyFoldedRuntimeEvents.map((event) => event.id);
           assert.equal(previousCheckpoint?.checkpointId, first.checkpoint.checkpointId);
@@ -643,11 +650,13 @@ describe('plan context compaction', () => {
       call('call-c', 'cc', 'turn-1'),
       result('res-c', 'cc', 'turn-1'),
     ];
-    const identityFold = async (covered: readonly RuntimeEvent[]) => [...covered];
     // First fold: no transition exists, so the effective view IS the raw view
     // and the checkpoint (covering through `res-a`) pins that digest.
     const first = await planHistoryCompaction(
-      planInput({ orderedEvents: events, projectEffectiveCoverage: identityFold }),
+      planInput({
+        orderedEvents: events,
+        loadProjectionSnapshot: async () => EMPTY_PROJECTION_SNAPSHOT,
+      }),
     );
     assert.equal(first.decision, 'compacted');
     if (first.decision !== 'compacted') return;
@@ -656,25 +665,15 @@ describe('plan context compaction', () => {
     // first checkpoint's coverage — leaving the raw prefix untouched. The
     // inherited summary still quotes the raw body, but the view it describes
     // no longer exists.
-    const foldWithArchive = async (covered: readonly RuntimeEvent[]): Promise<RuntimeEvent[]> =>
-      covered.map((event) => {
-        if (event.id !== 'res-a') return event;
-        const content = event.content as Extract<
-          RuntimeEvent['content'],
-          { kind: 'function_response' }
-        >;
-        return {
-          ...event,
-          content: {
-            ...content,
-            modelProjection: {
-              version: 1 as const,
-              kind: 'text' as const,
-              text: '[archived: artifact-res-a]',
-            },
-          },
-        };
-      });
+    const archiveSnapshot: LoadedModelProjectionTransitions = {
+      ...EMPTY_PROJECTION_SNAPSHOT,
+      transitions: [
+        archiveTransitionFor(
+          events.find((event) => event.id === 'res-a')!,
+          '[archived: artifact-res-a]',
+        ),
+      ],
+    };
 
     let summarizeSawPrevious: string | undefined;
     let seenCovered: string[] = [];
@@ -683,7 +682,7 @@ describe('plan context compaction', () => {
       planInput({
         orderedEvents: longerEvents,
         previousCheckpoint: first.checkpoint,
-        projectEffectiveCoverage: foldWithArchive,
+        loadProjectionSnapshot: async () => archiveSnapshot,
         summarize: ({ coveredRuntimeEvents, newlyFoldedRuntimeEvents, previousCheckpoint }) => {
           summarizeSawPrevious = previousCheckpoint?.checkpointId;
           seenCovered = coveredRuntimeEvents.map((event) => event.id);
diff --git a/packages/runtime/src/__tests__/mid-turn-capacity-backend.test.ts b/packages/runtime/src/__tests__/mid-turn-capacity-backend.test.ts
index 4d9c0595e4..8a5585dba6 100644
--- a/packages/runtime/src/__tests__/mid-turn-capacity-backend.test.ts
+++ b/packages/runtime/src/__tests__/mid-turn-capacity-backend.test.ts
@@ -42,6 +42,11 @@ import type {
   HistoryCompactCheckpoint,
   HistoryCompactProviderState,
 } from '../history-compact-checkpoint.js';
+import {
+  archiveTransitionFor,
+  EMPTY_PROJECTION_SNAPSHOT,
+} from './history-compact-test-fixtures.js';
+import type { LoadedModelProjectionTransitions } from '../model-projection-transition-ledger.js';
 import type { ContextBudgetDiagnostic } from '@maka/core/usage-stats/types';
 import { HistoryCompactSummarizerError } from '../history-compact-error.js';
 import { buildLlmHistorySummarizer } from '../history-compact-summarizer.js';
@@ -176,6 +181,12 @@ interface MidTurnFixtureOptions {
   systemPromptChars?: number;
   /** An always-active tool whose schema dominates the request payload. */
   bigActiveTool?: boolean;
+  /**
+   * Replace the transition-ledger read. Evaluated on every load, so a test can
+   * return a record only once some later fact (e.g. a recorded checkpoint)
+   * exists — that is how a transition committed mid-send is modeled.
+   */
+  loadTransitions?: () => Promise<LoadedModelProjectionTransitions>;
   /** Enable and capture automatic Memory extraction without allowing it to settle. */
   captureMemoryExtraction?: boolean;
   memoryGate?:
@@ -646,6 +657,7 @@ function buildFixture(options: MidTurnFixtureOptions = {}): MidTurnFixture {
       if (options.record) return options.record(checkpoint);
       recorded.push(checkpoint);
     },
+    ...(options.loadTransitions ? { loadModelProjectionTransitions: options.loadTransitions } : {}),
     loadTurnRuntimeEvents: async (turnId) => {
       fixture.ledgerReads += 1;
       // Emulate the durable read: let the event consumer's pending microtask
@@ -1245,6 +1257,56 @@ function defineMidTurnSuite(consumer: ConsumerMode): void {
     assert.match(thirdPrompt, /tool_result_pruned/);
   });
 
+  test('a covered-span transition committed after the fold drops the mid-turn block', async () => {
+    // The durable turn projection re-validates the checkpoint it replays
+    // against the SAME transition snapshot the rest of the request was built
+    // from. A record committed after the fold rewrites the covered span's
+    // effective view without touching the raw ledger, so the request after it
+    // must replay the effective events WITHOUT the stale block — its summary
+    // still quotes the removed body (#4845 review).
+    let coveredResult: RuntimeEvent | undefined;
+    const fixture = buildFixture({
+      // Three tool steps so one more request is projected after the fold.
+      toolSteps: 3,
+      // Keep the post-fold baseline inside the window so the last step does
+      // not fold again and re-mask the drift check.
+      usageByCall: { 3: { input: 60, output: 10 } },
+      loadTransitions: async () => {
+        // The record exists only once the checkpoint does: committed after
+        // the fold whose digest it invalidates.
+        const checkpoint = fixture.recorded[0];
+        if (!checkpoint) {
+          return EMPTY_PROJECTION_SNAPSHOT;
+        }
+        coveredResult ??= fixture.ledger.find(
+          (event) => event.content?.kind === 'function_response',
+        );
+        return {
+          ...EMPTY_PROJECTION_SNAPSHOT,
+          transitions: [archiveTransitionFor(coveredResult!, 'POST_FOLD_TRANSITIONED_RESULT')],
+        };
+      },
+    });
+    await runFixtureTurn(fixture, consumer);
+
+    assert.equal(fixture.model.doStreamCalls.length, 4);
+    // The fold ran once — during the third request's shaping — and persisted
+    // its checkpoint before the transition landed.
+    assert.equal(fixture.recorded.length, 1);
+    assert.match(promptJson(fixture, 2), /MID_TURN_SUMMARY_SENTINEL/);
+    assert.ok(coveredResult);
+    const fourthPrompt = promptJson(fixture, 3);
+    // The next durable projection judged the checkpoint against the same
+    // snapshot the request was built from: stale block dropped, raw body the
+    // transition removed stays removed, the verbatim anchor and uncovered tail
+    // replay from the effective view.
+    assert.equal(fourthPrompt.includes('maka_history_compact_checkpoint'), false);
+    assert.equal(fourthPrompt.includes('MID_TURN_SUMMARY_SENTINEL'), false);
+    assert.equal(fourthPrompt.includes('RAW_SPAN_ONE_'), false);
+    assert.match(fourthPrompt, /POST_FOLD_TRANSITIONED_RESULT/);
+    assert.equal(fourthPrompt.includes(ANCHOR_TEXT), true);
+  });
+
   test('compacts at most once per step, and again once a step is accepted', async () => {
     // The budget is one fold per logical step, not one per send. The first
     // fold covers everything except the live head, so re-entering on that same
diff --git a/packages/runtime/src/ai-sdk-compaction.ts b/packages/runtime/src/ai-sdk-compaction.ts
index 5afb31ec8e..c4d9ff5f11 100644
--- a/packages/runtime/src/ai-sdk-compaction.ts
+++ b/packages/runtime/src/ai-sdk-compaction.ts
@@ -52,11 +52,11 @@ import {
   type ContextBudgetPolicy,
   type ToolResultPruneStats,
 } from './context-budget.js';
-import { isHistoryCompactContentEvent } from './history-compaction.js';
 import {
   canContinueHistoryCompactCheckpointForModel,
   canReplayHistoryCompactCheckpointForModel,
-  historyCompactSourceDigest,
+  checkHistoryCompactCheckpointCurrency,
+  isHistoryCompactContentEvent,
   matchHistoryCompactCheckpointPrefix,
   projectHistoryCompactCheckpointReplay,
   type HistoryCompactCheckpoint,
@@ -325,6 +325,9 @@ export class AiSdkCompaction {
       }
 
       if (previousCheckpoint) {
+        // Load the transition view only when the raw prefix can replay at all:
+        // a checkpoint that cannot match never reaches for the ledger, the
+        // same as before (#5930 keeps the load point, not the check).
         const match = matchHistoryCompactCheckpointPrefix(previousCheckpoint, runtimeContext);
         if (!match.reason && match.successorRuntimeEvents.length === 0) {
           // Raw identity is not enough: a projection transition committed
@@ -333,11 +336,15 @@ export class AiSdkCompaction {
           // digest to still match — the same gate the pre-send path applies
           // (#5929). On drift, fall through to the planner, whose roll-forward
           // currency check discards the stale checkpoint and re-summarizes.
-          const effectiveCovered = await this.foldEffectiveModelHistory(match.coveredRuntimeEvents);
-          if (this.checkpointEffectiveCoverageMatches(previousCheckpoint, effectiveCovered)) {
+          const currency = checkHistoryCompactCheckpointCurrency(
+            previousCheckpoint,
+            runtimeContext,
+            await this.loadModelProjectionTransitions(),
+          );
+          if (currency.status === 'current') {
             const projectedEvents = projectHistoryCompactCheckpointReplay(
               previousCheckpoint,
-              match.coveredRuntimeEvents,
+              currency.match.coveredRuntimeEvents,
               [],
             );
             return {
@@ -391,11 +398,11 @@ export class AiSdkCompaction {
           : {}),
         ...(automaticMemoryBoundary ? { memoryExtractionBoundary: automaticMemoryBoundary } : {}),
         ...(previousCheckpoint ? { previousCheckpoint } : {}),
-        // The planner projects the covered span to its effective view before
+        // The planner folds the covered span through this snapshot before
         // summarizing and pins its digest as coverage.effectiveSourceDigest:
         // the summary can never quote a body a durable transition removed, and
         // a later transition invalidates the checkpoint at replay (#4845).
-        projectEffectiveCoverage: (covered) => this.foldEffectiveModelHistory(covered),
+        loadProjectionSnapshot: () => this.loadModelProjectionTransitions(),
         summarize: async ({
           coveredRuntimeEvents,
           newlyFoldedRuntimeEvents,
@@ -576,28 +583,6 @@ export class AiSdkCompaction {
       .events;
   }
 
-  /**
-   * Whether the checkpoint's pinned effective view still is the covered
-   * prefix's effective view. The raw identity match happens later in the
-   * replay authority; this gate is about content currency: a projection
-   * transition committed after the fold changes what the model may see of the
-   * covered span without touching the raw ledger, and the checkpoint's summary
-   * or provider state must not survive that drift (#4845 review).
-   */
-  private checkpointEffectiveCoverageMatches(
-    checkpoint: HistoryCompactCheckpoint,
-    effectiveEvents: readonly RuntimeEvent[],
-  ): boolean {
-    const pinned = checkpoint.coverage.effectiveSourceDigest;
-    if (pinned === undefined) return false;
-    const covered = effectiveEvents
-      .filter(isHistoryCompactContentEvent)
-      .slice(0, checkpoint.coverage.eventCount);
-    if (covered.length !== checkpoint.coverage.eventCount) return false;
-    if (covered.at(-1)?.id !== checkpoint.coverage.through.runtimeEventId) return false;
-    return historyCompactSourceDigest(covered) === pinned;
-  }
-
   /**
    * Apply the same bounded-result rule to current and prior events.
    *
@@ -727,16 +712,12 @@ export class AiSdkCompaction {
       // This gate is only for the case where the raw identity matches but a
       // projection transition committed after the fold changed the effective
       // view the summary (or provider state) was built from (#4845 review).
-      const rawIdentityMatch = matchHistoryCompactCheckpointPrefix(
+      const currency = checkHistoryCompactCheckpointCurrency(
         loadedCheckpoint,
         runtimeContext.filter(isHistoryCompactContentEvent),
+        prepared.projectionSnapshot,
       );
-      if (rawIdentityMatch.reason) {
-        nextPolicy = {
-          ...nextPolicy,
-          historyCompact: { ...nextPolicy.historyCompact!, checkpoint: loadedCheckpoint },
-        };
-      } else if (this.checkpointEffectiveCoverageMatches(loadedCheckpoint, effective.events)) {
+      if (currency.status !== 'effective_history_changed') {
         nextPolicy = {
           ...nextPolicy,
           historyCompact: { ...nextPolicy.historyCompact!, checkpoint: loadedCheckpoint },
@@ -1114,7 +1095,7 @@ export class AiSdkCompaction {
             },
           }
         : {}),
-      projectEffectiveCoverage: (covered) => this.foldEffectiveModelHistory(covered),
+      loadProjectionSnapshot: () => this.loadModelProjectionTransitions(),
       summarize: async ({ coveredRuntimeEvents, newlyFoldedRuntimeEvents, previousCheckpoint }) => {
         // Same contract as the standalone path: the planner hands the
         // effective (transition-folded) view to the summarizer and pins its
diff --git a/packages/runtime/src/ai-sdk-turn.ts b/packages/runtime/src/ai-sdk-turn.ts
index 72922657dc..e6f6e14b3a 100644
--- a/packages/runtime/src/ai-sdk-turn.ts
+++ b/packages/runtime/src/ai-sdk-turn.ts
@@ -167,12 +167,11 @@ import {
   shouldAppendContextCompactedNote,
   shouldAppendContextCompactionFailedOpenNote,
 } from './context-budget.js';
-import { isHistoryCompactContentEvent } from './history-compaction.js';
 import {
   canContinueHistoryCompactCheckpointForModel,
-  historyCompactSourceDigest,
+  checkHistoryCompactCheckpointCurrency,
+  isHistoryCompactContentEvent,
   isProviderHistoryCompactCheckpoint,
-  matchHistoryCompactCheckpointPrefix,
   projectHistoryCompactCheckpointReplay,
   type HistoryCompactCheckpoint,
 } from './history-compact-checkpoint.js';
@@ -1374,33 +1373,26 @@ export class AiSdkTurn {
           let replayEvents = rawProjectionEvents;
           let effectiveProjectionCheckpoint = projectionCheckpoint;
           if (projectionCheckpoint) {
-            const checkpointMatch = matchHistoryCompactCheckpointPrefix(
+            const currency = checkHistoryCompactCheckpointCurrency(
               projectionCheckpoint,
               rawProjectionEvents,
+              pruned.projectionSnapshot,
             );
-            if (checkpointMatch.reason) {
-              throw new Error(`durable checkpoint projection mismatch: ${checkpointMatch.reason}`);
+            if (currency.status === 'raw_mismatch') {
+              throw new Error(`durable checkpoint projection mismatch: ${currency.reason}`);
             }
             // Content-currency guard: the raw identity still matches, but a
             // transition committed after this fold (e.g. an active-turn prune
             // in this very send) changed the effective view the block was
             // built from. Replay without the stale block — the provider
             // decides fit and overflow recovery re-folds (#4845 review).
-            const pinnedEffectiveDigest = projectionCheckpoint.coverage.effectiveSourceDigest;
-            const coveredEffective = await this.deps.compaction.foldEffectiveModelHistory(
-              checkpointMatch.coveredRuntimeEvents,
-              pruned.projectionSnapshot,
-            );
-            if (
-              pinnedEffectiveDigest === undefined ||
-              historyCompactSourceDigest(coveredEffective) !== pinnedEffectiveDigest
-            ) {
+            if (currency.status === 'effective_history_changed') {
               effectiveProjectionCheckpoint = undefined;
             } else {
               replayEvents = projectHistoryCompactCheckpointReplay(
                 projectionCheckpoint,
-                checkpointMatch.coveredRuntimeEvents,
-                checkpointMatch.successorRuntimeEvents,
+                currency.match.coveredRuntimeEvents,
+                currency.match.successorRuntimeEvents,
               );
             }
             // The checkpoint was capacity-validated before it was persisted.
diff --git a/packages/runtime/src/context-budget.ts b/packages/runtime/src/context-budget.ts
index b86627cdf8..3c7d2fc94a 100644
--- a/packages/runtime/src/context-budget.ts
+++ b/packages/runtime/src/context-budget.ts
@@ -47,10 +47,13 @@ export type {
 import type { ToolResultPrunePolicy } from './tool-result-archive.js';
 import {
   applyRuntimeEventHistoryCompact as applyRuntimeEventHistoryCompactNarrow,
-  isHistoryCompactContentEvent,
   type HistoryCompactionPolicy,
   type HistoryCompactionReplayResult,
 } from './history-compaction.js';
+import {
+  isHistoryCompactContentEvent,
+  type HistoryCompactCheckpoint,
+} from './history-compact-checkpoint.js';
 
 import type { RuntimeEvent } from '@maka/core/runtime-event';
 import type {
@@ -58,7 +61,6 @@ import type {
   ContextBudgetDiagnostic,
 } from '@maka/core/usage-stats/types';
 import { compactionDecisionDiagnosticPatch } from './compaction-boundary.js';
-import type { HistoryCompactCheckpoint } from './history-compact-checkpoint.js';
 
 export interface ContextBudgetPolicy {
   name?: string;
diff --git a/packages/runtime/src/conversation-copy.ts b/packages/runtime/src/conversation-copy.ts
index 32f154a468..ce29a964e1 100644
--- a/packages/runtime/src/conversation-copy.ts
+++ b/packages/runtime/src/conversation-copy.ts
@@ -43,12 +43,11 @@ import { TOOL_RECOVERY_DECISION_FACT_KIND } from '@maka/core/tool-recovery-fact'
 import { canonicalToolArgsHash } from '@maka/core/tool-args-identity';
 import {
   buildHistoryCompactCheckpoint,
-  historyCompactSourceDigest,
-  matchHistoryCompactCheckpointPrefix,
+  checkHistoryCompactCheckpointCurrency,
+  isHistoryCompactContentEvent,
   validateHistoryCompactCheckpointShape,
 } from './history-compact-checkpoint.js';
 import { findCheckpointSummaryDefect } from './history-compact-summary-validation.js';
-import { isHistoryCompactContentEvent } from './history-compaction.js';
 import {
   classifyTerminalRuntimeLedger,
   commitTerminalRunWithRuntimeFact,
@@ -1241,20 +1240,21 @@ function cloneAgentRunEvent(
     // under an older policy would otherwise be permanently uncopyable.
     if (!validateHistoryCompactCheckpointShape(sourceCheckpoint, event.sessionId)) return null;
     if (sourceCheckpoint.version === 3) return null;
-    const match = matchHistoryCompactCheckpointPrefix(sourceCheckpoint, sourceCompactableEvents);
-    if (match.reason) return null;
-    // Rebinding a digest must not make an already stale source summary valid.
-    const sourceEffective = reduceEffectiveModelProjections(
-      match.coveredRuntimeEvents,
-      [...clonedTransitions.keys()].map((record) =>
-        decodeModelProjectionTransition(record.data?.transition, record.sessionId),
-      ),
-    ).events;
-    if (
-      sourceCheckpoint.coverage.effectiveSourceDigest !==
-      historyCompactSourceDigest(sourceEffective)
-    )
-      return null;
+    // Rebinding a digest must not make an already stale source summary valid:
+    // the currency check covers the raw prefix match too, so a prefix that no
+    // longer matches is dropped here as well.
+    const currency = checkHistoryCompactCheckpointCurrency(
+      sourceCheckpoint,
+      sourceCompactableEvents,
+      {
+        transitions: [...clonedTransitions.keys()].map((record) =>
+          decodeModelProjectionTransition(record.data?.transition, record.sessionId),
+        ),
+        unreadableTargets: new Set<string>(),
+        unscopedUnreadable: 0,
+      },
+    );
+    if (currency.status !== 'current') return null;
     // Copy is an admission seam for the sectioned summary contract: a marked
     // checkpoint whose summary no longer satisfies the COMPLETE predicate —
     // re-runnable here on structure and truncation (the size floor needs the
@@ -1263,7 +1263,7 @@ function cloneAgentRunEvent(
     if (findCheckpointSummaryDefect(sourceCheckpoint.summary) !== undefined) {
       throw new Error(`Cannot copy invalid history compact checkpoint ${event.id}`);
     }
-    const coveredRuntimeEvents = match.coveredRuntimeEvents.map((sourceEvent) => {
+    const coveredRuntimeEvents = currency.match.coveredRuntimeEvents.map((sourceEvent) => {
       const cloned = clonedRuntimeEvents.get(sourceEvent.id);
       if (!cloned) {
         throw new Error(
diff --git a/packages/runtime/src/history-compact-checkpoint.ts b/packages/runtime/src/history-compact-checkpoint.ts
index ab28f8ed88..c8b3676fa0 100644
--- a/packages/runtime/src/history-compact-checkpoint.ts
+++ b/packages/runtime/src/history-compact-checkpoint.ts
@@ -22,6 +22,11 @@ import { Buffer } from 'node:buffer';
 import type { ExecutionLogCoverage } from '@maka/core/execution-log-coverage';
 import type { RuntimeEvent } from '@maka/core/runtime-event';
 import { nonEmpty, sha256 } from './context-budget-helpers.js';
+import { estimateRuntimeEventChars } from './model-history.js';
+import {
+  reduceEffectiveModelProjections,
+  type LoadedModelProjectionTransitions,
+} from './model-projection-transition-ledger.js';
 import type { ModelMessage } from './model-protocol.js';
 import { stableStringify } from './request-shape.js';
 import {
@@ -650,6 +655,61 @@ export function matchHistoryCompactCheckpointPrefix(
   };
 }
 
+/**
+ * The two-layer currency decision every checkpoint consumer shares (#5930): a
+ * checkpoint replays only when its RAW identity still matches the current
+ * RuntimeEvent prefix AND the EFFECTIVE, transition-folded view of the covered
+ * span still matches the digest pinned at creation. `raw_mismatch` carries the
+ * raw-match failure; `effective_history_changed` covers every way the folded
+ * view can drift — missing pin, too few surviving content events, a covered
+ * through-event the effective prefix never reaches, or a digest difference.
+ * Callers keep their own policy for each outcome; this function only decides.
+ */
+export type HistoryCompactCheckpointCurrency =
+  | {
+      status: 'current';
+      match: Extract<HistoryCompactCheckpointPrefixMatch, { reason?: undefined }>;
+    }
+  | {
+      status: 'raw_mismatch';
+      reason: 'invalid_checkpoint' | 'coverage_miss' | 'source_hash_mismatch';
+    }
+  | { status: 'effective_history_changed' };
+
+/**
+ * Judges a checkpoint against `events` under `projectionSnapshot` — the SAME
+ * loaded transition view the caller used to build the request being judged.
+ * The function never loads transitions: a checkpoint is only current for the
+ * snapshot it is checked with, and a record committed after that snapshot
+ * belongs to the next request's decision.
+ */
+export function checkHistoryCompactCheckpointCurrency(
+  checkpoint: HistoryCompactCheckpoint,
+  events: readonly RuntimeEvent[],
+  projectionSnapshot: LoadedModelProjectionTransitions,
+): HistoryCompactCheckpointCurrency {
+  const match = matchHistoryCompactCheckpointPrefix(checkpoint, events);
+  if (match.reason !== undefined) {
+    return { status: 'raw_mismatch', reason: match.reason };
+  }
+  const effectiveCovered = reduceEffectiveModelProjections(
+    match.coveredRuntimeEvents,
+    projectionSnapshot.transitions,
+    projectionSnapshot.unreadableTargets,
+  ).events.filter(isHistoryCompactContentEvent);
+  const pinned = checkpoint.coverage.effectiveSourceDigest;
+  if (
+    pinned === undefined ||
+    effectiveCovered.length < checkpoint.coverage.eventCount ||
+    effectiveCovered[checkpoint.coverage.eventCount - 1]?.id !==
+      checkpoint.coverage.through.runtimeEventId ||
+    historyCompactSourceDigest(effectiveCovered.slice(0, checkpoint.coverage.eventCount)) !== pinned
+  ) {
+    return { status: 'effective_history_changed' };
+  }
+  return { status: 'current', match };
+}
+
 /**
  * Deterministic RuntimeEvent projection for a checkpoint. V2 prepends its
  * text block; V3 stays out of the event list and is materialized directly at
@@ -799,6 +859,11 @@ function effectiveDigestEvent(event: RuntimeEvent): unknown {
   return { ...event, content: { ...identity, result } };
 }
 
+/** True when the event carries model-visible content the compact projection counts. */
+export function isHistoryCompactContentEvent(event: RuntimeEvent): boolean {
+  return event.modelVisibility !== 'hidden' && estimateRuntimeEventChars(event) > 0;
+}
+
 function validHistoryCompactProviderState(value: unknown): value is HistoryCompactProviderState {
   if (!value || typeof value !== 'object') return false;
   const state = value as Partial<HistoryCompactProviderState>;
diff --git a/packages/runtime/src/history-compaction.ts b/packages/runtime/src/history-compaction.ts
index b33dd38865..5afc0abe24 100644
--- a/packages/runtime/src/history-compaction.ts
+++ b/packages/runtime/src/history-compaction.ts
@@ -21,7 +21,7 @@ import type { RuntimeInvocationRecord } from '@maka/core/runtime-invocation';
 import type { RuntimeEvent } from '@maka/core/runtime-event';
 import type { ContextBudgetDiagnostic } from '@maka/core/usage-stats/types';
 import { finitePositive } from './context-budget-helpers.js';
-import { estimateRuntimeEventChars, estimateRuntimeEventsTokens } from './model-history.js';
+import { estimateRuntimeEventsTokens } from './model-history.js';
 import { compactionDecisionDiagnosticPatch } from './compaction-boundary.js';
 import {
   HistoryCompactSummarizerError,
@@ -30,8 +30,9 @@ import {
 import { findCheckpointSummaryDefect } from './history-compact-summary-validation.js';
 import {
   buildHistoryCompactCheckpoint,
+  checkHistoryCompactCheckpointCurrency,
   historyCompactCheckpointToRuntimeEvent,
-  historyCompactSourceDigest,
+  isHistoryCompactContentEvent,
   matchHistoryCompactCheckpointPrefix,
   midTurnHeadAnchorEvent,
   projectHistoryCompactCheckpointReplay,
@@ -39,6 +40,10 @@ import {
   type HistoryCompactMemoryExtractionBoundary,
   type HistoryCompactProviderState,
 } from './history-compact-checkpoint.js';
+import {
+  reduceEffectiveModelProjections,
+  type LoadedModelProjectionTransitions,
+} from './model-projection-transition-ledger.js';
 
 /**
  * Context compaction: the pure measurement + safe-boundary engine.
@@ -198,14 +203,16 @@ export interface PlanHistoryCompactionInput {
   /** Present only when this automatic Compaction should create a Memory task. */
   memoryExtractionBoundary?: HistoryCompactMemoryExtractionBoundary;
   /**
-   * Projects the covered span to its effective (transition-folded) view. When
-   * present, the summary is written from that view and its digest is pinned as
-   * `coverage.effectiveSourceDigest`, so a later projection transition
-   * invalidates the checkpoint instead of being restored by it (#4845 review).
+   * Loads the model-projection transition view this plan is judged and built
+   * under. Called only after a safe covered boundary exists — the ledger is
+   * never read for a fold that cannot run — and the loaded snapshot then
+   * covers the whole pass: the covered span is folded through it before
+   * summarizing, its digest is pinned as `coverage.effectiveSourceDigest` so
+   * a later projection transition invalidates the checkpoint instead of being
+   * restored by it (#4845 review), and the same view gates checkpoint
+   * roll-forward.
    */
-  projectEffectiveCoverage?: (
-    coveredRuntimeEvents: readonly RuntimeEvent[],
-  ) => Promise<readonly RuntimeEvent[]>;
+  loadProjectionSnapshot?: () => Promise<LoadedModelProjectionTransitions>;
   summarize: HistoryCompactionSummarizer;
 }
 
@@ -326,13 +333,15 @@ export async function planHistoryCompaction(
     const coveredRuntimeEvents = input.orderedEvents.slice(0, boundary.coveredCount);
     const tailRuntimeEvents = input.orderedEvents.slice(boundary.coveredCount);
 
+    // One transition view per pass: the same loaded snapshot judges the
+    // previous checkpoint's currency and produces the effective view the
+    // summary is written from.
+    const projectionSnapshot = input.loadProjectionSnapshot
+      ? await input.loadProjectionSnapshot()
+      : undefined;
+
     // Roll forward from a previous checkpoint when it is an exact prefix of the
     // covered events, so the summary only re-reads the newly folded span.
-    const checkpointMatch = input.previousCheckpoint
-      ? matchHistoryCompactCheckpointPrefix(input.previousCheckpoint, coveredRuntimeEvents)
-      : undefined;
-    let previousCheckpoint =
-      checkpointMatch && !checkpointMatch.reason ? input.previousCheckpoint : undefined;
     // Content-currency gate for roll-forward: the inherited summary or
     // provider state describes the EFFECTIVE view of the previous coverage at
     // its own creation. A projection transition committed since rewrites that
@@ -340,23 +349,37 @@ export async function planHistoryCompaction(
     // would launder it into the new checkpoint under the current effective
     // digest — later replay guards would then pass it (#4845 review). On
     // drift, discard the checkpoint and re-summarize the whole effective span.
-    if (previousCheckpoint && checkpointMatch && input.projectEffectiveCoverage) {
-      const pinned = previousCheckpoint.coverage.effectiveSourceDigest;
-      const previousEffectiveCovered = await input.projectEffectiveCoverage(
-        checkpointMatch.coveredRuntimeEvents,
+    let previousCheckpoint = input.previousCheckpoint;
+    let successorRuntimeEvents: readonly RuntimeEvent[] | undefined;
+    if (previousCheckpoint && projectionSnapshot) {
+      const currency = checkHistoryCompactCheckpointCurrency(
+        previousCheckpoint,
+        coveredRuntimeEvents,
+        projectionSnapshot,
       );
-      if (pinned === undefined || historyCompactSourceDigest(previousEffectiveCovered) !== pinned) {
+      if (currency.status === 'current') {
+        successorRuntimeEvents = currency.match.successorRuntimeEvents;
+      } else {
+        previousCheckpoint = undefined;
+      }
+    } else if (previousCheckpoint) {
+      const match = matchHistoryCompactCheckpointPrefix(previousCheckpoint, coveredRuntimeEvents);
+      if (match.reason === undefined) {
+        successorRuntimeEvents = match.successorRuntimeEvents;
+      } else {
         previousCheckpoint = undefined;
       }
     }
-    const newlyFoldedRuntimeEvents = previousCheckpoint
-      ? checkpointMatch!.successorRuntimeEvents
-      : coveredRuntimeEvents;
+    const newlyFoldedRuntimeEvents = successorRuntimeEvents ?? coveredRuntimeEvents;
 
     // The model-visible summary reads the effective (transition-folded) view
     // of the covered span; the raw events keep the coverage identity.
-    const effectiveCoveredRuntimeEvents = input.projectEffectiveCoverage
-      ? [...(await input.projectEffectiveCoverage(coveredRuntimeEvents))]
+    const effectiveCoveredRuntimeEvents = projectionSnapshot
+      ? reduceEffectiveModelProjections(
+          coveredRuntimeEvents,
+          projectionSnapshot.transitions,
+          projectionSnapshot.unreadableTargets,
+        ).events
       : undefined;
     const effectiveNewlyFoldedRuntimeEvents = effectiveCoveredRuntimeEvents
       ? newlyFoldedRuntimeEvents.length === coveredRuntimeEvents.length
@@ -543,8 +566,3 @@ export function applyRuntimeEventHistoryCompact(
     }),
   };
 }
-
-/** True when the event carries model-visible content the compact projection counts. */
-export function isHistoryCompactContentEvent(event: RuntimeEvent): boolean {
-  return event.modelVisibility !== 'hidden' && estimateRuntimeEventChars(event) > 0;
-}
diff --git a/packages/runtime/src/memory-extraction.ts b/packages/runtime/src/memory-extraction.ts
index ae7d019e73..5cbf2d32a7 100644
--- a/packages/runtime/src/memory-extraction.ts
+++ b/packages/runtime/src/memory-extraction.ts
@@ -55,8 +55,8 @@ import {
   type AdmittedProposalFields,
   type MemoryProposalItem,
 } from './memory-extraction-proposal.js';
-import { isHistoryCompactContentEvent } from './history-compaction.js';
 import {
+  isHistoryCompactContentEvent,
   isTextHistoryCompactCheckpoint,
   matchHistoryCompactCheckpointPrefix,
   renderHistoryCompactCheckpoint,


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org