diff --git a/src/deriver/enqueue.py b/src/deriver/enqueue.py index 71b00a2b..0e323779 100644 --- a/src/deriver/enqueue.py +++ b/src/deriver/enqueue.py @@ -1,5 +1,4 @@ import logging -from datetime import datetime, timezone from typing import Any, Literal from sqlalchemy import exists, insert, select @@ -516,8 +515,8 @@ async def enqueue_dream( stmt = insert(QueueItem).returning(QueueItem) await db_session.execute(stmt, [dream_record]) - # Update collection metadata (CRUD handles cache invalidation) - now_iso = datetime.now(timezone.utc).isoformat() + # Update collection metadata (CRUD handles cache invalidation). + # last_dream_at is written at completion in process_dream, not here. await crud.update_collection_internal_metadata( db_session, workspace_name, @@ -526,7 +525,6 @@ async def enqueue_dream( update_data={ "dream": { "last_dream_document_count": document_count, - "last_dream_at": now_iso, } }, ) diff --git a/src/dreamer/dream_scheduler.py b/src/dreamer/dream_scheduler.py index 1a339d21..a0e05f1a 100644 --- a/src/dreamer/dream_scheduler.py +++ b/src/dreamer/dream_scheduler.py @@ -254,11 +254,13 @@ async def check_and_schedule_dream( last_dream_document_count = dream_metadata.get("last_dream_document_count", 0) last_dream_at = dream_metadata.get("last_dream_at") - # Count current documents in the collection + # Count explicit-level docs only: dreamer output (deductive/inductive/ + # contradiction) would inflate the threshold and create a feedback loop. count_stmt = select(func.count(models.Document.id)).where( models.Document.workspace_name == collection.workspace_name, models.Document.observer == collection.observer, models.Document.observed == collection.observed, + models.Document.level == "explicit", ) current_document_count = int(await db.scalar(count_stmt) or 0) diff --git a/src/dreamer/orchestrator.py b/src/dreamer/orchestrator.py index 000d45a6..14678ff6 100644 --- a/src/dreamer/orchestrator.py +++ b/src/dreamer/orchestrator.py @@ -17,6 +17,7 @@ import logging import time import uuid from dataclasses import dataclass +from datetime import datetime, timezone from typing import Any import sentry_sdk @@ -323,6 +324,18 @@ DREAM: {payload.dream_type} documents for {workspace_name}/{payload.observer}/{p + f"duration={result.total_duration_ms:.0f}ms" ) + # Write last_dream_at at completion (not enqueue) so duplicate + # enqueues can't reset the 8h guard. Lenient: any non-null result. + now_iso = datetime.now(timezone.utc).isoformat() + async with tracked_db("dream.last_dream_at_write") as db: + await crud.update_collection_internal_metadata( + db, + workspace_name, + payload.observer, + payload.observed, + update_data={"dream": {"last_dream_at": now_iso}}, + ) + except Exception as e: logger.error( f"Error processing dream task {payload.dream_type} for {payload.observer}/{payload.observed}: {str(e)}",