airc-queue card
```json
{
"kind": "airc-queue-card-v1",
"status": "claimed",
"owner": "airc-8a5e",
"next_action": "Ship single-PR delegation + dead-code delete."
}
```
Smell
src/commands/ai/should-respond/server/AIShouldRespondServerCommand.ts (147 LOC) is a parallel reimplementation of gating — separate from the cognition/should-respond Rust path that AIDecisionService.evaluateGating already delegates to. The TS command:
- Builds its own gating instruction with a different prompt template
- Calls
AIProviderDaemon.generateText directly
- Implements a JSON-repair-via-second-LLM retry loop when parse fails
- Returns the same
AIShouldRespondResult shape
This is exactly the anti-pattern Joel called out: parallel paths doing the same logical thing with different code, one not using the Rust source of truth. Both prompts can drift independently.
Scope (single PR — atomic)
Replace execute() body with a thin delegation to client.cognitionShouldRespond(...). The Rust prompt template (cognition/should_respond.rs::build_gating_prompt) becomes the only gating prompt. Drop:
import { AIProviderDaemon } + import { LOCAL_MODELS } (if no other uses)
- Inline message-array construction
- JSON-repair retry path (Rust returns typed errors; caller decides retry policy)
buildGatingInstruction + parseGatingResponse (parent class — verify no other callers; remove if orphaned)
verbose debug fields that expose promptSent (Rust owns prompt; operator inspects Rust logs)
Verbose mode keeps ragContext.messageCount + ragContext.conversationPreview (TS-derivable) but drops promptSent + aiResponse (Rust-side).
Why atomic
cognition/should-respond IPC already exists + has 28+ tests + is in production via AIDecisionService.evaluateGating. No new Rust work needed — this is pure TS deletion + delegation.
Refs
Status log
- 2026-05-18T19:09Z — created + claimed by airc-8a5e. Starting now.
airc-queue card
```json
{
"kind": "airc-queue-card-v1",
"status": "claimed",
"owner": "airc-8a5e",
"next_action": "Ship single-PR delegation + dead-code delete."
}
```
Smell
src/commands/ai/should-respond/server/AIShouldRespondServerCommand.ts(147 LOC) is a parallel reimplementation of gating — separate from the cognition/should-respond Rust path that AIDecisionService.evaluateGating already delegates to. The TS command:AIProviderDaemon.generateTextdirectlyAIShouldRespondResultshapeThis is exactly the anti-pattern Joel called out: parallel paths doing the same logical thing with different code, one not using the Rust source of truth. Both prompts can drift independently.
Scope (single PR — atomic)
Replace
execute()body with a thin delegation toclient.cognitionShouldRespond(...). The Rust prompt template (cognition/should_respond.rs::build_gating_prompt) becomes the only gating prompt. Drop:import { AIProviderDaemon }+import { LOCAL_MODELS }(if no other uses)buildGatingInstruction+parseGatingResponse(parent class — verify no other callers; remove if orphaned)verbosedebug fields that exposepromptSent(Rust owns prompt; operator inspects Rust logs)Verbose mode keeps
ragContext.messageCount+ragContext.conversationPreview(TS-derivable) but dropspromptSent+aiResponse(Rust-side).Why atomic
cognition/should-respondIPC already exists + has 28+ tests + is in production via AIDecisionService.evaluateGating. No new Rust work needed — this is pure TS deletion + delegation.Refs
cognition/should_respond.rs::evaluate_gating(already shipped, in production)Status log