Skip to content

feat(dream): configurable dream.cycle_timeout (hot) - #32

Open
W4-NERF wants to merge 1 commit into
GottZ:rootfrom
W4-NERF:feat/dream-cycle-timeout-config
Open

feat(dream): configurable dream.cycle_timeout (hot)#32
W4-NERF wants to merge 1 commit into
GottZ:rootfrom
W4-NERF:feat/dream-cycle-timeout-config

Conversation

@W4-NERF

@W4-NERF W4-NERF commented Aug 20, 2026

Copy link
Copy Markdown

Summary

Make the whole dream cycle timeout configurable via a hot setting (dream.cycle_timeout), mirroring dream.temporal_timeout (PR #23). Today the cycle runs under a hardcoded CycleTimeout const (700s); this PR makes it a live, restart-free knob.

Why it matters

The entire dream cycle — pick → temporal → keywords → RRF → eval → recurrence → quality-score → cooldown — runs under a single context.WithTimeout(ctx, CycleTimeout). On slow reasoning models this const is too tight:

  • Qwen3.8-27B-NVFP4 runs the eval call alone for ~600–690s on full 16–20-candidate prompts.
  • At a 700s cycle cap, the eval fills the budget, so the remaining steps time out inside the cycle — the cascade recurrence phase 2 failedupdate quality score failed → (no cooldown set → block re-picks every cycle).
  • The symptom is "Dream seems stuck / nothing promotes" while /health is green.

The only current fix is a source rebuild (const 700 → 2400). This PR makes it a hot setting, so an operator can raise the cycle deadline without a rebuild.

What changed

File Change
internal/config/config.go new DreamConfig.CycleTimeoutdream.cycle_timeout / CTX_DREAM_CYCLE_TIMEOUT, default 700 (legacy const), mut:hot, tenancy:global-only
internal/dream/router.go new Router.CycleTimeout field
internal/dream/dream.go new CycleTimeoutFor(r) helper (router value > 0 wins, else the package CycleTimeout const) + RunDreamCycle reads it
internal/events/scheduler.go newRouter wires CycleTimeout; the outer cycle context reads the effective value
internal/config/validate.go V16b budget reads the effective (hot) cycle deadline (temporalTimeoutBudgetOf), so a raised cycle widens the window instead of warning spuriously
internal/dream/cycle_timeout_test.go TestCycleTimeoutFor (nil / empty / override / negative → default)
internal/config/validate_test.go TestValidateTemporalTimeoutBudget updated (default 400s; 2400s cycle → 2100s)
docs/operations.md new env-table row + temporal_timeout budget clause updated

Precedence: the key is the default for the enclosing cycle. A timeouts.dream entry on the serving context_backends row still wins per call (TimeoutFor, walked in llm/chain.go) — it bounds eval/keywords/recurrence per call, whereas this key bounds the whole cycle. On a configured row, raise the row value instead.

Backward compatibility

  • Default 700 = the legacy CycleTimeout constant — no behavior change for existing deployments.
  • 0 (and a negative value, rejected at boot / 422s the settings write by V16) falls back to the constant — same contract as temporal_timeout.
  • No migrations, no schema change.

Tests & verification

  • go build ./... — clean
  • go vet ./internal/{dream,config,events}/ — clean
  • go test -short ./internal/{dream,config,events}/ — all pass (incl. the two new/updated tests)
  • golangci-lint v2 (repo config) on the 3 touched packages — 0 issues

Base: root.

The whole dream cycle ran under a hardcoded CycleTimeout const (700s).
Slow reasoning models (Qwen3.8-27B-NVFP4) run eval alone for ~600-690s on
full 16-20-candidate prompts, so the enclosing cycle deadline cut the
cycle before the recurrence/quality-score/cooldown steps ran — re-picking
the same block every cycle.

Make it a hot setting, mirroring dream.temporal_timeout (PR GottZ#23):
- config.Dream.CycleTimeout `dream.cycle_timeout` / CTX_DREAM_CYCLE_TIMEOUT
  (default 700 = legacy constant, mut:hot, tenancy:global-only)
- Router.CycleTimeout + newRouter wiring
- dream.CycleTimeoutFor(r) helper: router value > 0 wins, else the
  package CycleTimeout constant (0 = documented "package default"
  sentinel) — the fallback the scheduler's outer cycle context and
  RunDreamCycle's inner context both read
- validate.go V16b budget reads the effective (hot) cycle deadline via
  temporalTimeoutBudgetOf, so a raised cycle_timeout widens the window
  instead of warning spuriously

Backward compatible: default 700 = the old const; 0/negative fall back to
the constant. No migrations.

Tests: TestCycleTimeoutFor (nil/empty/override/negative), updated
TestValidateTemporalTimeoutBudget (default 400 + 2400->2100). Verified:
go build ./... , go vet, go test -short on the 3 touched packages,
golangci-lint v2 (repo config) = 0 issues. Docs: docs/operations.md
env-table row + temporal_timeout budget clause.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant