fix(dreamer): threshold and time-guard semantics

Finding 2: filter count_stmt on documents.level == 'explicit' in
check_and_schedule_dream. Dreamer-created levels (deductive, inductive,
contradiction) are consolidation output, not input, and would otherwise
inflate the threshold count and create a feedback loop.

Finding 3 (code-level): relocate last_dream_at write from enqueue_dream
(enqueue.py) to process_dream (orchestrator.py), inside the
'if result is not None' block. Duplicate enqueues can no longer reset
the 8-hour time guard clock. Failed/never-run dreams don't advance it.

Success criteria: lenient (any non-null DreamResult counts). Pending
Vineeth confirmation — will adjust to strict/middle if requested.

Tests pending in follow-up commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
lilyplasticlabs 2026-04-17 15:13:43 -04:00
parent 952435848c
commit 1487cd269f
3 changed files with 18 additions and 5 deletions

View File

@ -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,
}
},
)

View File

@ -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)

View File

@ -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)}",