 M src/daemon.js
 M src/daemon.test.js
--- diff HEAD ---
diff --git a/src/daemon.js b/src/daemon.js
index 69250f2d..adffb466 100644
--- a/src/daemon.js
+++ b/src/daemon.js
@@ -1352,14 +1352,116 @@ function quarantineRunInRegistry(pointer, harnessHome, error) {
   return quarantine;
 }
 
+const runJournalScanCache = new Map();
+let runJournalScanSeam = null;
+
+function clearRunJournalScanCache() {
+  runJournalScanCache.clear();
+}
+
+function setRunJournalScanSeam(seam) {
+  runJournalScanSeam = seam && typeof seam === 'object' ? seam : null;
+}
+
+function resetRunJournalScanSeam() {
+  runJournalScanSeam = 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 journalFileIdentityMatches(left, right) {
+  return Boolean(left && right && left.dev === right.dev && left.ino === right.ino);
+}
+
+function snapshotJournalEntries(entries) {
+  return entries.map((entry) => ({ ...entry }));
+}
+
+function readJournalEntriesForScan(journal, dbPath) {
+  const resolvedPath = path.resolve(String(dbPath));
+  const identity = readJournalFileIdentity(resolvedPath);
+  if (!identity) {
+    if (runJournalScanSeam) {
+      runJournalScanSeam.readAll += 1;
+    }
+    const entries = journal.readAll();
+    runJournalScanCache.delete(resolvedPath);
+    return snapshotJournalEntries(entries);
+  }
+
+  const currentLastSeq = typeof journal.lastSeq === 'function' ? journal.lastSeq() : 0;
+  const cached = runJournalScanCache.get(resolvedPath);
+  const cacheInvalid = !cached
+    || !journalFileIdentityMatches(cached.identity, identity)
+    || currentLastSeq < cached.lastSeq;
+
+  if (cacheInvalid) {
+    if (runJournalScanSeam) {
+      runJournalScanSeam.readAll += 1;
+    }
+    const entries = journal.readAll();
+    runJournalScanCache.set(resolvedPath, {
+      entries: snapshotJournalEntries(entries),
+      lastSeq: currentLastSeq,
+      identity,
+    });
+    return snapshotJournalEntries(entries);
+  }
+
+  if (currentLastSeq === cached.lastSeq) {
+    return snapshotJournalEntries(cached.entries);
+  }
+
+  if (runJournalScanSeam) {
+    runJournalScanSeam.readSince += 1;
+  }
+  const newEvents = typeof journal.readSince === 'function'
+    ? journal.readSince(cached.lastSeq)
+    : [];
+  if (newEvents.length > 0 && newEvents[0].seq !== cached.lastSeq + 1) {
+    if (runJournalScanSeam) {
+      runJournalScanSeam.readSince -= 1;
+      runJournalScanSeam.readAll += 1;
+    }
+    const entries = journal.readAll();
+    runJournalScanCache.set(resolvedPath, {
+      entries: snapshotJournalEntries(entries),
+      lastSeq: currentLastSeq,
+      identity,
+    });
+    return snapshotJournalEntries(entries);
+  }
+
+  for (const event of newEvents) {
+    cached.entries.push({ ...event });
+  }
+  cached.lastSeq = currentLastSeq;
+  cached.identity = identity;
+  return snapshotJournalEntries(cached.entries);
+}
+
 function openRunJournalForScan(pointer, harnessHome) {
   if (isJournalCorrupt(pointer)) {
     return null;
   }
   let journal;
   try {
-    journal = stateJournal.open(journalDbPath(pointer.journalPath));
-    return { journal, entries: journal.readAll() };
+    const dbPath = journalDbPath(pointer.journalPath);
+    journal = stateJournal.open(dbPath);
+    return { journal, entries: readJournalEntriesForScan(journal, dbPath) };
   } catch (error) {
     try {
       journal?.close();
@@ -4896,4 +4998,8 @@ module.exports = {
   createDefaultFixForwardReadFixerResult,
   createDefaultFixForwardPruneWorktree,
   isFixForwardEnabled,
+  clearRunJournalScanCache,
+  openRunJournalForScan,
+  resetRunJournalScanSeam,
+  setRunJournalScanSeam,
 };
diff --git a/src/daemon.test.js b/src/daemon.test.js
index bc5f2cc6..223440a7 100644
--- a/src/daemon.test.js
+++ b/src/daemon.test.js
@@ -29,6 +29,10 @@ const {
   enforceHungRunWatchdog,
   findPendingHungDetection,
   createMemoryJournal,
+  clearRunJournalScanCache,
+  openRunJournalForScan,
+  resetRunJournalScanSeam,
+  setRunJournalScanSeam,
   defaultKillRun,
   handleControlCommand,
   probeSpawnSupport,
@@ -1747,6 +1751,88 @@ test('watchdog tick detects dead runner pid and journals run.hung-detected witho
   fs.rmSync(tempRoot, { recursive: true, force: true });
 });
 
+test('openRunJournalForScan reuses cached journal rows and only reads appended events 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');
+  const pointer = {
+    runId: 'run-123',
+    slug: 'fixture-slug',
+    repoRoot,
+    worktree: repoRoot,
+    journalPath,
+    plane: 'runner',
+    ts: '2026-07-12T00:00:00.000Z',
+  };
+
+  fs.mkdirSync(path.dirname(pointerPath), { recursive: true });
+  fs.mkdirSync(runstateDir, { recursive: true });
+  fs.writeFileSync(pointerPath, `${JSON.stringify(pointer, null, 2)}\n`);
+
+  const journal = stateJournal.open(journalPath);
+  try {
+    journal.append('run.start', { slug: 'fixture-slug' });
+    journal.append('task.state', { task: 'w1.t1', state: 'leased' });
+  } finally {
+    journal.close();
+  }
+
+  clearRunJournalScanCache();
+  const seam = { readAll: 0, readSince: 0 };
+  setRunJournalScanSeam(seam);
+
+  try {
+    const first = openRunJournalForScan(pointer, harnessHome);
+    assert.ok(first);
+    try {
+      assert.equal(first.entries.length, 2);
+      assert.equal(seam.readAll, 1);
+      assert.equal(seam.readSince, 0);
+    } 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(seam.readAll, 1, 'unchanged journal must not re-parse existing rows');
+      assert.equal(seam.readSince, 0);
+    } finally {
+      second.journal.close();
+    }
+
+    const reopened = stateJournal.open(journalPath);
+    try {
+      reopened.append('task.state', { task: 'w1.t2', state: 'leased' });
+    } finally {
+      reopened.close();
+    }
+
+    const third = openRunJournalForScan(pointer, harnessHome);
+    assert.ok(third);
+    try {
+      assert.equal(third.entries.length, 3);
+      assert.equal(third.entries.at(-1).task, 'w1.t2');
+      assert.equal(seam.readAll, 1);
+      assert.equal(seam.readSince, 1, 'appended rows are picked up incrementally');
+    } finally {
+      third.journal.close();
+    }
+  } finally {
+    resetRunJournalScanSeam();
+    clearRunJournalScanCache();
+    fs.rmSync(tempRoot, { recursive: true, force: true });
+  }
+});
+
 test('watchdog tick honors config.watchdog.runHeartbeatStaleMs override', async () => {
   const tempRoot = fs.mkdtempSync(path.join('/tmp', 'daemon-watchdog-config-'));
   const harnessHome = path.join(tempRoot, 'home');
