@@ -3,12 +3,44 @@ import {
33 markNodesAsRead
44} from '../../bridge/queries.mjs' ;
55
6+ /**
7+ * Reads pending low-latency summarization markers from the coordinator table.
8+ *
9+ * @summary Keeps the scheduler's pending-lane check cheap: it only needs to know
10+ * whether pending rows exist, while the spawned summary child remains responsible
11+ * for lease-claiming and draining the actual jobs.
12+ * @param {Object } db SQLite database handle.
13+ * @param {Object } [options]
14+ * @param {Number } [options.limit=50] Maximum marker ids to return.
15+ * @returns {String[] }
16+ */
17+ export function getPendingSummarizationJobs ( db , { limit = 50 } = { } ) {
18+ if ( ! db ?. prepare ) {
19+ return [ ] ;
20+ }
21+
22+ const numericLimit = Number . isInteger ( limit ) && limit > 0 ? limit : 50 ;
23+
24+ try {
25+ return db . prepare ( `
26+ SELECT session_id
27+ FROM SummarizationJobs
28+ WHERE status = 'pending'
29+ ORDER BY rowid ASC
30+ LIMIT ?
31+ ` ) . all ( numericLimit ) . map ( row => row . session_id ) . filter ( Boolean ) ;
32+ } catch {
33+ return [ ] ;
34+ }
35+ }
36+
637/**
738 * Builds the task trigger for the summarization sweep lane.
839 *
9- * Two wake-up sources, in priority order:
40+ * Three wake-up sources, in priority order:
1041 * 1. Unread sunset handovers — priority because they unblock the next agent boot
11- * 2. Periodic sweep — fallback for ordinary unsummarized sessions
42+ * 2. Pending disconnect markers — targeted low-latency session close handling
43+ * 3. Periodic sweep — fallback for ordinary unsummarized sessions
1244 *
1345 * Pure function. Test the scheduling contract without mounting the SQLite graph.
1446 *
@@ -17,9 +49,10 @@ import {
1749 * @param {Number } options.lastRunAt Last summary task start timestamp.
1850 * @param {Number } options.intervalMs Periodic sweep interval; `0` disables the interval source.
1951 * @param {Object[] } [options.handovers=[]] Unread sunset-handover message nodes.
52+ * @param {String[] } [options.pendingJobs=[]] Pending SummarizationJobs session ids.
2053 * @returns {Object|null } A summary task trigger or null when no work is due.
2154 */
22- export function buildSummaryTrigger ( { now, lastRunAt, intervalMs, handovers = [ ] } ) {
55+ export function buildSummaryTrigger ( { now, lastRunAt, intervalMs, handovers = [ ] , pendingJobs = [ ] } ) {
2356 if ( handovers . length > 0 ) {
2457 return {
2558 taskName : 'summary' ,
@@ -29,6 +62,15 @@ export function buildSummaryTrigger({now, lastRunAt, intervalMs, handovers = []}
2962 } ;
3063 }
3164
65+ if ( pendingJobs . length > 0 ) {
66+ return {
67+ taskName : 'summary' ,
68+ source : 'pending-summarization' ,
69+ reason : `pending-summarization:${ pendingJobs . length } ` ,
70+ pendingCount : pendingJobs . length
71+ } ;
72+ }
73+
3274 if ( intervalMs > 0 && now - lastRunAt >= intervalMs ) {
3375 return {
3476 taskName : 'summary' ,
@@ -50,6 +92,7 @@ export function buildSummaryTrigger({now, lastRunAt, intervalMs, handovers = []}
5092 * @param {Number } options.now Current timestamp in milliseconds.
5193 * @param {Number } options.summarySweepIntervalMs Periodic summary sweep interval.
5294 * @param {Function } [options.getUnreadSunsetHandoversFn] Test seam for handover reads.
95+ * @param {Function } [options.getPendingSummarizationJobsFn] Test seam for pending marker reads.
5396 * @param {Function } [options.markNodesAsReadFn] Test seam for handover mark-read writes.
5497 * @param {Function } [options.log] Optional orchestrator log function.
5598 * @returns {Object|null } Task trigger with optional `onSuccess` callback, or null.
@@ -60,13 +103,16 @@ export function getDueTask({
60103 now,
61104 summarySweepIntervalMs,
62105 getUnreadSunsetHandoversFn = getUnreadSunsetHandovers ,
106+ getPendingSummarizationJobsFn = getPendingSummarizationJobs ,
63107 markNodesAsReadFn = markNodesAsRead ,
64108 log
65109} ) {
66110 const handovers = getUnreadSunsetHandoversFn ( db ) ;
111+ const pendingJobs = handovers . length > 0 ? [ ] : getPendingSummarizationJobsFn ( db ) ;
67112 const trigger = buildSummaryTrigger ( {
68113 now,
69114 handovers,
115+ pendingJobs,
70116 intervalMs : summarySweepIntervalMs ,
71117 lastRunAt : state . summary ?. lastRunAt || 0
72118 } ) ;
0 commit comments