Context and Problem Statement
spec-kitty init supported a preference system for routing work to specific AI agents:
--preferred-implementer(init.py:735,init.py:911–937): Prompted the user to select their preferred AI agent for implementation tasks (e.g., "Claude", "Codex", "Gemini").--preferred-reviewer(init.py:736,init.py:949–967): Similarly prompted for a preferred reviewer agent.
These preferences were stored in AgentSelectionConfig, serialized to .kittify/config.yaml under an agents.selection block, and loaded at startup by load_agent_config().
The Audit Finding
During the code audit for feature 076, a systematic search was conducted for all call sites of the methods that would act on these preferences:
select_implementer() — defined in core/agent_config.py:57–72
select_reviewer() — defined in core/agent_config.py:75–99
Result: neither method is called anywhere in the codebase. Not from any CLI command, not from any migration, not from any runtime code, not from any test that exercises actual routing. The agent config management CLI (see ADR-6) does not expose either field. The preference data is written to config.yaml and read back into memory during startup — and then never consulted.
This means: every developer who answered the "preferred implementer" prompt during spec-kitty init provided data that was silently discarded. The system was collecting user preferences and doing nothing with them.
Pre-Existing Bug Discovered During Audit
The same audit revealed a related data loss bug in load_agent_config():
# Buggy pre-076 code:
agents_data = config_data.get("agents", {}) # Always reads "agents" key
Migration m_2_0_1 had previously renamed the config key from agents to tools. After that migration ran, load_agent_config() silently read from an empty dict (because "agents" no longer existed) and returned default values — losing all agent configuration for projects that had been upgraded. This bug was discovered and fixed in WP01 by reading tools first with a fallback to agents for pre-migration projects.
The preference audit and the key-rename bug were discovered in the same code path, confirming that the agent config subsystem had not been exercised by any meaningful test coverage.
Decision Drivers
- No speculative features: Implementing a routing system for
select_implementer()/select_reviewer()requires a specification that does not exist. Building the implementation would be speculative. - Remove the data collection antipattern: Collecting user input and silently ignoring it is a user experience failure. If the preference system is not implemented, the prompts should not exist.
- Reduce the
AgentConfigdataclass surface: RemovingAgentSelectionConfigsimplifies serialization, deserialization, documentation, and testing. - Clean up config.yaml: Existing
config.yamlfiles contain anagents.selectionblock that will never be read. A migration should remove it to keep configs clean.
Considered Options
- Option A: Remove the entire preference layer (chosen)
- Option B: Implement the routing feature using the existing preference storage
- Option C: Deprecate with a warning, remove in a future release
Decision Outcome
Chosen option: Option A — Remove AgentSelectionConfig, select_implementer(), select_reviewer(), and all selection serialization. A cleanup migration removes the orphaned data from existing config.yaml files.
What Is Removed
| Removed artifact | Location | Notes |
|---|---|---|
AgentSelectionConfig dataclass |
core/agent_config.py:28–37 |
Entire class deleted |
select_implementer() method |
core/agent_config.py:57–72 |
Never called; deleted |
select_reviewer() method |
core/agent_config.py:75–99 |
Never called; deleted |
selection block in save_agent_config() |
core/agent_config.py:195–196 |
No longer written |
selection block in load_agent_config() |
core/agent_config.py:158–162 |
No longer read |
--preferred-implementer flag |
init.py:735, 911–937 |
Flag and selection stage removed |
--preferred-reviewer flag |
init.py:736, 949–967 |
Flag and selection stage removed |
Cleanup Migration 3.2.1
m_3_2_1_remove_selection_config.py strips the agents.selection block from existing .kittify/config.yaml files:
def apply(self, project_path: Path, dry_run: bool = False) -> MigrationResult:
config_path = project_path / ".kittify" / "config.yaml"
if not config_path.exists():
return MigrationResult.skipped("No config.yaml found")
# Load, strip selection block, write back
data = yaml.safe_load(config_path.read_text())
agents = data.get("agents", data.get("tools", {}))
if "selection" in agents:
del agents["selection"]
if not dry_run:
config_path.write_text(yaml.dump(data))
return MigrationResult.success("Removed agents.selection block")
return MigrationResult.skipped("No selection block found")
The Key-Rename Bug Fix (WP01)
The load_agent_config() fix is documented here for completeness:
# Before (buggy): only reads "agents" key, silent data loss after m_2_0_1 renames to "tools"
agents_data = config_data.get("agents", {})
# After (fixed): reads "tools" first (current), falls back to "agents" (pre-migration)
agents_data = config_data.get("tools", config_data.get("agents", {}))
This fix is in WP01 but is architecturally related to the preference system audit. The preference data was subject to the same silent data loss: after m_2_0_1 ran, the selection block under agents was inaccessible because the key had been renamed to tools. The data was collected, then silently lost on upgrade, then the methods that would read it were never called anyway.
Consequences
Positive
spec-kitty initno longer prompts for preferences that have no effect.AgentConfigdataclass is simpler;core/agent_config.pyis shorter.config.yamlfiles are cleaner; no orphanedselectionblock.- The codebase honestly represents what it can do: no silent data collection.
- Tests for the preference system (which were testing dead code) are removed.
Negative
- If a routing feature is implemented in the future, the preference storage must be re-introduced. The data model is not preserved as a placeholder.
- Developers who explicitly set preferences via
--preferred-implementerhad those preferences silently ignored anyway; removing the option makes the situation transparent rather than creating a new loss.
Neutral
- The
AgentConfigclass still exists; only theselectionsubfield and its two methods are removed. - The
agent config add/remove/listCLI commands (from ADR-6) are unaffected.
Confirmation
Correct behavior is confirmed when: spec-kitty init --help does not list --preferred-implementer or --preferred-reviewer; AgentSelectionConfig does not appear anywhere in core/agent_config.py; and spec-kitty upgrade on a project with an existing agents.selection block removes that block without touching other config values. All three conditions are covered by the test suite.
Pros and Cons of the Options
Option A: Remove the entire preference layer (chosen)
Delete AgentSelectionConfig, both methods, all serialization, both flags. Cleanup migration removes orphaned config data.
Pros:
- Honest codebase: no data is collected that isn't used.
- Simpler
AgentConfigsurface. - No prompts for non-functional choices.
- Cleanup migration removes orphaned config data from existing projects.
Cons:
- If routing is implemented in the future, preference storage must be re-added.
Option B: Implement the routing feature
Keep AgentSelectionConfig and the preference storage, and implement actual routing logic so that select_implementer() and select_reviewer() are called from the workflow commands.
Pros:
- Realizes the intended value of the preference system.
- No data model churn (no removal + re-addition cycle).
Cons:
- No specification exists for how routing should work. What happens when the preferred agent is unavailable? How does routing interact with the lane model? How are conflicting preferences resolved across projects?
- Implementing an undocumented feature design adds speculative surface area.
- Feature 076's scope is explicitly limited to cleanup. Implementing a new orchestration capability is out of scope.
Why Rejected: Speculative implementation without a specification is a known cause of architectural debt. The routing concept should be specified properly as a future feature before any implementation work begins.
Option C: Deprecate with a warning, remove in a future release
Print a deprecation notice when --preferred-implementer or --preferred-reviewer is passed. Remove the flags in a subsequent release.
Pros:
- Standard deprecation practice; gives users advance notice.
Cons:
- The flags collect data that has never been acted upon. There are no users depending on routing behavior (because routing never existed). Deprecating a non-functional feature is noise.
- The data is subject to silent loss due to the
m_2_0_1key rename bug. Some users' data was already lost. Deprecation implies users have something to migrate; they do not. - Extends the presence of dead code for at least one release cycle.
Why Rejected: Deprecation is appropriate for functional features with users. Deprecating a data collection prompt that was never connected to any behavior misleads users into thinking there was something to lose. Immediate removal is cleaner and more honest.
More Information
- Spec:
kitty-specs/076-init-command-overhaul/spec.md— FR-017 (preferred_implementerandpreferred_reviewerremoved from data model), FR-018 (select_implementer()andselect_reviewer()methods removed), FR-019 (AgentSelectionConfigremoved entirely), FR-020 (cleanup migration strips orphaned keys fromconfig.yaml) - Related ADR: ADR-6 (2026-01-23-6) — Config-driven agent management (the broader
AgentConfigmodel thatAgentSelectionConfigwas a subcomponent of) - Code locations:
src/specify_cli/core/agent_config.py—AgentSelectionConfig,select_implementer(),select_reviewer(), serialization blockssrc/specify_cli/init.py:735–736, 911–967—--preferred-implementerand--preferred-reviewerflag definitions and selection stagesrc/specify_cli/upgrade/migrations/m_3_2_1_remove_selection_config.py— cleanup migration (new)