feat(cron): Phase 2.5 — Roux intent decay cron - #115
Conversation
…pired)
Adds a daily decay sweep for stale 'pending' roux_ingest intents that have
outlived their 30-day TTL. Caps unbounded queue growth from Gmail ingest
sources the user never triages.
- migration 0018: cc_intents.expires_at + decayed_at columns, partial scan
index, refreshed message_id uniqueness guard that frees the slot when an
intent decays to 'expired' (allowing future re-ingest if the source comes
back online).
- src/lib/intent-decay.ts: decayStaleRouxIntents(sql, {ttlDays, batchLimit})
+ computeRouxExpiresAt helper. Atomic CTE-bounded UPDATE; concurrent-safe.
- meta/intent.ts: IntentStatus union gains 'expired'.
- src/lib/cron.ts: Phase 12 in the daily_api cron source.
- tests/meta/intent-decay.spec.ts: real-Neon integration covering empty case,
stale vs. fresh, non-roux_ingest skip, dispatched skip, batchLimit cap, plus
unit tests for computeRouxExpiresAt.
Validated against Neon branch test-intent-decay-0018 on project cool-bar-13270800:
all 8 tests pass; typecheck clean.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThis PR implements a complete intent decay system for managing stale Roux intents. It introduces an ChangesRoux Intent Decay Pipeline
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
To use Codex here, create a Codex account and connect to github. |
…LICT (#117) PR #115 / migration 0018 added `AND status <> 'expired'` to the partial unique index `cc_intents_roux_ingest_message_id_uidx` but did not update the upsert's ON CONFLICT WHERE clause in createRouxIngestIntentIdempotent. PostgreSQL requires predicate equality to match an index to ON CONFLICT — the mismatch caused the upsert to fail with "no unique or exclusion constraint matching the ON CONFLICT specification", breaking the Roux ingest path. Verified against a fresh Neon branch: - pre-fix: SQLSTATE 42P10 (no matching constraint) - post-fix: ON CONFLICT resolves cleanly to the index Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…LICT (#118) PR #115 / migration 0018 added `AND status <> 'expired'` to the partial unique index `cc_intents_roux_ingest_message_id_uidx` but did not update the upsert's ON CONFLICT WHERE clause in createRouxIngestIntentIdempotent. PostgreSQL requires predicate equality to match an index to ON CONFLICT — the mismatch caused the upsert to fail with "no unique or exclusion constraint matching the ON CONFLICT specification", breaking the Roux ingest path. Verified against a fresh Neon branch: - pre-fix: SQLSTATE 42P10 (no matching constraint) - post-fix: ON CONFLICT resolves cleanly to the index Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Phase 2.5 of ChittyRoux × Workspace Studio. Adds a daily decay sweep that expires stale
pendingroux_ingestintents (30-day TTL), preventing unbounded queue growth from Gmail ingest sources the user never triages.cc_intents.expires_at+decayed_atcolumns, partial scan index, refreshedmessage_iduniqueness guard (excludesexpiredso future re-ingest is allowed if the source comes back online).src/lib/intent-decay.ts—decayStaleRouxIntents(sql, {ttlDays, batchLimit})andcomputeRouxExpiresAt(createdAt, ttlDays). Atomic CTE-bounded UPDATE; concurrent-safe across cron leaders. Defaults: 30 days, 500 row batch.meta/intent.ts—IntentStatusunion gains'expired'.src/lib/cron.ts— Phase 12 wired into thedaily_apicron source.tests/meta/intent-decay.spec.ts— Real-Neon integration: empty case, stale vs. fresh, non-roux_ingestskip, dispatched-skip, batchLimit cap, plus unit tests forcomputeRouxExpiresAt.Validation
Validated against Neon branch
test-intent-decay-0018(projectcool-bar-13270800):expires_at TIMESTAMPTZ,decayed_at TIMESTAMPTZ.idx_cc_intents_decay_scan(partial),cc_intents_roux_ingest_message_id_uidx(partial unique, excludesexpired).roux_ingestexpired withdecayed_atstamped; 1-day-oldroux_ingestand 60-day-oldnoopboth untouched.npx vitest run tests/meta/intent-decay.spec.ts— 8/8 pass.npx tsc --noEmit— clean.main(workspace-studio-ingest + leader specs) and unrelated to this change.Note on
executeIntentterminal-checkThe blueprint asked to extend
executeIntent's terminal-state guard with|| intent.status === 'expired'.executeIntentdoes not exist onmainyet (it lives on the unmergedfix/real-health-probe-and-tail-consumerfamily). Adding the'expired'value to theIntentStatusunion is the load-bearing part — any futureexecuteIntentintroduced on top of this will naturally compare against the full union. Deviation flagged.Test plan
pendingroux_ingestrows with no dispatched_task_id.batchLimitcaps the result; second pass picks up the remainder.computeRouxExpiresAtmath correct for default + custom ttlDays.tsc --noEmitclean.🤖 Generated with Claude Code
Summary by CodeRabbit