Skip to content

refactor: rename dream → learning; consolidate .devflow/dream/ + .devflow/decisions/ → .devflow/learning/ - #261

Merged
dean0x merged 28 commits into
mainfrom
refactor/rename-dream-to-learning
Jul 16, 2026
Merged

refactor: rename dream → learning; consolidate .devflow/dream/ + .devflow/decisions/ → .devflow/learning/#261
dean0x merged 28 commits into
mainfrom
refactor/rename-dream-to-learning

Conversation

@dean0x

@dean0x dean0x commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

The "dream" placeholder codename (capture hooks + background agent + decisions pipeline) is renamed to learning end-to-end. Directories consolidate to a flat .devflow/learning/; agent DreamLearning (shared/agents/learning.md, directive --- LEARNING MAINTENANCE --- with subagent_type="Learning"); CLI devflow decisionsdevflow learning (clean break, no alias); init flags --learning/--no-learning; feature toggles move to a neutral .devflow/config.json {memory, learning, knowledge} (legacy decisions key coalesces and wins); tuning config decisions.jsonlearning.json (project .devflow/learning/learning.json + global ~/.devflow/learning.json); TS modules per naming map (feature-config.ts, learning.ts, learning-tuning-config.ts, learning-queue-cleanup.ts, legacy-hooks.ts, HUD learning-counts.ts); shell layer (LEARNING_DIR, _QG_LEARNING, learning-lock).

Changes

Naming Boundary: Content identifiers deliberately KEEP their names (decisions.md, pitfalls.md, decisions-ledger.jsonl, decisions-log.jsonl, index.md, decisions_status, DECISIONS_CONTEXT, decisions_load(), render-decisions.cjs, decisions-format.cjs, decisions-usage-scan.cjs, ADR-NNN/PF-NNN) — user data compatibility.

Migrations: New consolidate-dream-decisions-to-learning-v1 (per-project: config-first with legacy-key coalesce, queue + content moves via moveFile/moveDirContents, 9-entry skip-set drops locks/telemetry, index re-render) + rename-global-decisions-config-v1 (global). The hazardous retired-pipeline migrations purge-learning-pipeline-v1/purge-learning-global-v1 (which would have destroyed the new .devflow/learning/) are deleted; consolidate-to-devflow-dir learning-handling neutralized (ID unchanged). Legacy sweeps: LEGACY_AGENT_NAMES += 'dream', LEGACY_HOOK_FILES += 'dream-lock'; build-plugins.ts now prunes shared-agent copies absent from plugin manifests.

Scope: Explicitly out of scope / unchanged: learnings stay local + gitignored (git-tracking was evaluated and dropped — gitignore functional lines, sentinel, and marker version byte-unchanged); no devflow decisions alias; queue-emptiness stays the sole directive gate (no new locks/throttles/state); 900s staleness, 120s memory throttle, 200→100 queue cap unchanged.

Documentation: Docs rewritten to the learning end-state (CLAUDE.md, README, cli-reference incl. removal of documented-but-unimplemented --review/--purge, file-organization, working-memory, docs-framework); feature KB dream-capture-system replaced by learning-capture-system.

Verification

  • Full suite: Test Files 63 passed (63) / Tests 1988 passed (1988); build deterministic (npm run build + build:mds leave the tree clean).
  • Pipeline: 3 sequential Coders + Simplifier + Scrutinizer (fixed a real P0: repointed remaining .devflow/decisions/index.md consumers in MDS partials/shared agents/release command) + Evaluator (final verdict ALIGNED — 30/30 acceptance criteria incl. AC-P5) + Tester (11/11 scenario seeds PASS against sandboxed fixtures: full migration, idempotent re-init, clean break, stale-agent sweep, HUD, directive gating, global tuning rename).

Known Follow-Ups

  • Pre-existing test flake: build-mds.test.ts temp fixture (commands/_test-dest-safety.mds) can race the rename-completeness sweep scan in learning-agent.test.ts under full-suite parallelism (observed once, second run clean).
  • Snyk code scan skipped in this session (MCP unauthenticated).

Related Issues

None specified in this PR.

dean0x added 18 commits July 14, 2026 22:19
…and HUD notifications stack

Commit 1 of 5 (Coder A). Clears the learning namespace for the
dream→learning rename by removing retired artifacts:

- Delete purge-learning-pipeline-v1 + purge-learning-global-v1 migrations
  and their _dropLearningKeyFromConfig helper (IDs remain inert strings in
  migrations.json; no live readers of old learning/ artifacts remain)
- Neutralize consolidate-to-devflow-dir: remove learning/ mkdir and 9
  memMap entries (learning-log.jsonl, learning.json, *.learning-* files)
- Strip .learning-manifest.json from purge-orphaned-sidecar-judgment-state
- Delete src/cli/utils/learning-cleanup.ts (only used by deleted migration)
- Delete dead HUD notifications stack (no writer of .decisions-notifications.json
  exists): notifications.ts, components/notifications.ts, notifications-shape.ts,
  and all registrations in index.ts, render.ts, config.ts, types.ts
- Delete tests/learning/hud-notifications.test.ts, tests/learning-cleanup.test.ts,
  tests/integration/learning/end-to-end.test.ts
- Fold tests/learning/ live-code tests into tests/decisions/ (fix import depths)
- Remove purge-learning describe blocks from migrations.test.ts; re-anchor
  ordering assert to purge-orphaned-dream-commit-hook-v1
…to learning/

Commit 2 of 5 (Coder A). Renames path accessors in lockstep across
project-paths.ts and project-paths.cjs, then repoints every caller.

Path renames:
- getDreamDir + getDecisionsDir → getLearningDir (.devflow/learning/)
- getDreamConfigPath → getFeatureConfigPath (.devflow/config.json)
- getDreamPendingTurnsPath → getLearningPendingTurnsPath
- getDreamPendingTurnsProcessingPath → getLearningPendingTurnsProcessingPath
- getDecisionsConfigPath → getLearningTuningConfigPath (.devflow/learning/learning.json)
- getObservationsLockDir: repoints to .devflow/learning/.observations.lock
- All getDecisions* content accessors: keep names, repoint dir segment
  from decisions/ to learning/ (rule: accessors follow the file they point to)

Callers updated: dream-config.ts, decisions-ledger-migration.ts,
legacy-decisions-purge.ts, dream-cleanup.ts, decisions-config.ts,
decisions.ts, tests/project-paths.test.ts, tests/decisions/cli-subcommands.test.ts

The TS/CJS full-export parity test catches any future one-sided drift.
…decisions→learning key

- dream-config.ts → feature-config.ts: FeatureConfig {memory,learning,knowledge}
- coerceConfig reads learning, coalesces from legacy decisions (decisions wins)
- writeConfig mkdirs .devflow/ root (config.json at neutral root)
- Repoint callers: init.ts, decisions.ts, memory.ts, knowledge/toggle.ts
- tests/dream-config.test.ts → tests/learning-config.test.ts + coalesce cases
…rning, global tuning rename

- consolidate-dream-decisions-to-learning-v1 (per-project): moves .devflow/dream/ +
  .devflow/decisions/ → .devflow/learning/; writes .devflow/config.json from dream config;
  drops lock dirs + orphaned telemetry; re-renders index.md footer paths
- rename-global-decisions-config-v1 (global): ~/.devflow/decisions.json → learning.json
- 13 tests for consolidation + 6 tests for global rename
- Update v3-independent test: seed/read learning/ (post-path-repoint) + pre-apply consolidation
…ning

- ensure-devflow-init: fast-path and mkdir use learning/ (not dream/+decisions/)
- NEW learning-lock: learning_lock_acquire/release (replaces dream-lock functions)
- queue-append: _QG_LEARNING/_QG_DECISIONS → _QG_LEARNING, learning-lock callers,
  .learning jq/node config field, queue_append_both param names
- capture-prompt/turn/question: LEARNING_DIR, $DEVFLOW_DIR/config.json (neutral),
  LEARNING_ENABLED, source learning-lock, learning/ queue paths
- session-start-context: LEARNING_DIR, FEATURE_CONFIG, LEARNING_ENABLED,
  --- LEARNING MAINTENANCE --- directive, subagent_type="Learning",
  model resolution from learning/learning.json → ~/.devflow/learning.json
- session-start-memory, memory-worker, background-memory-update, pre-compact-memory:
  remove DREAM_DIR, DREAM_CONFIG → FEATURE_CONFIG at $DEVFLOW_DIR/config.json
- render-decisions.cjs: '.devflow','decisions' → '.devflow','learning' (2 sites)
- Test pins updated: dream-lock → learning-lock in harness source chains;
  $DECISIONS → $LEARNING in queue_read_gates assertions; writeDreamConfig →
  writeFeatureConfig writing to .devflow/config.json; dream/ paths → learning/;
  .decisions-usage.json in learning/; session-start-context directive strings
… green)

Update all test files and two source files to align with the dream/decisions
→ learning consolidation from commits 2–5:

Source fixes:
- decisions-config.ts: global tuning config reads ~/.devflow/learning.json
  (was decisions.json); update JSDoc comment
- legacy-decisions-purge.ts: revert memoryDir fallback to decisions/ —
  .memory/decisions/ is a content identifier (legacy purge target), not a
  system identifier to rename

Test updates (16 files):
- config-disable-guards.test.ts: feature config at .devflow/config.json;
  learning: false field; usage/TL;DR paths in learning/
- eager-memory-refresh.test.ts: writeDreamConfig writes to .devflow/config.json
- decisions/config.test.ts: global learning.json + project learning/learning.json
- decisions/cli-subcommands.test.ts: config.learning field (was config.decisions)
- decisions/decisions-format.test.ts: seed dir .devflow/learning/ for assign-anchor tests
- decisions/{ledger-ops,render-decisions,decisions-ledger-migration,
  index-content,decisions-usage-scan,dream-curation}.test.ts: bulk path rename
- hud-decisions-counts.test.ts, legacy-decisions-purge.test.ts,
  memory.test.ts: bulk path rename

All 1977 tests passing.
…acy-hooks module

- Rename decisions.ts → learning.ts (command: 'learning', export learningCommand)
- Rename dream.ts → legacy-hooks.ts (keep removeDreamHook/hasDreamHook names per KEEP list)
- Rename dream-cleanup.ts → learning-queue-cleanup.ts (drainDreamQueue→drainLearningQueue; sweepLegacyDreamMarkers keeps name)
- Rename decisions-config.ts → learning-tuning-config.ts (DecisionsConfig→LearningTuningConfig, global path ~/.devflow/learning.json)
- Fix latent bug in handleConfigure: was calling getMemoryDir() instead of getLearningDir()
- Simplify handleReset to single-dir semantics: fs.rm(getLearningDir(), recursive+force)
- Update init.ts: --learning/--no-learning flags, dream-lock in LEGACY_HOOK_FILES, manifest write uses learning key
- Update manifest.ts: ManifestData.features.learning + self-heal features.decisions→.learning on read
- Update cli.ts, list.ts, uninstall.ts for new imports and learning terminology
- Update migrations.ts: import sweepLegacyDreamMarkers from learning-queue-cleanup
- Test updates: cli-subcommands/config tests repoint imports, fix reset idempotency paths,
  add AC-C2 unknown-command test, update config-disable-guards path for learning dir
…data types

- Rename decisions-counts.ts → learning-counts.ts (gatherDecisionsCounts→gatherLearningCounts, default export learningCounts)
- Update types.ts: ComponentId 'decisionsCounts'→'learningCounts', DecisionsCountsData→LearningCountsData, GatherContext.decisionsCounts→learningCounts
- Update config.ts: HUD_COMPONENTS entry 'decisionsCounts'→'learningCounts'
- Update render.ts: import + COMPONENT_MAP + LINE_GROUPS updated
- Update index.ts: needsDecisionsCounts→needsLearningCounts, gatherDecisionsCounts→gatherLearningCounts, context field renamed
- Rename test file hud-decisions-counts.test.ts → hud-learning-counts.test.ts with all references updated
- Update hud-render.test.ts: learningCounts field in GatherContext helper
…epoint index paths

- Create shared/agents/learning.md: name Learning, all .devflow/dream/ paths → .devflow/learning/, unlink PF-003 preserved
- Update plugin manifests (core-skills, ambient): agents: ['dream'] → ['learning']
- Update src/cli/plugins.ts: agents arrays updated, 'dream' added to LEGACY_AGENT_NAMES
- Update scripts/build-plugins.ts: add post-copy pruning of stale shared-agent copies not in manifest
- Update commands/_partials/_decisions.mds: index path .devflow/decisions/ → .devflow/learning/
- Update commands/_partials/_knowledge.mds: config gate .devflow/dream/config.json → .devflow/config.json
- Update shared/skills/apply-decisions/SKILL.md: decisions.md/pitfalls.md path references updated
- Build distributes learning.md to plugin agents/; dream.md pruned by new prune logic
- Rename tests/dream-agent.test.ts → tests/learning-agent.test.ts with all path/name assertions updated
  plus lockstep tests: 900s staleness, no .devflow/dream/ in shipped artifacts, no dream.md after build,
  AC-C9 (none) fallback cases for decisions_load()
- Rename tests/decisions/dream-curation.test.ts → tests/decisions/learning-curation.test.ts
  (curation contract now pins learning agent paths, .devflow/learning/ helper paths)
- Update tests/build-mds.test.ts: pin .devflow/learning/index.md (not decisions/)
- Update tests/plugins.test.ts: pin dream in LEGACY_AGENT_NAMES, learning in core-skills/ambient
…nt structure

- list-logic.test.ts: decisions→learning in allOff constant and test fixtures;
  update expected output strings from 'decisions' to 'learning'
- manifest.test.ts: decisions→learning in valid-data round-trip fixture;
  normalizes-without-hud and normalizes-without-decisions assertions use .learning
- command-adoption.test.ts: .devflow/decisions/index.md→.devflow/learning/index.md
  to match the repointed _decisions.mds partial (commit 8 change)
- build.test.ts: add LEGACY_AGENTS exclusion for dream.md (orphan guard skips
  the legacy agent file pending deletion in commit 9 by Coder C)
Remove inline history notes that explain what directories used to be
called before the dream→learning rename — per ADR-003 "leave the
end-state, not the transition":

- ARCHITECTURE block: drop the "PR 5b" and "dream→learning rename
  consolidated" lines (kept the sync-counterpart guidance)
- getLearningDir JSDoc: drop "(dream queue + decisions content
  consolidated)"
- getFeaturesDir JSDoc: drop "(promoted from .features/)"
- getDocsDir JSDoc: drop "(promoted from .docs/)"
- Section header: "Learning queue files (formerly under dream/)" →
  "Learning queue files"
- Section header + note: "Learning content files (formerly under
  decisions/) / Accessor names follow the file they point to — only
  the dir segment changed." → "Learning content files"

Applied identically to both mirror files:
  scripts/hooks/lib/project-paths.cjs
  src/cli/utils/project-paths.ts

Build and full test suite (63 files / 1988 tests) pass.
- Delete shared/agents/dream.md (replaced by learning.md in commit 8)
- Remove LEGACY_AGENTS bridge exclusion from tests/build.test.ts
- Update tests/decisions/decisions-format.test.ts: path + describe block
  renamed from dream.md → learning.md, comment at line 418 updated
- Remove dream.md pending-deletion carve-out from tests/learning-agent.test.ts

Shared-agent count returns to exactly 16 (all referenced by manifests).
npm run build prune logic verified: no plugins/*/agents/dream.md survives.
Replace every system identifier (dream→learning, decisions→learning,
Dream agent→Learning agent, .devflow/dream/→.devflow/learning/,
devflow decisions→devflow learning) across CLAUDE.md, README.md,
docs/cli-reference.md, docs/reference/file-organization.md,
docs/reference/skills-architecture.md, docs/working-memory.md,
shared/skills/docs-framework/SKILL.md, and prose comments in
src/cli/commands/memory.ts.

Adds worktree edge case note to migrations paragraph (D37): per-project
discovery reads ~/.claude/history.jsonl, so a linked worktree that never
hosted a Claude session is not swept even after removing the marker;
recovery requires opening a Claude session there first.

No rename narration, no tombstones — ADR-003.
The feature area has been fully renamed to learning. The dream-capture-system
KB slug is obsolete — callers would now look up learning-capture-system or
similar. Removing the stale entry prevents misleading KB matches.
The consolidation moved the rendered index/decisions/pitfalls to
.devflow/learning/ (producers: render-decisions.cjs, session-start-context,
_decisions.mds, apply-decisions skill), but several downstream consumers were
missed and still read the now-dead .devflow/decisions/ directory:

- commands/_partials/_preamble.mds, _engine.mds — produce DECISIONS_CONTEXT
  for the dynamic-* workflow commands; read .devflow/decisions/index.md
- shared/agents/skimmer.md — TL;DR read of decisions.md (ungated by CONTEXT)
- shared/agents/coder.md — decisions/pitfalls fallback + citation guidance
- shared/agents/{reviewer,designer,triager,scrutinizer}.md — canonical
  DECISIONS_CONTEXT description string
- plugins/devflow-release/commands/release.md — Phase 1b decisions read

On a migrated project these all resolved to a non-existent path, silently
dropping ADR/PF context (DECISIONS_CONTEXT -> (none)). Repoint the directory
segment only; content file names (index.md/decisions.md/pitfalls.md) are
unchanged per the naming-boundary KEEP list. Update the canonical-description
pin in command-adoption.test.ts to match. Rebuilt plugin/command artifacts.
Drop `dream`/`decisions` from the comment line in both gitignore writers
(ensure-root-gitignore line ~50 and DEVFLOW_GITIGNORE_BLOCK in post-install.ts)
so the emitted comment reads `memory, learning, docs, locks`. Both emitted
blocks remain byte-identical; functional ignore lines, sentinel, and the
.root-gitignore-configured-v2 marker version are unchanged.

Also update stale dev-comments in the same files (lines ~5, ~17, ~19 in
ensure-root-gitignore; line ~44 in post-install.ts) and the render-decisions.cjs
doc comment for LEDGER_FILENAME (`.devflow/decisions/` → `.devflow/learning/`).
The LEDGER_FILENAME constant itself (`decisions-ledger.jsonl`) is untouched.
@dean0x

dean0x commented Jul 15, 2026

Copy link
Copy Markdown
Owner Author

Code Review: PR #261 — Rename Dream → Learning

Status: ⚠️ CHANGES_REQUESTED

Summary: This is a well-executed rename refactor with strong end-state correctness. However, there are 9 blocking/should-fix issues that must be resolved before merge. Multiple reviewers independently flagged the same items (dead file, half-renamed symbols, stale comments), elevating priority.


Blocking Issues (Must Fix Before Merge)

1. HIGH: Test-isolation TOCTOU race in learning-agent.test.ts:189-201

Confidence: 88% (Testing)

The lockstep artifact sweep at lines 189-201 collects a file list via readdir, then reads each file with readFileSync unguarded. In parallel, test fixtures planted by build-mds.test.ts can be deleted between the sweep's readdir and readFileSync, causing ENOENT crashes.

Fix: Guard file reads with try/catch for ENOENT:

for (const f of files) {
  let content: string;
  try {
    content = fsSync.readFileSync(f, 'utf-8');
  } catch (e) {
    if ((e as NodeJS.ErrnoException).code === 'ENOENT') continue;
    throw e;
  }
  if (content.includes('.devflow/dream/')) violations.push(path.relative(ROOT, f));
}

Apply the same guard to the subagent_type="Dream" sweep at line 207.


2. HIGH: Dead scripts/hooks/dream-lock file shipped but unused

Confidence: 97% (Consistency 95% + Reliability 92%)

This PR added scripts/hooks/learning-lock (the renamed version) but left the old scripts/hooks/dream-lock in place. Nothing sources dream-lock anymore — the old file is dead code.

Fix: git rm scripts/hooks/dream-lock (keep the LEGACY_HOOK_FILES('dream-lock') cleanup entry in init.ts so existing user installs get cleaned up).


3. MEDIUM: Half-renamed writeDreamConfig alias at src/cli/commands/init.ts:43

Confidence: 88% (Consistency)

The import was repointed from dream-config.js to feature-config.js, but the alias name stayed "Dream":

import { writeConfig as writeDreamConfig } from '../utils/feature-config.js';

This function now writes memory/learning/knowledge toggles — nothing to do with "dream". The alias is a stale name left on a changed line.

Fix: Drop the alias:

import { writeConfig } from '../utils/feature-config.js';
// ... later:
await writeConfig(gitRoot, { memory, learning, knowledge })

4. MEDIUM: Consolidate migration step 6 render call unguarded after irreversible moves

File: src/cli/utils/migrations.ts:1073-1074
Confidence: 82% (Testing)

The await renderDecisionsIndex(ctx.projectRoot) call has zero test coverage and no try/catch. If the renderer throws after step 5's irreversible file moves, the migration strands in a broken state with a stale index.md.

Fix:

  • Wrap in try/catch that pushes to warnings instead of throwing post-move
  • Inject rendererPath into tests so step 6 can be exercised (currently the test skips it)

5. MEDIUM: Consolidate migration run() spans 96 lines with duplicated coalesce logic

File: src/cli/utils/migrations.ts:983-1081
Confidence: 84% (Complexity)

Step 1's config block (lines 1008-1019) re-implements the exact decisions → learning coalesce that coerceConfig in feature-config.ts owns. They can't be unified (intentional differences), but the boilerplate duplication is maintainability debt. Also hardcodes { memory: true, learning: true, knowledge: true } instead of using DEFAULT_CONFIG.

Fix:

  • Extract step 1 into a named helper seedFeatureConfigFromDream()
  • Export DEFAULT_CONFIG from feature-config.ts
  • This drops the run() body to ~70 lines, matching the file's pattern of delegating steps to helpers

Should-Fix Issues (Address While Code is Open)

6. MEDIUM: Dead path constructors re-pathed instead of deleted

File: src/cli/utils/project-paths.ts:100, :130, :135
Confidence: 85% (Architecture)

Three unused path constructors were re-pathed from .devflow/decisions/.devflow/learning/ instead of deleted:

  • getDecisionsManifestPath() (line 100)
  • getDecisionsNotificationsPath() (line 130)
  • getDecisionsBatchIdsPath() (line 135)

Grep confirms zero production references — only the path constructors, their mirrors in project-paths.cjs, and test assertions exist. Ironically, this PR's consolidate migration explicitly lists all three as orphaned.

Fix: Delete the three functions and their exports. Remove the test cases at project-paths.test.ts:247-253.


7. MEDIUM: handleConfigure global write bypasses canonical DEVFLOW_DIR accessor

File: src/cli/commands/learning.ts:191
Confidence: 80% (Architecture)

The --configure --global branch writes directly to $HOME/.devflow/learning.json, but every reader uses getDevFlowDirectory(). This breaks for users with DEVFLOW_DIR set — written config is never read back.

Fix: Replace hardcoded path with getDevFlowDirectory():

path.join(getDevFlowDirectory(), 'learning.json')

8. MEDIUM: Feature KB mislabels migrations as per-project

File: .devflow/features/learning-capture-system/KNOWLEDGE.md:218
Confidence: 90% (Documentation)

The Migrations section says "Two per-project migrations" but then lists both consolidate-dream-decisions-to-learning-v1 (per-project) AND rename-global-decisions-config-v1 (global). The per-project vs global distinction is load-bearing for D37 fresh-clone edge case reasoning.

Fix: Reword to reflect scope:

One per-project migration (consolidate-dream-decisions-to-learning-v1) and one global migration 
(rename-global-decisions-config-v1) consolidate older installs:

9. MEDIUM: Stale "Dream" comments in changed code

Files:

  • src/cli/commands/init.ts:1170 — "spawns the Dream agent" → Learning agent
  • src/cli/commands/init.ts:1155 — "see the dream hook below"
  • src/cli/commands/init.ts:1203 — "Write dream config.json" → ".devflow/config.json"
  • src/cli/commands/init.ts:1206 — "dream-config.ts" → "feature-config.ts"
  • src/cli/utils/migrations.ts:828 — "runs as the directive-spawned Dream agent" → Learning agent
  • src/cli/utils/migrations.ts:888 — "next Dream run" → Learning run
  • scripts/hooks/memory-worker:3 — "Dream System: memory-worker" → Learning System
  • CLAUDE.md:58 — "NO Dream task" → NO Learning task

Confidence: 90% (Consistency)

These are stale references left during the rename. A reader trusting "spawns the Dream agent" will be misled.

Fix: Replace all occurrences with the Learning equivalents.


Additional Suggestions (60–79% Confidence)

  1. Weak lockstep 900s assertionexpect(hookContent).toMatch(/900/) only checks for substring presence; it doesn't verify the threshold context. Strengthen with anchored context.

  2. Export DEFAULT_CONFIG — Removes the hardcoded literal at migrations.ts:1008 and unifies feature config initialization.

  3. Stale parameter namedreamDir in sweepLegacyDreamMarkers should be renamed to learningDir or oldDreamDir to clarify.

  4. JSDoc precedence invertedsrc/cli/utils/feature-config.ts:25 JSDoc says "mirrors manifest kb→knowledge" but implementation is backwards. Reword to match code.

  5. .decisions-usage.json missing from file tree — Still actively written but dropped from the CLAUDE.md runtime file tree; re-add for completeness.


Pre-Existing Issues (Not Blocking This PR)

Several stale "Dream" comments exist in untouched files:

  • scripts/hooks/lib/mkdir-lock.cjs:21,31
  • scripts/hooks/resolve-project-root:11
  • src/cli/commands/capture.ts:9-10

These are pre-existing and can be addressed in a separate sweep.


Risk Assessment

  • Security: ✓ Clean
  • Functional Correctness: ✓ Clean (full compile pass, no dangling imports)
  • Architecture: ✓ Sound
  • Performance: ✓ No regression
  • Test Coverage: ⚠️ One flake introduced, two coverage gaps (fixable)
  • Consistency: ⚠️ End-state residue violates ADR-003 (high priority given PR scope)

Merge Recommendation

STATUS: CHANGES_REQUESTED — Fix items 1-9 above before merge.

This is a well-executed rename refactor. The functional rename is complete and correct. The issues are consistency/cleanliness concerns appropriate to the PR's scope ("clean end-state per ADR-003"). Multiple reviewers independently flagged the same dead file, orphaned comments, and half-renamed symbols, elevating their priority.


Reviewed by: 10 agents (security, architecture, performance, complexity, consistency, regression, testing, reliability, typescript, documentation)
Review Date: 2026-07-15_2239
Branch: refactor/rename-dream-to-learningmain


Generated by Claude Code / Devflow Code Review — Review Reports

const violations: string[] = [];
for (const f of files) {
const content = fsSync.readFileSync(f, 'utf-8');
if (content.includes('.devflow/dream/')) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Testing] HIGH (88%) — TOCTOU race: file list via readdir can be stale when readFileSync runs; if build-mds.test.ts deletes transient fixtures between calls, test crashes with ENOENT. Fix: guard reads with try/catch for ENOENT, continue on file not found.

Comment thread src/cli/utils/migrations.ts Outdated
// 6. Re-render index.md so footer paths reference learning/ not decisions/.
// Without this, workflow commands hand sub-agents dead paths until the next
// Learning-agent render. No-op when ledger is absent.
const { renderDecisionsIndex } = await import('./decisions-ledger-migration.js');

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Testing/Reliability] MEDIUM (82%) — renderDecisionsIndex called after irreversible dir moves with no try/catch; if throw occurs post-move, migration strands in broken state. Fix: wrap in try/catch→warn, inject rendererPath into tests.

await fs.mkdir(memoryDir, { recursive: true });
const projectConfigPath = getDecisionsConfigPath(process.cwd());
// FIX: mkdir the learning dir (parent of learning.json), not the memory dir
const learningDir = getLearningDir(process.cwd());

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Architecture] MEDIUM (80%) — handleConfigure writes to $HOME/.devflow/learning.json but readers use getDevFlowDirectory(); silent no-op when DEVFLOW_DIR is set. Fix: route through canonical accessor via getDevFlowDirectory().

Comment thread src/cli/commands/init.ts Outdated
import { addContextHook, removeContextHook, hasContextHook } from './context.js';
import { writeFileAtomicExclusive } from '../utils/fs-atomic.js';
import { writeConfig as writeDreamConfig } from '../utils/dream-config.js';
import { writeConfig as writeDreamConfig } from '../utils/feature-config.js';

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Consistency] MEDIUM (88%) — alias 'writeConfig as writeDreamConfig' repointed to feature-config.js but name retained; function now writes {memory,learning,knowledge}, unrelated to dream. Fix: drop alias or rename to writeFeatureConfig.

Comment thread src/cli/utils/project-paths.ts Outdated

/** .devflow/decisions/.decisions-manifest.json */
/** .devflow/learning/.decisions-manifest.json */
export function getDecisionsManifestPath(projectRoot: string): string {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Architecture] MEDIUM (85%) — dead getDecisionsManifestPath re-pathed instead of deleted; builds path for files this PR's consolidate migration drops (zero production refs). Fix: delete the constructor and test case.

id: 'consolidate-dream-decisions-to-learning-v1',
description: 'Consolidate .devflow/dream/ + .devflow/decisions/ into .devflow/learning/ and write .devflow/config.json',
scope: 'per-project',
async run(ctx: PerProjectMigrationContext): Promise<MigrationRunResult> {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Complexity] MEDIUM (84%) — 96-line run() duplicates coerceConfig coalesce logic and hardcodes DEFAULT_CONFIG; maintainability debt. Fix: extract seedFeatureConfigFromDream helper, export DEFAULT_CONFIG, drop run() to ~70 lines.

Comment thread CLAUDE.md Outdated
**Claude Code Flags**: Typed registry (`src/cli/utils/flags.ts`) for managing Claude Code feature flags (env vars and top-level settings). Pure functions `applyFlags`/`stripFlags`/`getDefaultFlags` follow the `applyViewMode`/`stripViewMode` pattern. Flags (20 total): default ON — `tui`, `tool-search`, `lsp`, `prompt-caching-1h`, `show-turn-duration`, `clear-context-on-plan`, `disable-bundled-skills`, `pin-sonnet-4-6`; default OFF — `brief`, `thinking-summaries`, `subprocess-env-scrub`, `disable-nonessential-traffic`, `forked-subagents`, `disable-adaptive-thinking`, `always-thinking`, `disable-git-instructions`, `disable-compact`, `disable-1m-context`, `disable-autoupdater`, `agent-teams`. Manageable via `devflow flags --enable/--disable/--status/--list`. Stored in manifest `features.flags: string[]`. View mode (`default`/`verbose`/`focus`) stored in manifest `features.viewMode?: string` and applied to `settings.json` as the `viewMode` key; `applyViewMode`/`stripViewMode` utilities colocated in `flags.ts`.

**Feature Knowledge Bases**: Per-feature `.devflow/features/` directory containing KNOWLEDGE.md files that capture area-specific patterns, conventions, architecture, and gotchas. Uses a **write-through** model: load = direct file-I/O reading `.devflow/features/index.md` (regenerable cache) with frontmatter-glob fallback over `features/*/KNOWLEDGE.md` (source of truth) + verify-against-code on read; save = in-command write-through via a simplified Knowledge agent that writes `KNOWLEDGE.md` + the `index.md` line directly (no `.create-result.json`, no external scripts, no lock). **Git-tracked & shared (amends ADR-021 for `features/`)**: the root `.gitignore` carve-out (`.devflow/*` + level-by-level `!` re-includes, written byte-identically by `ensure-root-gitignore` / `ensureDevflowGitignore`) un-ignores `.devflow/features/index.md` + every `{slug}/KNOWLEDGE.md` while the rest of `.devflow/` stays local; after writing, the **Knowledge agent commits those two paths to the current worktree branch itself** by running git via its Bash tool (scoped `commit --only` pathspec, never `git add -A`, **never push, never force**, no commit script — per the LLM-vs-plumbing principle the commit is the agent's, not a deterministic helper). A user opts back out by re-adding `.devflow/features/` to their own `.gitignore`. Existing installs upgrade once via the versioned `.root-gitignore-configured-v2` marker. Freshness = write-through + verify-on-read (NO git-staleness, NO SessionEnd eval, NO Dream task). `index.md` line format: `- **{slug}** — {areas} — {Use-when description}`; frontmatter is authoritative if the line is lost. MDS module: `commands/_partials/_knowledge.mds` (defines/exports `knowledge_load` and `knowledge_writeback` partials) + 9 host `.mds` sources in `commands/` compiled to plugin commands at build time by `scripts/build-mds.ts` (`npm run build:mds`). `knowledge_load` is used up-front by: implement, plan, resolve, code-review, self-review, research, bug-analysis. `knowledge_writeback` is used at workflow end by: implement, resolve, self-review, explore, debug. explore/debug do NOT load up-front (intentional asymmetry). Config gate: single `knowledge: true|false` in dream config (default true) — gates write-back only; load is ungated. CLI: `devflow knowledge list` (read index.md / frontmatter glob), `devflow knowledge --enable/--disable/--status` (flip config). Note: `/debug` keeps FEATURE_KNOWLEDGE orchestrator-local (investigation workers examine code without pre-loaded context). Toggleable via `devflow knowledge --enable/--disable/--status` or `devflow init --knowledge/--no-knowledge`.
**Feature Knowledge Bases**: Per-feature `.devflow/features/` directory containing KNOWLEDGE.md files that capture area-specific patterns, conventions, architecture, and gotchas. Uses a **write-through** model: load = direct file-I/O reading `.devflow/features/index.md` (regenerable cache) with frontmatter-glob fallback over `features/*/KNOWLEDGE.md` (source of truth) + verify-against-code on read; save = in-command write-through via a simplified Knowledge agent that writes `KNOWLEDGE.md` + the `index.md` line directly (no `.create-result.json`, no external scripts, no lock). **Git-tracked & shared (amends ADR-021 for `features/`)**: the root `.gitignore` carve-out (`.devflow/*` + level-by-level `!` re-includes, written byte-identically by `ensure-root-gitignore` / `ensureDevflowGitignore`) un-ignores `.devflow/features/index.md` + every `{slug}/KNOWLEDGE.md` while the rest of `.devflow/` stays local; after writing, the **Knowledge agent commits those two paths to the current worktree branch itself** by running git via its Bash tool (scoped `commit --only` pathspec, never `git add -A`, **never push, never force**, no commit script — per the LLM-vs-plumbing principle the commit is the agent's, not a deterministic helper). A user opts back out by re-adding `.devflow/features/` to their own `.gitignore`. Existing installs upgrade once via the versioned `.root-gitignore-configured-v2` marker. Freshness = write-through + verify-on-read (NO git-staleness, NO SessionEnd eval, NO Dream task). `index.md` line format: `- **{slug}** — {areas} — {Use-when description}`; frontmatter is authoritative if the line is lost. MDS module: `commands/_partials/_knowledge.mds` (defines/exports `knowledge_load` and `knowledge_writeback` partials) + 9 host `.mds` sources in `commands/` compiled to plugin commands at build time by `scripts/build-mds.ts` (`npm run build:mds`). `knowledge_load` is used up-front by: implement, plan, resolve, code-review, self-review, research, bug-analysis. `knowledge_writeback` is used at workflow end by: implement, resolve, self-review, explore, debug. explore/debug do NOT load up-front (intentional asymmetry). Config gate: single `knowledge: true|false` in feature config (default true) — gates write-back only; load is ungated. CLI: `devflow knowledge list` (read index.md / frontmatter glob), `devflow knowledge --enable/--disable/--status` (flip config). Note: `/debug` keeps FEATURE_KNOWLEDGE orchestrator-local (investigation workers examine code without pre-loaded context). Toggleable via `devflow knowledge --enable/--disable/--status` or `devflow init --knowledge/--no-knowledge`.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Consistency/Documentation] MEDIUM (90%) — 'NO Dream task' is stale; KB renamed to Learning. Fix: change to 'NO Learning task'.

| `--reset` | Removes `.devflow/learning/` state; prints pinned message: `Reset complete — removed .devflow/learning/ state.` |

### Migrations

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Documentation] MEDIUM (90%) — mislabels both migrations as per-project, but rename-global-decisions-config-v1 is global (load-bearing for D37). Fix: relabel one per-project, one global.

* Parse and narrow an unknown JSON value into a FeatureConfig, merging onto
* DEFAULT_CONFIG. Pure function — no I/O, no side effects.
*
* Coalesces legacy `decisions` key into `learning` when both are present:

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Documentation] MEDIUM (85%) — JSDoc says 'mirrors manifest kb→knowledge' but implementation is reversed: knowledge from manifest wins, then decisions→learning. Fix: reword to match precedence.

dean0x and others added 2 commits July 16, 2026 00:37
…tory()

handleConfigure was building the global config path with
`process.env.HOME || '~'`, bypassing the DEVFLOW_DIR override
that loadLearningTuningConfig (the reader) uses via getDevFlowDirectory().
When DEVFLOW_DIR is set, this made the writer and reader disagree on the
global learning.json path, causing a silent write to the wrong location.
The || '~' fallback was also a footgun — a missing HOME would create a
literal ~/ directory rather than throwing a clear error.

Routes the global-scope write through the same getDevFlowDirectory()
accessor already imported in the module so writer and reader are in sync.

Co-Authored-By: Claude <noreply@anthropic.com>
ISS-05: Remove the `writeConfig as writeDreamConfig` alias — import and
call `writeConfig` directly. No behavior change; the alias was a
half-renamed leftover from the Dream→Learning rename.

ISS-08: Update stale comments that still described the system as Dream:
- Line 1146: "dream config" → "feature config"
- Line 1155: strip tombstone "no longer live here — see the dream hook";
  replace with clean end-state (Learning agent handles detection)
- Lines 1169–1171: "spawns the Dream agent" → Learning agent via
  session-start-context directive
- Lines 1203/1206: "dream config.json" / "dream-config.ts" →
  ".devflow/config.json" / "feature-config.ts"

Surviving `[Dd]ream` references are all intentional legacy-sweep entries
(removeDreamHook imports, LEGACY_HOOK_FILES array, migration docs).

applies ADR-003

Co-Authored-By: Claude <noreply@anthropic.com>
dean0x and others added 7 commits July 16, 2026 00:38
…ertion

ISS-01: Guard readFileSync in rename-completeness sweeps against ENOENT.
build-mds.test.ts plants and unlinks _test-*.mds fixtures in commands/
under vitest's default parallelism; a file vanishing between readdir and
readFileSync caused a sporadic crash. A vanished file cannot be a shipping
violation — skip on ENOENT, rethrow all other errors.

ISS-13: Remove vacuous `catch { return; }` around the session-start-context
read — an unreadable/missing hook must fail loudly, not silently pass. Also
tighten the 900s assertion from toMatch(/900/) to toContain('PROCESSING_STALE_SECS=900')
so it matches the exact constant rather than any substring (avoids PF-008).

Co-Authored-By: Claude <noreply@anthropic.com>
…ct-shape guard

ISS-11: Change `LearningTuningConfig.model` from `string` to the closed union
`LearningModelAlias = 'opus' | 'sonnet' | 'haiku'`. The parse path now uses
`isValidModel()` to reject out-of-domain strings before accepting them, matching
the parse-don't-validate principle — on-disk values outside the domain fall
through to `config.model`.

ISS-12: Add the same object-shape guard used by `coerceConfig` in feature-config.ts
before casting the parsed JSON to `Record<string, unknown>`. A JSON array, null,
or primitive can no longer slip through the unsafe `as Record<string, unknown>` cast.

Regression tests added for both issues in tests/decisions/config.test.ts.

Co-Authored-By: Claude <noreply@anthropic.com>
getDecisionsManifestPath, getDecisionsNotificationsPath, and
getDecisionsBatchIdsPath were re-pathed during the dream→learning rename
instead of deleted. Nothing in the repo calls them; the purge-orphaned-
sidecar-judgment-state migration explicitly drops the files they address.

Applies ADR-003: leave a clean end-state, delete dead code rather than
re-pathing it.
- Export DEFAULT_CONFIG so migrations.ts can import it instead of
  hardcoding the same literal (ISS-07a, applies ADR-003)
- Correct the coerceConfig JSDoc that falsely claimed the semantics
  "mirror the manifest kb→knowledge self-heal": the semantics are
  intentionally inverted — legacy-decisions-wins is the opposite of
  manifest.ts's new-key-wins, which is by design for migration-compat
  (ISS-10)

Co-Authored-By: Claude <noreply@anthropic.com>
ISS-08 (comment-only): update stale "Dream" references to "Learning"
  - "directive-spawned Dream agent" → "Learning agent" (~line 829)
  - "next Dream run" → "next Learning-agent render" (~line 888)

ISS-07b: extract seedFeatureConfigFromDream helper; use DEFAULT_CONFIG
  - Import DEFAULT_CONFIG + FeatureConfig type from feature-config.ts
  - Extract step-1 config parsing into a named helper that reads ONLY
    the `decisions` key and ignores `learning` (migration-compat guard);
    starts from DEFAULT_CONFIG so future fields default correctly
  - Replace inline hardcoded `{memory:true, learning:true, knowledge:true}`
    literal with `{...DEFAULT_CONFIG}` (applies ADR-003)
  - Existing test CL-5 verifies the critical semantic guard: decisions-wins
    over a pre-existing learning key (test green before and after)

ISS-14: unlink orphaned file-type entries before moveDirContents
  - fs.unlink .decisions-usage.json / .decisions-manifest.json /
    .decisions-notifications.json / .decisions-batch-ids from decisions/
    BEFORE moveDirContents so they cannot block the best-effort rmdir
    (applies ADR-003 clean end-state; ENOENT-tolerant, rethrows others)
  - decisionsSkip set updated: "drop" comments → "defence-in-depth" for
    the now-unlinked entries

ISS-02: guard renderDecisionsIndex; move before rmdirs; injectable path
  - Add rendererPath?: string to PerProjectMigrationContext for tests
  - Move render call to BEFORE the best-effort rmdirs (step 6 before 7)
    so the ledger is already in learning/ when the renderer reads it
  - Wrap in try/catch that appends a non-fatal warning and continues;
    a render-lock timeout (30 s) no longer strands the migration
  - Forward ctx.rendererPath to renderDecisionsIndex opts

Co-Authored-By: Claude <noreply@anthropic.com>
…e comment

- Rewrite seedFeatureConfigFromDream to use single-return ternary style,
  consistent with coerceConfig in feature-config.ts. Eliminates the
  locally-mutated spread variable; semantics are unchanged (p.decisions
  maps to learning, p.learning is still intentionally ignored).
- Remove // FIX: tombstone comment from handleConfigure in learning.ts;
  getLearningDir() is self-documenting.
- CLAUDE.md: 'NO Dream task' -> 'NO Learning task' (ISS-08b)
- learning-capture-system KB: label migrations per-project vs global; rename-global-decisions-config-v1 is scope:global (ISS-09)

applies ADR-003
Finish the incomplete rename by clearing genuine residue while leaving
must-stay historical anchors intact.

- .gitignore: fix the agent ignore rule (dream.md → learning.md) so the
  generated Learning agent copy stops leaking into git; refresh the stale
  runtime-dir comment
- untrack leaked build-output agent copies: plugins/devflow-{ambient,
  core-skills}/agents/learning.md and plugins/devflow-plan/agents/designer.md
- rename stale "dream" comments/identifiers to learning in
  background-memory-update, init.ts, knowledge/toggle.ts, and the
  dreamDir → learningDir param in learning-queue-cleanup.ts
- fix dangling dream-curation.test.ts reference → learning-curation.test.ts

Left unchanged by design: migration IDs, legacy-hook cleanup literals,
guard tests, and sweepLegacyDreamMarkers (which documents the retired
dream pipeline it sweeps).
@dean0x
dean0x merged commit 1928f42 into main Jul 16, 2026
2 checks passed
@dean0x
dean0x deleted the refactor/rename-dream-to-learning branch July 16, 2026 11:43
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