Found during a deep audit of gbrain v0.42.26.0 (Postgres engine, but the same call shape exists in pglite-engine and the symptom was identical there: Timeline: 0 despite daily extraction runs).
Symptom
gbrain extract timeline --from-meetings --source db reports candidate generation (e.g. "on 1014 entity pages from 103 meetings") but timeline_entries stays at 0 rows, with no error anywhere.
Root cause (two bugs compounding)
-
src/core/postgres-engine.ts _addTimelineEntriesBatchOnce: const dates = entries.map(e => e.date); — when TimelineBatchInput.date is a JS Date (which it is whenever the caller sources dates from SQL rows, e.g. meeting.effective_date in extract-timeline-from-meetings.ts), the driver types the bound array as timestamptz, and the statement's ${dates}::text[] cast fails with:
cannot cast type timestamp with time zone to text[]
Every batch insert fails.
-
src/core/extract-timeline-from-meetings.ts flush(): the bare catch {} swallows that error, so the run completes "successfully" with 0 entries. The feature has plausibly never written a row for any user whose dates arrive as Date objects.
Fix (verified locally)
const dates = entries.map(e => e.date instanceof Date ? e.date.toISOString().slice(0, 10) : String(e.date));
plus logging the error in the flush() catch. After this one-liner, the same vault produced 3,212 timeline entries on the first run.
Also worth checking pglite-engine.ts's equivalent batch method for the same Date-binding hazard.
Found during a deep audit of gbrain v0.42.26.0 (Postgres engine, but the same call shape exists in pglite-engine and the symptom was identical there:
Timeline: 0despite daily extraction runs).Symptom
gbrain extract timeline --from-meetings --source dbreports candidate generation (e.g. "on 1014 entity pages from 103 meetings") buttimeline_entriesstays at 0 rows, with no error anywhere.Root cause (two bugs compounding)
src/core/postgres-engine.ts_addTimelineEntriesBatchOnce:const dates = entries.map(e => e.date);— whenTimelineBatchInput.dateis a JSDate(which it is whenever the caller sources dates from SQL rows, e.g.meeting.effective_dateinextract-timeline-from-meetings.ts), the driver types the bound array astimestamptz, and the statement's${dates}::text[]cast fails with:Every batch insert fails.
src/core/extract-timeline-from-meetings.tsflush(): the barecatch {}swallows that error, so the run completes "successfully" with 0 entries. The feature has plausibly never written a row for any user whose dates arrive as Date objects.Fix (verified locally)
plus logging the error in the
flush()catch. After this one-liner, the same vault produced 3,212 timeline entries on the first run.Also worth checking
pglite-engine.ts's equivalent batch method for the same Date-binding hazard.