ADR-001: Dual-Mode Agent System¶
Date: 2025-10-26 Status: Accepted
Context¶
Nova AI needs to support two distinct execution environments:
-
Local Development: Developers working interactively at their keyboards, where prompting before risky operations (git push, file deletion, API calls) provides safety and allows for human oversight.
-
CI/CD Pipelines: Automated workflows in GitHub Actions where user prompts are impossible and would cause pipeline hangs. These environments require fully autonomous execution.
Initially, we maintained separate agent files for each mode (20 total agents):
- 10 interactive agents in .claude/agents/interactive/
- 10 autonomous agents in .claude/agents/autonomous/
This resulted in 95% duplicate content, high maintenance burden, and confusion about which agent to use in different contexts.
Key Requirements¶
- Support both interactive (prompt-based) and autonomous (no-prompt) execution
- Minimize code duplication between modes
- Make mode selection automatic and transparent
- Maintain safety in both environments
- Enable easy testing and debugging
Decision¶
We implemented a unified dual-mode agent system where:
-
Single Agent File: Each agent exists as one file (e.g.,
code-reviewer.md) instead of two separate files -
Automatic Mode Detection: The system automatically detects execution mode based on environment variables:
-
YAML Frontmatter Configuration: Agents specify behavior for both modes in their frontmatter:
-
Agent Consolidation: Reduced from 20 agent files to 10 unified agents
-
Explicit Override: Users can override auto-detection by setting
NOVA_AGENT_MODE=interactive|autonomous
Implementation¶
In claude_sdk_executor.py:
def load_agent(self, agent_name: str) -> Dict[str, Any]:
"""Load agent with automatic mode detection."""
mode = self._detect_mode()
# Load single agent file
agent_path = self.project_root / ".claude" / "agents" / f"{agent_name}.md"
# Parse frontmatter and apply mode-specific settings
agent_config = self._parse_agent_frontmatter(agent_path, mode)
return agent_config
Environment Detection:
- CI/CD: CI=true or GITHUB_ACTIONS=true → autonomous mode
- Local: No env vars → interactive mode
- Override: NOVA_AGENT_MODE=autonomous → forces autonomous mode
Consequences¶
Positive¶
- 50% Reduction in Agent Files: 20 files → 10 files
- 95% Less Duplication: Shared content maintained once
- Automatic Context Awareness: No manual mode selection needed
- Easier Maintenance: Updates to agent behavior happen in one place
- Clearer Intent: Agent files explicitly document both interaction patterns
- Better Testing: Easy to test both modes by setting environment variables
- Backward Compatible: Old code using
-autonomoussuffix still works (with deprecation warning)
Negative¶
- Slightly More Complex Frontmatter: Agents need to specify behavior for both modes
- Initial Migration Effort: Required consolidating 20 files into 10
- Testing Requirement: Must test agents in both modes to ensure correctness
Trade-offs¶
Considered Alternatives:
- Separate Files (Original Approach)
- ❌ 95% duplication
- ❌ High maintenance burden
- ❌ Easy to forget updating both versions
-
✅ Simple to understand
-
Single File with Manual Mode Parameter
- ❌ Requires passing mode explicitly everywhere
- ❌ Easy to forget mode in CI/CD
-
✅ No environment detection complexity
-
Runtime Permission Prompts (Chosen Alternative)
- ❌ Would hang CI/CD pipelines
-
✅ Good UX for local development only
-
Configuration File per Environment
- ❌ Separate config to maintain
- ❌ Unclear which config applies in edge cases
- ✅ More explicit
Why We Chose Dual-Mode: - Automatic mode detection eliminates human error - Single source of truth for agent behavior - Clear separation of concerns (behavior vs execution context) - Scales to future execution environments (Docker, Lambda, etc.)
Implementation Timeline¶
- Week 1: Created mode detection logic in
claude_sdk_executor.py - Week 2: Added frontmatter parser with mode-specific sections
- Week 3: Migrated 10 agent pairs to unified format
- Week 4: Deprecated old agent files with migration warnings
- Week 5: Removed deprecated files after validation
Related Decisions¶
- See ADR-004: Model Selection Strategy for how modes influence model choice
Validation¶
Tested dual-mode system with:
- ✅ Local development (interactive mode)
- ✅ GitHub Actions (autonomous mode)
- ✅ Explicit mode override (NOVA_AGENT_MODE=autonomous)
- ✅ All 10 agents in both modes
- ✅ Backward compatibility with old -autonomous suffix
References¶
- Implementation:
src/orchestrator/claude_sdk_executor.py - Mode Detection:
src/orchestrator/agent_mode_detector.py(production) - Agent Examples:
.claude/agents/code-reviewer.md,.claude/agents/tester.md - Documentation:
AGENT_MERGE_EXECUTIVE_SUMMARY.md - Test Suite:
tests/agents/test_mode_detection.py
Status Update (October 26, 2025)¶
Production Implementation: agent_mode_detector.py is the active production module used by claude_sdk_executor.py.
Deprecated Modules:
- mode_selector.py - Removed (redundant with agent_mode_detector.py, 0% production usage)
- Original test suite relocated to tests/agents/test_mode_detection.py