 M src/daemon.js
 M src/daemon.test.js
--- diff HEAD ---
diff --git a/src/daemon.js b/src/daemon.js
index 69250f2d..bd592430 100644
--- a/src/daemon.js
+++ b/src/daemon.js
@@ -1352,15 +1352,94 @@ function quarantineRunInRegistry(pointer, harnessHome, error) {
   return quarantine;
 }
 
+const runJournalEntryCache = new Map();
+let journalReadAllForScanHook = null;
+
+function clearRunJournalEntryCache() {
+  runJournalEntryCache.clear();
+}
+
+function setJournalReadAllForScanHook(hook) {
+  journalReadAllForScanHook = typeof hook === 'function' ? hook : null;
+}
+
+function journalFileIdentity(dbPath) {
+  try {
+    const stat = fs.statSync(dbPath);
+    return {
+      dev: stat.dev,
+      ino: stat.ino,
+    };
+  } catch (_error) {
+    return null;
+  }
+}
+
+function journalFileIdentitiesMatch(left, right) {
+  return Boolean(left && right && left.dev === right.dev && left.ino === right.ino);
+}
+
+function readJournalEntriesForScan(journal, dbPath) {
+  const readAllWithHook = () => {
+    if (typeof journalReadAllForScanHook === 'function') {
+      journalReadAllForScanHook();
+    }
+    return journal.readAll();
+  };
+
+  const identity = journalFileIdentity(dbPath);
+  if (!identity) {
+    runJournalEntryCache.delete(dbPath);
+    return readAllWithHook();
+  }
+
+  let cache = runJournalEntryCache.get(dbPath);
+  if (!cache || !journalFileIdentitiesMatch(cache.identity, identity)) {
+    const entries = readAllWithHook();
+    cache = {
+      identity,
+      entries,
+      lastSeq: journal.lastSeq(),
+    };
+    runJournalEntryCache.set(dbPath, cache);
+    return [...cache.entries];
+  }
+
+  const currentLastSeq = journal.lastSeq();
+  if (currentLastSeq < cache.lastSeq) {
+    const entries = readAllWithHook();
+    cache = {
+      identity,
+      entries,
+      lastSeq: currentLastSeq,
+    };
+    runJournalEntryCache.set(dbPath, cache);
+    return [...cache.entries];
+  }
+
+  if (currentLastSeq > cache.lastSeq) {
+    const delta = journal.readSince(cache.lastSeq);
+    if (delta.length > 0) {
+      cache.entries.push(...delta);
+      cache.lastSeq = currentLastSeq;
+      cache.identity = identity;
+    }
+  }
+
+  return [...cache.entries];
+}
+
 function openRunJournalForScan(pointer, harnessHome) {
   if (isJournalCorrupt(pointer)) {
     return null;
   }
+  const dbPath = journalDbPath(pointer.journalPath);
   let journal;
   try {
-    journal = stateJournal.open(journalDbPath(pointer.journalPath));
-    return { journal, entries: journal.readAll() };
+    journal = stateJournal.open(dbPath);
+    return { journal, entries: readJournalEntriesForScan(journal, dbPath) };
   } catch (error) {
+    runJournalEntryCache.delete(dbPath);
     try {
       journal?.close();
     } catch (_closeError) {
@@ -4891,6 +4970,8 @@ module.exports = {
   writeStatusCache,
   runFixForwardTickStep,
   createTickScheduler,
+  clearRunJournalEntryCache,
+  setJournalReadAllForScanHook,
   createDefaultFixForwardGhAdapter,
   createDefaultFixForwardDispatchFixer,
   createDefaultFixForwardReadFixerResult,
diff --git a/src/daemon.test.js b/src/daemon.test.js
index bc5f2cc6..f9202fc5 100644
--- a/src/daemon.test.js
+++ b/src/daemon.test.js
@@ -42,6 +42,8 @@ const {
   sendControlCommandToCurrentDaemon,
   transitionRunState,
   writeStatusCache,
+  clearRunJournalEntryCache,
+  setJournalReadAllForScanHook,
   startDaemon,
   systemdNotify,
 } = require('./daemon.js');
@@ -2687,6 +2689,66 @@ test('tick scheduler pets the watchdog while a slow tick is in flight and never
   assert.ok(pets.filter((p) => p === 'WATCHDOG=1').length >= 8, `pet must fire during slow ticks, got ${pets.length}`);
 });
 
+test('openRunJournalForScan caches journal entries and only parses appended rows on later scans', async () => {
+  const tempRoot = fs.mkdtempSync(path.join('/tmp', 'daemon-journal-scan-cache-'));
+  const harnessHome = path.join(tempRoot, 'home');
+  const repoRoot = path.join(tempRoot, 'repo');
+  const runstateDir = path.join(repoRoot, 'runstate');
+  const journalPath = path.join(runstateDir, 'fixture-slug.db');
+  const pointerPath = path.join(harnessHome, 'runs', 'run-123.json');
+
+  fs.mkdirSync(path.dirname(pointerPath), { recursive: true });
+  fs.mkdirSync(runstateDir, { recursive: true });
+  fs.writeFileSync(pointerPath, `${JSON.stringify({
+    runId: 'run-123',
+    slug: 'fixture-slug',
+    repoRoot,
+    worktree: repoRoot,
+    journalPath,
+    plane: 'runner',
+    ts: '2026-07-10T00:00:00.000Z',
+  }, null, 2)}\n`);
+
+  const journal = stateJournal.open(journalPath);
+  try {
+    journal.append('run.start', { slug: 'fixture-slug' });
+    journal.append('task.usage', { task: 'w1.t1', costUsd: 0.25 });
+  } finally {
+    journal.close();
+  }
+
+  let readAllCount = 0;
+  clearRunJournalEntryCache();
+  setJournalReadAllForScanHook(() => {
+    readAllCount += 1;
+  });
+
+  try {
+    const first = await writeStatusCache({ harnessHome, now: '2026-07-10T12:00:00.000Z' });
+    assert.equal(readAllCount, 1, 'first scan must fully parse the journal once');
+    assert.equal(first.runs[0].totalCostUsd, 0.25);
+
+    const second = await writeStatusCache({ harnessHome, now: '2026-07-10T12:00:15.000Z' });
+    assert.equal(readAllCount, 1, 'unchanged journal must not re-parse existing rows');
+    assert.equal(second.runs[0].totalCostUsd, 0.25);
+
+    const reopened = stateJournal.open(journalPath);
+    try {
+      reopened.append('task.usage', { task: 'w1.t2', costUsd: 0.75 });
+    } finally {
+      reopened.close();
+    }
+
+    const third = await writeStatusCache({ harnessHome, now: '2026-07-10T12:00:30.000Z' });
+    assert.equal(readAllCount, 1, 'incremental scan must append only new rows without readAll');
+    assert.equal(third.runs[0].totalCostUsd, 1);
+  } finally {
+    setJournalReadAllForScanHook(null);
+    clearRunJournalEntryCache();
+    fs.rmSync(tempRoot, { recursive: true, force: true });
+  }
+});
+
 test('tick scheduler journals overrun once and stops petting past the hang limit', async () => {
   const { createTickScheduler } = require('./daemon.js');
   const pets = [];
