Skip to content

feat: simplify ambient mode to plan detection + command awareness#227

Merged
dean0x merged 11 commits into
mainfrom
feat/ambient-mode
May 26, 2026
Merged

feat: simplify ambient mode to plan detection + command awareness#227
dean0x merged 11 commits into
mainfrom
feat/ambient-mode

Conversation

@dean0x
Copy link
Copy Markdown
Owner

@dean0x dean0x commented May 25, 2026

Summary

Implements ambient mode for Devflow — a zero-overhead session enhancement system with plan auto-detection and command awareness. Ambient mode consists of two independent, toggleable components:

  1. Plan Auto-Detection: UserPromptSubmit hook detects structured implementation plans (marked with ## Goal, ## Steps, ## Files) and emits a directive to invoke devflow:implement via the Skill tool.
  2. Command Awareness: Always-on rule installed to ~/.claude/rules/devflow/commands.md documents available /devflow:<name> commands and the auto-execution trigger.

Key Features

  • Zero Overhead: Plan detection hook only outputs when a plan is detected; normal prompts are unaffected
  • Toggleable: Enable/disable via devflow ambient --enable/--disable or devflow init
  • Independent Components: Can be configured separately
  • Passive Reference: Commands rule provides documentation without changing behavior

Implementation Details

  • Ambient mode state stored in manifest features.ambient: boolean
  • Hook implementation via preamble hook for plan detection
  • Commands rule managed directly by ambient.ts command (not through rules plugin system)
  • SessionStart hook injects plan marker context if plan is detected in current session
  • Feature toggleable separately from other Devflow systems (memory, learning, decisions, knowledge, rules)

Testing

Added comprehensive tests covering:

  • Hook addition/removal lifecycle
  • Rule file creation with stable content hash
  • Ambient mode enable/disable transitions
  • Edge cases (already enabled, already disabled, invalid states)
  • Cleanup of stale hooks/rules

Closes #220

Co-Authored-By: Claude [email protected]

Dean Sharon added 3 commits May 25, 2026 21:35
Replace 4-layer classification pipeline (~950 lines removed) with:
- preamble hook: deterministic plan detection (## Goal/Steps/Files markers)
- shared/rules/commands.md: passive command awareness for all sessions

Removes router skill, 7 triage skills, 7 guided skills, and
session-start-classification hook. addAmbientHook/removeAmbientHook
are now async (write/delete commands rule to disk). Updates all
callers (init.ts, uninstall.ts), registries (plugin.json, plugins.ts),
tests, and reference documentation.

applies ADR-001
… LEGACY_HOOK_FILES for disk cleanup - Update skill count 66->51, rule count 12->13 in CLAUDE.md and file-organization.md - Include unstaged Coder simplifications (hasAmbientHook, dead helpers removal)
@dean0x
Copy link
Copy Markdown
Owner Author

dean0x commented May 25, 2026

src/cli/commands/ambient.ts:26 — Dual-ownership of commands rule content (90% confidence)

The COMMANDS_RULE_CONTENT string literal duplicates shared/rules/commands.md. This creates a maintenance hazard — any future edit to the command list must be made in two places. The PR description notes this is managed by ambient.ts directly, but keeping identical content in two locations risks drift.

Fix: Read the rule content from the shared source at build time instead of embedding as a constant, OR remove shared/rules/commands.md entirely if ambient.ts is the sole source of truth.

@dean0x
Copy link
Copy Markdown
Owner Author

dean0x commented May 25, 2026

src/cli/commands/ambient.ts:95 — addAmbientHook mixes I/O with JSON transformation (85% confidence)

This function now performs filesystem operations (fs.mkdir, fs.writeFile) as side effects while also transforming settings JSON. This violates separation of concerns — the function name suggests it manages hooks in settings, but it also manages files on disk. Callers don't expect I/O side effects from what appears to be a pure JSON transformation.

Fix: Extract rule file management into a separate function that callers invoke explicitly:

const updated = addAmbientHook(settingsJson, devflowDir); // pure JSON
await writeCommandsRule(); // explicit side effect

@dean0x
Copy link
Copy Markdown
Owner Author

dean0x commented May 25, 2026

src/cli/commands/ambient.ts:141 — removeAmbientHook drops stale hook cleanup (92% confidence - BLOCKING)

Line 145 calls filterHookEntries(settings, 'SessionStart', isClassification) to clean stale hooks, but the return value is discarded. If a user has ONLY a stale classification hook (no UserPromptSubmit), the mutation is lost because line 154 only checks removedPrompt.

Fix: Capture and check both:

const removedClassification = filterHookEntries(settings, 'SessionStart', isClassification);
if (!removedPrompt && !removedClassification) return settingsJson;

@dean0x
Copy link
Copy Markdown
Owner Author

dean0x commented May 25, 2026

src/cli/commands/ambient.ts:148 — Error swallowing in removeAmbientHook (82% confidence)

The catch block swallows ALL fs.unlink errors, not just ENOENT. Permission errors are hidden, leaving stale rule files active while the user believes ambient mode is disabled.

Fix: Only swallow ENOENT:

try {
  await fs.unlink(COMMANDS_RULE_PATH);
} catch (err: unknown) {
  if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
    throw err;
  }
}

@dean0x
Copy link
Copy Markdown
Owner Author

dean0x commented May 25, 2026

src/cli/plugins.ts — LEGACY_SKILL_NAMES missing namespaced entries (95% confidence - BLOCKING)

17 skill directories were deleted, including 15 with devflow: namespaces. Existing users who run devflow init will retain these stale directories because LEGACY_SKILL_NAMES lacks the namespaced entries.

Add to LEGACY_SKILL_NAMES:

// v2.x ambient simplification: deleted router, triage, and guided skills
'devflow:router',
'devflow:implement:triage', 'devflow:implement:guided',
'devflow:debug:triage', 'devflow:debug:guided',
'devflow:explore:triage', 'devflow:explore:guided',
'devflow:plan:triage', 'devflow:plan:guided',
'devflow:review:triage', 'devflow:review:guided',
'devflow:research:triage', 'devflow:research:guided',
'devflow:release:triage', 'devflow:release:guided',

@dean0x
Copy link
Copy Markdown
Owner Author

dean0x commented May 25, 2026

shared/rules/commands.md:24 — False documentation claim (92% confidence)

The file claims detection fires only on "the first message in a session", but scripts/hooks/preamble checks every prompt with no first-message guard. The CLAUDE.md description is accurate ("when a prompt contains"), making this documentation actively misleading.

Fix: Change "When the first message in a session is..." to "When a prompt is..."

@dean0x
Copy link
Copy Markdown
Owner Author

dean0x commented May 25, 2026

tests/ambient.test.ts:17-140 — Unit tests write real files (90% confidence - BLOCKING)

addAmbientHook and removeAmbientHook now call fs.mkdir, fs.writeFile, and fs.unlink on the real filesystem during unit tests. This violates test isolation — tests must not produce side effects on the developer's home directory.

Fix: Mock filesystem operations:

beforeEach(() => {
  vi.spyOn(fs, 'mkdir').mockResolvedValue(undefined);
  vi.spyOn(fs, 'writeFile').mockResolvedValue();
  vi.spyOn(fs, 'unlink').mockResolvedValue();
});

afterEach(() => {
  vi.restoreAllMocks();
});

@dean0x
Copy link
Copy Markdown
Owner Author

dean0x commented May 25, 2026

PR #227 Review Summary

Review Timestamp: 2026-05-25_2233
High-Confidence Findings: 7 (≥80%)
Created: 7 inline comments above
Files Reviewed: 10

Lower-Confidence Issues (60-79%) — Summary Only

  • Architecture: Missing function to distinguish hasAmbientHook and commands rule presence (82% MEDIUM)
  • Documentation: README rule count not updated (88% HIGH) — says "12" but should be "13"
  • Documentation: README "See it work" example implies auto-execution without classification pipeline (85% MEDIUM)
  • Regression: orphaned shared/rules/commands.md from build system (85% MEDIUM)
  • Performance: Unconditional file writes in addAmbientHook (70% MEDIUM)
  • Testing: Missing stale classification hook edge case test (82% MEDIUM)
  • Testing: Integration test bypasses actual preamble hook with hardcoded systemPrompt (80% MEDIUM)
  • Testing: Preamble hook pattern-matching has no direct unit test coverage (85% MEDIUM)
  • Security: Plan marker detection could match unintended content (65% LOW)

Recommendation

CHANGES_REQUESTED — Fix the 7 blocking issues (marked above) before merge. The PR achieves a significant and well-motivated architectural simplification, but the dual-source rule content, function side-effect mixing, stale hook cleanup bug, and missing legacy cleanup entries must be resolved for production quality.


Review conducted by devflow:git agent with 90% minimum inline comment confidence threshold. See .devflow/docs/reviews/feat-ambient-mode/2026-05-25_2233/ for full detail.

Dean Sharon and others added 4 commits May 25, 2026 22:59
…arification

- commands.md / ambient.ts: "first message in a session" was wrong — the preamble
  hook fires on every prompt, not just the first. Changed to "When a prompt is a
  structured implementation plan" to match CLAUDE.md and actual hook behavior.
- README.md: update rule count from 12 to 13 to reflect the addition of commands.md.
- plugins/devflow-ambient/README.md: plan detection wording aligned with the above;
  Skills section now clarifies that only implement:orch is auto-triggered by plan
  detection — the other 8 orch skills are invoked via explicit slash commands.

Co-Authored-By: Claude <[email protected]>
…narrow ENOENT catch, add legacy prefixed skill names

- Extract installCommandsRule() and removeCommandsRule() as standalone
  exported async functions, keeping addAmbientHook/removeAmbientHook as
  the public API (SRP: rule file I/O isolated and independently testable)
- Fix removeAmbientHook discarding filterHookEntries return value for
  SessionStart classification cleanup — stale-only configs now serialize
  correctly and the idempotency guard checks both removals
- Narrow catch block in removeCommandsRule to ENOENT only; EACCES and
  other permission errors now propagate instead of being silently eaten
- Add 15 devflow:-prefixed triage/guided/router skill names to
  LEGACY_SKILL_NAMES so existing user installs are cleaned up on next
  devflow init

Co-Authored-By: Claude <[email protected]>
- Stub fs.mkdir/writeFile/unlink in addAmbientHook, removeAmbientHook, and
  hasAmbientHook describe blocks so unit tests no longer write to or delete
  ~/.claude/rules/devflow/commands.md during test runs
- Add 'removes stale classification hook when no UserPromptSubmit hooks exist'
  to cover the removedClassification-only return path in removeAmbientHook
- Add 'matches shared/rules/commands.md source file' sync guard to the
  COMMANDS_RULE_CONTENT block to catch drift between the two sources

Co-Authored-By: Claude <[email protected]>
Simplify the guard condition in filterHookEntries — settings.hooks is
guaranteed to exist at that point in the function (checked in line 60).

Remove unused COMMANDS_RULE_PATH export from test imports.

Co-Authored-By: Claude <[email protected]>
@dean0x
Copy link
Copy Markdown
Owner Author

dean0x commented May 25, 2026

Code Review — High-Confidence Findings (≥80%)

Inline Comments Posted Below

Summary: 4 high-confidence issues identified:

  • 2 security concerns (pre-existing broad catch blocks)
  • 1 architecture concern (dual-source content duplication)
  • 1 blocking test gap (new exported helpers lack direct tests)

All findings are detailed as inline comments on the affected lines. Pre-existing issues are marked as such; the testing gap is a blocking issue for this PR.

@dean0x
Copy link
Copy Markdown
Owner Author

dean0x commented May 25, 2026

Inline Comments: High-Confidence Findings (≥80%)

Below are the high-confidence blocking and should-fix issues identified in the review:

1. Testing Gap (BLOCKING) - src/cli/commands/ambient.ts:93-109

Missing direct tests for extracted helper functions (Confidence: 82%)

The new exported functions installCommandsRule() and removeCommandsRule() are tested only indirectly via addAmbientHook/removeAmbientHook with fs mocked. There are no direct unit tests for:

  • ENOENT handling in removeCommandsRule() (line 107) — should swallow, not re-throw
  • Non-ENOENT errors in removeCommandsRule() — should re-throw
  • mkdir + writeFile flow in installCommandsRule()

Suggestion: Add describe blocks with direct mocks for each helper.


2. Security (Should-Fix) - src/cli/commands/ambient.ts:217

Broad catch swallows all errors in settings.json read (Confidence: 82%, Pre-existing)

The catch block catches all errors (ENOENT, EACCES, etc.). If settings.json exists with permission issues or is corrupted, the code silently creates '{}' and overwrites the file, potentially destroying configuration.

This mirrors the issue you correctly fixed in removeCommandsRule() at line 107. For consistency, narrow the catch to ENOENT only.


3. Security (Should-Fix) - src/cli/commands/ambient.ts:244

Broad catch at JSON.parse (Confidence: 80%, Pre-existing)

The catch swallows any error from JSON.parse(settingsContent). Silent error swallowing could mask filesystem permission issues.

Suggestion: Add a comment explaining the fallback is safe, or narrow to specific error types.


4. Architecture (Should-Fix) - src/cli/commands/ambient.ts:26-52

Dual-source content duplication (Confidence: 82%)

The COMMANDS_RULE_CONTENT constant is a verbatim copy of shared/rules/commands.md. Although the new sync test (lines 383–389) guards against drift, there are now two sources of truth for the same content.

The sync test is a good stopgap, but architectural direction should be single-source. Consider a build-time approach or runtime file read.


Summary

Category Count
Blocking Tests 1
Should-Fix (Pre-existing Security) 2
Should-Fix (Architecture) 1

Recommendation: Address the test gap before merge. The others improve the codebase.


Review completed by devflow:git — High-confidence findings (≥80%) only

Dean Sharon and others added 4 commits May 26, 2026 00:09
…er tests

- Narrow settings.json read catch to ENOENT only (was silently swallowing
  EACCES and other errors, risking config overwrite on permission failure)
- Use getDevFlowDirectory() as primary devflowDir source instead of Stop hook
  path heuristic; keep hook-path inference as fallback for legacy installs only;
  log unexpected JSON.parse errors instead of swallowing them silently
- Add direct unit tests for installCommandsRule (mkdir+writeFile path) and
  removeCommandsRule (ENOENT swallowed, non-ENOENT re-thrown)

Co-Authored-By: Claude <[email protected]>
…LL_NAMES by era

- Replace sequential fs.rm() loop with Promise.allSettled() so all 224+
  legacy skill deletions run concurrently rather than serially; reduces
  cleanup latency on slow/network filesystems
- Split flat LEGACY_SKILL_NAMES array into three era-scoped sub-arrays
  (LEGACY_SKILLS_PRE_V1, LEGACY_SKILLS_V2, LEGACY_SKILLS_V2X) composed
  via spread; makes it easy to scan for duplicates and understand the
  evolution of the list without changing observable behaviour

Co-Authored-By: Claude <[email protected]>
…all 9 :orch variant skills (debug, explore, implement, pipeline, plan, release, research, resolve, review) as part of simplifying ambient mode to plan detection + command awareness. These orchestration skills duplicated logic already present in commands. - Delete shared/skills/*:orch/ (9 skills, ~1775 lines removed) - Remove :orch refs from plugin manifests (ambient, release, research) - Add devflow:-prefixed orch names to LEGACY_SKILL_NAMES for cleanup - Remove SHADOW_RENAMES entries for deleted orch skills - Update skill count 51->42 in CLAUDE.md and file-organization.md - Update skill-catalog.md: orch->command terminology - Update skills-architecture.md: remove orchestration tier - Update commands (debug, implement, release) with enhancements - Update tests: remove orch-dependent assertions, fix test data - Fix stale comment in decisions-citation.test.ts
…ion Remove stale orch skill references and old QUICK/GUIDED/ORCHESTRATED terminology from docs that were missed in the main refactor commit.
@dean0x dean0x merged commit c51114d into main May 26, 2026
3 of 4 checks passed
@dean0x dean0x deleted the feat/ambient-mode branch May 26, 2026 20:36
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