 M src/daemon.js
 M src/daemon.test.js
--- diff HEAD ---
diff --git a/src/daemon.js b/src/daemon.js
index 69250f2d..195bf732 100644
--- a/src/daemon.js
+++ b/src/daemon.js
@@ -1278,6 +1278,94 @@ function journalDbPath(journalPath) {
   return `${resolved}.db`;
 }
 
+const runJournalScanCache = new Map();
+let journalScanReadSeam = null;
+
+function readJournalFileIdentity(dbPath) {
+  try {
+    const stat = fs.statSync(dbPath);
+    return { dev: stat.dev, ino: stat.ino };
+  } catch (error) {
+    if (error && error.code === 'ENOENT') {
+      return null;
+    }
+    throw error;
+  }
+}
+
+function journalFileIdentitiesMatch(left, right) {
+  if (!left || !right) {
+    return false;
+  }
+  return left.dev === right.dev && left.ino === right.ino;
+}
+
+function callJournalReadAllForScan(journal) {
+  if (journalScanReadSeam) {
+    journalScanReadSeam.readAllCalls += 1;
+  }
+  return journal.readAll();
+}
+
+function callJournalReadSinceForScan(journal, seq) {
+  if (journalScanReadSeam) {
+    journalScanReadSeam.readSinceCalls += 1;
+  }
+  return journal.readSince(seq);
+}
+
+function readRunJournalEntriesForScan(journal, dbPath) {
+  const identity = readJournalFileIdentity(dbPath);
+  const currentLastSeq = typeof journal.lastSeq === 'function' ? journal.lastSeq() : 0;
+  const cached = runJournalScanCache.get(dbPath);
+
+  if (cached) {
+    if (!journalFileIdentitiesMatch(cached.fileIdentity, identity) || currentLastSeq < cached.lastSeq) {
+      runJournalScanCache.delete(dbPath);
+    }
+  }
+
+  const activeCache = runJournalScanCache.get(dbPath);
+  if (activeCache) {
+    if (currentLastSeq === activeCache.lastSeq) {
+      return [...activeCache.entries];
+    }
+    if (currentLastSeq > activeCache.lastSeq) {
+      const appended = typeof journal.readSince === 'function'
+        ? callJournalReadSinceForScan(journal, activeCache.lastSeq)
+        : callJournalReadAllForScan(journal).filter((entry) => entry.seq > activeCache.lastSeq);
+      const entries = activeCache.entries.concat(appended);
+      runJournalScanCache.set(dbPath, {
+        fileIdentity: identity,
+        lastSeq: currentLastSeq,
+        entries,
+      });
+      return [...entries];
+    }
+  }
+
+  const entries = callJournalReadAllForScan(journal);
+  runJournalScanCache.set(dbPath, {
+    fileIdentity: identity,
+    lastSeq: currentLastSeq,
+    entries,
+  });
+  return [...entries];
+}
+
+function resetRunJournalScanCacheForTests() {
+  runJournalScanCache.clear();
+  journalScanReadSeam = null;
+}
+
+function setJournalScanReadSeamForTests(seam) {
+  journalScanReadSeam = seam || null;
+  if (journalScanReadSeam) {
+    journalScanReadSeam.readAllCalls = journalScanReadSeam.readAllCalls || 0;
+    journalScanReadSeam.readSinceCalls = journalScanReadSeam.readSinceCalls || 0;
+  }
+}
+
 function logDaemonEvent(event, payload = {}) {
   process.stderr.write(`${JSON.stringify({ ts: new Date().toISOString(), event, ...payload })}\n`);
 }
@@ -1356,10 +1444,11 @@ 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: readRunJournalEntriesForScan(journal, dbPath) };
   } catch (error) {
     try {
       journal?.close();
@@ -4896,4 +4985,7 @@ module.exports = {
   createDefaultFixForwardReadFixerResult,
   createDefaultFixForwardPruneWorktree,
   isFixForwardEnabled,
+  openRunJournalForScan,
+  resetRunJournalScanCacheForTests,
+  setJournalScanReadSeamForTests,
 };
diff --git a/src/daemon.test.js b/src/daemon.test.js
index bc5f2cc6..caf4c0b9 100644
--- a/src/daemon.test.js
+++ b/src/daemon.test.js
@@ -31,8 +31,10 @@ const {
   createMemoryJournal,
   defaultKillRun,
   handleControlCommand,
+  openRunJournalForScan,
   probeSpawnSupport,
   promoteQueuedRuns,
+  resetRunJournalScanCacheForTests,
   runAdapterHealthTick,
   reconcileDeadRunsOnBoot,
   reattachRun,
@@ -40,6 +42,7 @@ const {
   runDeliveryControllerTick,
   resolveRun,
   sendControlCommandToCurrentDaemon,
+  setJournalScanReadSeamForTests,
   transitionRunState,
   writeStatusCache,
   startDaemon,
@@ -2709,3 +2712,56 @@ 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 incrementally reads run journals across daemon ticks', () => {
+  resetRunJournalScanCacheForTests();
+  const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'daemon-journal-scan-cache-'));
+  const harnessHome = path.join(tempRoot, 'home');
+  const journalPath = path.join(tempRoot, 'fixture-slug.db');
+  const pointer = {
+    runId: 'run-cache',
+    slug: 'fixture-slug',
+    journalPath,
+  };
+
+  const seed = stateJournal.open(journalPath);
+  try {
+    seed.append('task.start', { task: 't1' });
+    seed.append('task.done', { task: 't1' });
+  } finally {
+    seed.close();
+  }
+
+  const seam = { readAllCalls: 0, readSinceCalls: 0 };
+  setJournalScanReadSeamForTests(seam);
+
+  const first = openRunJournalForScan(pointer, harnessHome);
+  assert.equal(seam.readAllCalls, 1);
+  assert.equal(seam.readSinceCalls, 0);
+  assert.equal(first.entries.length, 2);
+  first.journal.close();
+
+  const second = openRunJournalForScan(pointer, harnessHome);
+  assert.equal(seam.readAllCalls, 1, 'unchanged journal must not call readAll again');
+  assert.equal(seam.readSinceCalls, 0, 'unchanged journal must not call readSince');
+  assert.equal(second.entries.length, 2);
+  second.journal.close();
+
+  const append = stateJournal.open(journalPath);
+  try {
+    append.append('task.start', { task: 't2' });
+  } finally {
+    append.close();
+  }
+
+  const third = openRunJournalForScan(pointer, harnessHome);
+  assert.equal(seam.readAllCalls, 1, 'incremental scan must not call readAll');
+  assert.equal(seam.readSinceCalls, 1, 'new rows must be read via readSince');
+  assert.equal(third.entries.length, 3);
+  assert.equal(third.entries[2].kind, 'task.start');
+  assert.equal(third.entries[2].task, 't2');
+  third.journal.close();
+
+  resetRunJournalScanCacheForTests();
+  fs.rmSync(tempRoot, { recursive: true, force: true });
+});
