 M src/daemon.js
 M src/daemon.test.js
--- diff HEAD ---
diff --git a/src/daemon.js b/src/daemon.js
index 69250f2d..55eec329 100644
--- a/src/daemon.js
+++ b/src/daemon.js
@@ -1352,14 +1352,86 @@ function quarantineRunInRegistry(pointer, harnessHome, error) {
   return quarantine;
 }
 
+const runJournalScanCache = new Map();
+
+const defaultRunJournalScanDeps = Object.freeze({
+  openJournal: stateJournal.open,
+});
+
+let runJournalScanDeps = defaultRunJournalScanDeps;
+
+function clearRunJournalScanCache() {
+  runJournalScanCache.clear();
+}
+
+function setRunJournalScanDeps(deps = {}) {
+  runJournalScanDeps = {
+    ...defaultRunJournalScanDeps,
+    ...deps,
+  };
+}
+
+function resetRunJournalScanDeps() {
+  runJournalScanDeps = defaultRunJournalScanDeps;
+  clearRunJournalScanCache();
+}
+
+function journalFileIdentity(dbPath) {
+  try {
+    const stat = fs.statSync(dbPath);
+    return `${stat.dev}:${stat.ino}`;
+  } catch (error) {
+    if (error && error.code === 'ENOENT') {
+      return null;
+    }
+    throw error;
+  }
+}
+
+function copyJournalEntries(entries) {
+  return entries.map((entry) => ({ ...entry }));
+}
+
+function readRunJournalEntriesForScan(journal, dbPath) {
+  const currentLastSeq = journal.lastSeq();
+  const identity = journalFileIdentity(dbPath);
+  let cached = runJournalScanCache.get(dbPath);
+
+  if (cached) {
+    if (identity == null || cached.identity !== identity || currentLastSeq < cached.lastSeq) {
+      runJournalScanCache.delete(dbPath);
+      cached = null;
+    }
+  }
+
+  if (!cached) {
+    const entries = journal.readAll();
+    runJournalScanCache.set(dbPath, {
+      entries,
+      lastSeq: currentLastSeq,
+      identity,
+    });
+    return copyJournalEntries(entries);
+  }
+
+  if (currentLastSeq > cached.lastSeq) {
+    cached.entries.push(...journal.readSince(cached.lastSeq));
+    cached.lastSeq = currentLastSeq;
+    cached.identity = identity;
+  }
+
+  return copyJournalEntries(cached.entries);
+}
+
 function openRunJournalForScan(pointer, harnessHome) {
   if (isJournalCorrupt(pointer)) {
     return null;
   }
   let journal;
+  const dbPath = journalDbPath(pointer.journalPath);
   try {
-    journal = stateJournal.open(journalDbPath(pointer.journalPath));
-    return { journal, entries: journal.readAll() };
+    journal = runJournalScanDeps.openJournal(dbPath);
+    return { journal, entries: readRunJournalEntriesForScan(journal, journal.dbPath || dbPath) };
   } catch (error) {
     try {
       journal?.close();
@@ -4896,4 +4968,10 @@ module.exports = {
   createDefaultFixForwardReadFixerResult,
   createDefaultFixForwardPruneWorktree,
   isFixForwardEnabled,
+  __runJournalScanTesting: {
+    openRunJournalForScan,
+    clearRunJournalScanCache,
+    setRunJournalScanDeps,
+    resetRunJournalScanDeps,
+  },
 };
diff --git a/src/daemon.test.js b/src/daemon.test.js
index bc5f2cc6..71b0e6fb 100644
--- a/src/daemon.test.js
+++ b/src/daemon.test.js
@@ -2709,3 +2709,98 @@ test('tick scheduler journals overrun once and stops petting past the hang limit
   const lastPetAt = Math.max(...pets.map((p) => p.at));
   assert.ok(lastPetAt - startedAt < 250, 'petting must stop after the hang limit so systemd can kill a hung daemon');
 });
+
+test('openRunJournalForScan reuses cached entries and only parses appended rows on later scans', () => {
+  const {
+    __runJournalScanTesting: {
+      openRunJournalForScan,
+      resetRunJournalScanDeps,
+      setRunJournalScanDeps,
+    },
+  } = require('./daemon.js');
+
+  const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), '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-cache.json');
+  const pointer = {
+    runId: 'run-cache',
+    slug: 'fixture-slug',
+    repoRoot,
+    worktree: repoRoot,
+    journalPath,
+  };
+
+  fs.mkdirSync(path.dirname(pointerPath), { recursive: true });
+  fs.mkdirSync(runstateDir, { recursive: true });
+  fs.writeFileSync(pointerPath, `${JSON.stringify(pointer, null, 2)}\n`);
+
+  let readAllCalls = 0;
+  const countingOpenJournal = (dbPath) => {
+    const handle = stateJournal.open(dbPath);
+    const readAll = handle.readAll.bind(handle);
+    handle.readAll = () => {
+      readAllCalls += 1;
+      return readAll();
+    };
+    return handle;
+  };
+
+  resetRunJournalScanDeps();
+  setRunJournalScanDeps({ openJournal: countingOpenJournal });
+
+  try {
+    const seed = stateJournal.open(journalPath);
+    try {
+      seed.append('run.start', { slug: 'fixture-slug' });
+      seed.append('task.state', { task: 't1', state: 'running' });
+    } finally {
+      seed.close();
+    }
+
+    const first = openRunJournalForScan(pointer, harnessHome);
+    assert.ok(first);
+    try {
+      assert.equal(first.entries.length, 2);
+      assert.equal(readAllCalls, 1);
+    } finally {
+      first.journal.close();
+    }
+
+    const second = openRunJournalForScan(pointer, harnessHome);
+    assert.ok(second);
+    try {
+      assert.equal(second.entries.length, 2);
+      assert.deepEqual(
+        second.entries.map((entry) => entry.kind),
+        first.entries.map((entry) => entry.kind),
+      );
+      assert.equal(readAllCalls, 1, 'unchanged journal must not call readAll again');
+    } finally {
+      second.journal.close();
+    }
+
+    const append = stateJournal.open(journalPath);
+    try {
+      append.append('task.state', { task: 't2', state: 'queued' });
+    } finally {
+      append.close();
+    }
+
+    const third = openRunJournalForScan(pointer, harnessHome);
+    assert.ok(third);
+    try {
+      assert.equal(third.entries.length, 3);
+      assert.equal(third.entries.at(-1).kind, 'task.state');
+      assert.equal(third.entries.at(-1).task, 't2');
+      assert.equal(readAllCalls, 1, 'incremental scan must append via readSince without full re-read');
+    } finally {
+      third.journal.close();
+    }
+  } finally {
+    resetRunJournalScanDeps();
+    fs.rmSync(tempRoot, { recursive: true, force: true });
+  }
+});
