fix(cycle): wire drift detection into the dream cycle — report-only v1 (#2653) (#3317)

dream.drift.enabled has gated an unwired scaffold since v0.28: runPhaseDrift
had zero call sites, the resolved model + BudgetMeter were discarded
(void modelId; void meter), and no operator-readable output existed.

- Wire 'drift' as a CyclePhase (default OFF via dream.drift.enabled),
  ordered after the calibration trio and before embed so the report page
  gets embedded same-cycle. PHASE_SCOPE=global, cycle-lock coordinated,
  --once (--phase drift --once) bypasses the gate for one run.
- Implement the LLM judge: soft-band candidates (weight 0.3-0.85, active,
  unresolved, fresh timeline evidence) are judged against their page's
  recent timeline entries via gateway chat; BudgetMeter-gated
  (dream.drift.budget, default $1), capped by dream.drift.max_per_cycle
  (default 20). Judge model resolves models.drift -> reasoning tier ->
  sonnet fallback (unchanged from scaffold).
- Report-only v1: judged candidates land on a reports/drift-<date> page.
  dream.drift.auto_update mutates NOTHING; the flag state is recorded in
  the report for operators.

Co-authored-by: Garry Tan <garrytan@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Time Attakc
2026-07-23 17:28:47 -07:00
committed by GitHub
co-authored by Garry Tan Claude Fable 5
parent 69bc37f745
commit 40d9b83d5c
7 changed files with 408 additions and 47 deletions
+5 -4
View File
@@ -41,15 +41,16 @@ describe('PHASE_SCOPE coverage', () => {
expect(invalid).toEqual([]);
});
test('all 22 phases covered (regression on accidental omission)', () => {
test('all 23 phases covered (regression on accidental omission)', () => {
// Pin the count so a future PR that adds a phase to ALL_PHASES
// without updating PHASE_SCOPE notices here too. The v0.39.1.0
// master merge brought in the 17th phase (`schema-suggest`); v0.41
// adds 'extract_atoms' + 'synthesize_concepts' (T9 lens packs) +
// 'conversation_facts_backfill' (v0.41.11.0) for 20; v0.41.39 (#1700)
// adds 'enrich_thin' and v0.42.0.0 adds 'skillopt' for a total of 22.
expect(ALL_PHASES.length).toBe(22);
expect(Object.keys(PHASE_SCOPE).length).toBe(22);
// adds 'enrich_thin' and v0.42.0.0 adds 'skillopt' for a total of 22;
// #2653 adds 'drift' for 23.
expect(ALL_PHASES.length).toBe(23);
expect(Object.keys(PHASE_SCOPE).length).toBe(23);
});
test('embed remains global (the headline brain-wide phase)', () => {