refactor(dream): rename sidecar subsystem to Dream + convert processor skill to a real agent#237
Conversation
…name Renames the sidecar worker subsystem to Dream (the worker consolidates memory between sessions) across all hook scripts, TypeScript/CJS utilities, agent definition, manifests, and tests. Includes per-project migration rename-sidecar-to-dream-v1 that moves .devflow/sidecar to .devflow/dream with old-wins config semantics and no-clobber marker merge per PF-002. Adds transitional TODO(dream-fallback) legacy config reads on every hook and TS readConfig. Two intentional sidecar remnants kept BY DESIGN: - devflow:sidecar in LEGACY_SKILL_NAMES (prunes orphaned installs) - TODO(dream-fallback) legacy sidecar/config.json reads (removable later) Applies ADR-001 EXCEPTION (migration required for feature-toggle config preservation). Avoids PF-002, PF-003, PF-007. Co-Authored-By: Claude <noreply@anthropic.com>
…ale test assertions Problem 1 (hook renames): dream-capture was calling sidecar_lock_acquire / sidecar_lock_release (undefined after dream-lock renamed the functions to dream_lock_acquire / dream_lock_release). Under set -e, calling an undefined function inside the overflow-truncation if-block caused silent early exit before the queue append. All dream-* hooks had equivalent stale sidecar variable names (SIDECAR_DIR, sidecar_lock_*, sidecar-capture log label) that are now corrected. Problem 2 (missing skills): dream.md lacked a skills: frontmatter block, failing the project invariant that every shared agent declares at least one skill. Added devflow:apply-decisions (decisions/curation tasks) and devflow:apply-feature-knowledge (knowledge task) — both skills are genuinely invoked by the Dream agent spec. Problem 3 (stale assertions): two shell-hooks.test.ts assertions expected the old SIDECAR MAINTENANCE directive string; session-start-context now emits DREAM MAINTENANCE. One migrations.test.ts assertion expected sidecar/ in the .devflow/.gitignore; getDevflowGitignoreContent now emits dream/. Updated all three assertions to the new intentional strings. Co-Authored-By: Claude <noreply@anthropic.com>
…review artifacts Add plugins/*/agents/dream.md to .gitignore alongside the other generated shared-agent entries (alphabetically after bug-analyzer.md). Untrack the two committed plugin copies (devflow-ambient, devflow-core-skills) which are build-regenerated artifacts and must not be versioned. Also untrack .devflow/docs/reviews/feat-ambient-keyword-trigger/ which was accidentally swept into the prior commit -- unrelated to this rename branch. Files remain on disk in their original untracked state. Co-Authored-By: Claude <noreply@anthropic.com>
…rs/comments - knowledge --status: replace jargon "Dream config:" label with neutral "Config:" matching sibling line style (Status:, Sentinel:, Knowledge bases:) - session-start-context: rename _LEARNING_SIDECAR to _LEARNING_DREAM at all 3 occurrences; update stale comments (Section 2: Sidecar to Dream, sidecar config to dream config, sidecar-recover to dream-recover, Primary: sidecar/config.json to dream/config.json) - learn.ts: update two code comments from "sidecar config" to "dream config" - CLAUDE.md: reword "replace...with a dream architecture" to "...background-maintenance (Dream) architecture" for clarity Allowlisted remnants untouched: TODO(dream-fallback) legacy sidecar/config.json path literals, LEGACY_SKILL_NAMES, LEGACY_HOOK_MARKERS, migration IDs. Co-Authored-By: Claude <noreply@anthropic.com>
Update 7 files where internal comments still referenced "sidecar processor" or "sidecar config" after the v3 rename. All changes are comment/doc text only — zero logic changes, zero identifier or string-literal changes. scripts/hud/learning-counts.js regenerates from src/cli/hud/learning-counts.ts via `npm run build:hud` (confirmed green). All 1575 tests pass.
|
BLOCKING: Over-broad rename conflated two unrelated "sidecar" concepts The word "sidecar" had two distinct meanings in this codebase:
This file (originally Impact: A future reader seeing Fix: Revert this file's rename to describe the companion-file pattern, not the subsystem it doesn't belong to. Options:
Then update imports in Cited by: Architecture (92%), Consistency (90%), Documentation (82%) |
|
BLOCKING: Shell-side fallback implemented but completely untested The three hooks (dream-capture, dream-evaluate, session-start-context) contain a legacy-fallback branch: if [ ! -f "$DREAM_DIR/config.json" ] && [ -f "$DEVFLOW_DIR/sidecar/config.json" ]; then
DREAM_CONFIG=...sidecar/config.json
fiThis is the D37 edge-case safety net (project cloned after the global migration marker is set, so the rename migration never runs and only The test helper Impact: An untested safety net that gates whether Fix: Add shell tests that seed ONLY it('falls back to legacy sidecar/config.json when dream/config.json absent', () => {
const legacyDir = path.join(tmpDir, '.devflow', 'sidecar');
fs.mkdirSync(legacyDir, { recursive: true });
fs.writeFileSync(path.join(legacyDir, 'config.json'), JSON.stringify({ learning: false }));
createTranscript(homeDir, tmpDir, 5);
runHook(EVALUATE_HOOK, { cwd: tmpDir, session_id: 'test' }, homeDir);
const log = fs.readFileSync(path.join(tmpDir, '.devflow', 'logs', '.dream-evaluate.log'), 'utf-8');
expect(log).not.toContain('Evaluating learning'); // toggle honored
});Cited by: Testing (95%) |
|
BLOCKING: Migration lacks true re-run idempotency test (PF-004 violation) M3 is labeled "idempotent" but only tests the no-op-when-sidecar-absent case. It never runs the full migration twice against the same seeded state. PF-004 ("migration idempotency — tests must verify correctness since buggy runs never re-run") explicitly requires proving a second run on already-migrated state is a clean no-op that preserves data. The sibling Impact: The Fix: Add a run-twice test seeded like M1 (config + marker), then: it('is idempotent on second run with fully migrated state', async () => {
// Seed: .devflow/sidecar/config.json + marker
seedSidecarState(tmpDir, { learning: true });
// First run
const migration = getMigration();
const ctx1 = makeCtx(tmpDir);
await expect(migration.run(ctx1)).resolves.toMatchObject({ success: true });
// Second run: should be clean no-op
const ctx2 = makeCtx(tmpDir);
await expect(migration.run(ctx2)).resolves.toMatchObject({ success: true });
// Verify state unchanged
const config = JSON.parse(fs.readFileSync(path.join(tmpDir, '.devflow/dream/config.json'), 'utf-8'));
expect(config.learning).toBe(true); // preserved
// And sidecar is gone
expect(fs.existsSync(path.join(tmpDir, '.devflow/sidecar'))).toBe(false);
});Cited by: Testing (88%) |
PR Review Summary — #237: refactor/rename-sidecar-to-dreamVerdict: CHANGES_REQUESTED — One consensus blocking rename issue + test coverage gaps. No runtime security or regression blockers. High-Confidence Findings (≥80%)BLOCKING (Address before merge)
SHOULD-FIX (High maintainability impact)
What Passed✓ No runtime blockers — Build passes, 514 tests pass. Regression review found zero dangling references. Lower-Confidence Observations (60–79%, for context)
Post-Merge Follow-ups (Not Blocking)
Recommendation: The core rename work is disciplined and correct. The blocking issues are architectural clarity (don't name Knowledge helper "Dream") and test coverage (D37 edge case + idempotency). These are cheap to fix and essential for merge readiness. The should-fix items are high-value maintainability improvements (duplication / EXDEV unification) that prevent future rot. Claude Code |
…em The sidecar->Dream rename was over-broad: src/cli/utils/sidecar.ts is the Knowledge-agent companion-file reader (reads .create-result.json / .refresh-result.json next to KNOWLEDGE.md), unrelated to the maintenance subsystem. The mechanical rename to dream.ts produced a false docstring and 'Dream sidecar' contradictions, with DreamData types flowing into sidecar/ sidecarName locals - three names for one concept. Rename to a self-describing name, dropping both 'sidecar' and 'dream' jargon: - dream.ts -> agent-result.ts; DreamData -> AgentResult; readDream -> readAgentResult - knowledge-agent.ts / create.ts / refresh.ts: sidecar -> result, sidecarName -> resultFileName - feature-knowledge SKILL.md / knowledge.md: 'sidecar JSON' -> 'result JSON' Resolves code-review findings (architecture/consistency HIGH, documentation/typescript). applies ADR-008. Co-Authored-By: Claude <noreply@anthropic.com>
… rename-sidecar-to-dream-v1 hardening from code review: - moveFile: add lstat symlink guard (EXDEV copyFile followed symlinks while rename did not - a planted symlink at sidecar/config.json could copy arbitrary contents into dream config); add overwrite? option so the config.json old-wins move reuses moveFile instead of a hand-rolled EXDEV block (removes a second divergent EXDEV impl) - moveDirContents: Promise.all -> allSettled, collect failures into warnings[] threaded through migrateMemoryDir/consolidate/rename migrations so the PF-004 manual sweep is diagnosable - tests: M5 true re-run idempotency (run twice, no-op + data preserved); M6 partial-state resume (config already in dream/, residual markers moved) Resolves code-review findings (security MEDIUM, reliability/complexity, testing HIGH). avoids PF-004. Co-Authored-By: Claude <noreply@anthropic.com>
…r The legacy-fallback (TODO(dream-fallback)) branch copy-pasted the 9-line parse/shape-guard/field-narrow block from the happy path, doubling readConfig's cyclomatic complexity and forcing the eventual fallback removal to edit two blocks. Extract coerceConfig(unknown): DreamConfig|null so both the primary and legacy reads delegate; the legacy branch is now a clean 4-line block for single-excision removal. Behavior identical. Resolves code-review finding (complexity/typescript/reliability MEDIUM). Co-Authored-By: Claude <noreply@anthropic.com>
…ent wording - tests/shell-hooks.test.ts: the D37 legacy-config dual-read (read stale sidecar/config.json when dream/config.json absent) was implemented in the hooks but UNTESTED on the shell side (PR claim of 'tested TS+shell' was false there). Add 6 behavior tests across dream-capture/dream-evaluate/ session-start-context seeding only the legacy path; rename misnamed helper createSidecarConfig -> createDreamConfig (it writes to dream/), add genuine createLegacySidecarConfig - scripts/hooks: rewrite 7 stale TODO(dream-fallback) comments (they implied pending work for code implemented two lines below) to a uniform removal-milestone note for a clean future grep-and-delete - shared/agents/dream.md: 'dream processor'/'processor' -> 'Dream agent' Resolves code-review findings (testing HIGH, reliability/complexity/consistency). avoids PF-003. Co-Authored-By: Claude <noreply@anthropic.com>
Post-rename cleanup of stale sidecar references the PR-237 rename left behind (flagged by code review as out-of-scope follow-ups): - docs/self-learning.md, docs/reference/file-organization.md, docs/reference/skills-architecture.md: 'sidecar processor' -> 'Dream agent' (now a real agent at shared/agents/dream.md, spawned via Agent(subagent_type="Dream"), not the deleted devflow:sidecar skill); .devflow/sidecar/ -> .devflow/dream/; sidecar-* hooks -> dream-*; SIDECAR MAINTENANCE -> DREAM MAINTENANCE directive. file-organization.md's hook tree/tables corrected to match actual scripts/hooks/ contents. Kept literal: purge-orphaned-sidecar-judgment-state migration ID. - .devflow/features/hooks KB: refreshed via 'devflow knowledge refresh hooks' (referencedFiles/agent path now dream-*); index entry synced to the KNOWLEDGE.md frontmatter (name 'Dream & Hooks System', directories drop the deleted shared/skills/sidecar/). KB now reads non-stale. Co-Authored-By: Claude <noreply@anthropic.com>
… KB, command docs
Final cleanup pass on the sidecar→Dream rename — references the bulk rename missed:
- docs/working-memory.md: was never synced. SIDECAR MAINTENANCE → DREAM
MAINTENANCE, sidecar processor → Dream agent, sidecar/ → dream/ dir, sidecar
marker → dream marker (file structure + Self-Learning sibling section).
- .devflow/features/hooks/.create-result.json: referencedFiles pointed at four
deleted paths (sidecar-capture/evaluate/recover, shared/skills/sidecar/SKILL.md)
→ repoint at dream-capture/evaluate/recover + shared/agents/dream.md. These
drive the KB staleness check, which was tracking dead files.
- explore{,-teams}.md / implement{,-teams}.md: Knowledge companion-file still
called "sidecar" though commit 9774c5d renamed the source-side concept to
"result" (.create-result.json). Align docs: Read sidecar → Read result file,
{from_sidecar} → {from_result}.
- feature-knowledge.test.ts: sidecar locals + test-sidecar-*.json → result.
- Stale "processor" prose (skill→agent conversion leftover) → "Dream agent" in
session-start-context, dream-recover, json-helper.cjs, migrations.ts, and two
learning test comments. Kept the .processor-spawned-at on-disk filename and its
"processor-spawn throttle" concept name intact (renaming prose but not the file
would create a new inconsistency).
Left intentionally: LEGACY_SKILL_NAMES, LEGACY_HOOK_MARKERS, migration code/IDs,
dream-fallback legacy reads + their tests, and the historical "Phase 3 (reliable
LLM sidecar consumption)" phase-name references.
Build green; affected tests pass.
Co-Authored-By: Claude <noreply@anthropic.com>
… sidecar/ This repo's own runtime .devflow/.gitignore predated the sidecar→dream rename and still ignored the old sidecar/ transient dir. Its three sources of truth (project-paths.ts, project-paths.cjs, ensure-devflow-init) already emit dream/. Align the committed file so dream transient state is ignored once created. Co-Authored-By: Claude <noreply@anthropic.com>
Summary
Renames the background-maintenance worker subsystem from "sidecar" to "Dream" and converts the processor skill into a real agent. The worker "dreams" — consolidates memory/learning/decisions/knowledge/curation — between sessions.
Why: Every session, a SessionStart hook forced the main model to load the
devflow:sidecarskill and paste its ~340-line "Processor Spec" into a bareAgent({prompt})call — visible noise (skill-load line + giant paste + narration) on every session, and the spec was re-shipped through the model each time.Now: SessionStart emits a 5-line
DREAM MAINTENANCEdirective that spawns a singleAgent(subagent_type="Dream", run_in_background: true)with a one-sentence prompt. The 340-line spec lives once inshared/agents/dream.md. Session noise collapses to oneAgent(...)line.What changed
Two intentional "sidecar" remnants (by design)
(Plus historical migration code that must keep its old paths/IDs, the `LEGACY_HOOK_MARKERS` self-healing list, and the unrelated "sidecar output file" pattern in the Knowledge agent — a different meaning, out of scope.)
Validation
Test plan
🤖 Generated with Claude Code