From the 2026-06-09 Architecture Review. Recommendation strength: Worth exploring.
⚠️ Touches ADR-0001 territory but does not contradict it. ADR-0001 deliberately deferred unifying scan-state signals; this change keeps both signal behaviours exactly as they are and only gives each a name and a seam. The divergence stays intentional and becomes visible in the type system instead of living in two ad-hoc functions plus ADR prose.
Files
src/codeforerunner/prompt_session.py
src/codeforerunner/cli.py:17-23 (_scan_satisfied(): artifact ∨ FORERUNNER_SCAN_DONE env ∨ absent config)
src/codeforerunner/mcp_server.py:88-124 (artifact ∨ in-process scan_called flag)
Problem
The Prompt Session owns the scan-first rule, but the contract a scan-signal source must satisfy is implicit: each adapter computes a bare scan_satisfied: bool its own way and injects it. A third adapter (a new delivery channel for Agent Onboarding, say) would have to rediscover both strategies from source and ADR prose, and could drift.
Solution
Introduce a small scan-signal protocol with two sealed implementations — CliScanSignal (artifact ∨ env ∨ absent-config) and McpScanSignal (artifact ∨ in-process flag). PromptSession accepts the adapter instead of a raw bool. Observable behaviour is identical for both adapters.
Benefits
- Seam: two adapters already exist, so the seam is real, not hypothetical.
- Tests:
test_prompt_session.py parameterizes the gate rule over both signal adapters (plus a fake), instead of passing bare booleans.
- Locality: ADR-0001's rule/signal split becomes visible in code rather than folklore.
From the 2026-06-09 Architecture Review. Recommendation strength: Worth exploring.
Files
src/codeforerunner/prompt_session.pysrc/codeforerunner/cli.py:17-23(_scan_satisfied(): artifact ∨FORERUNNER_SCAN_DONEenv ∨ absent config)src/codeforerunner/mcp_server.py:88-124(artifact ∨ in-processscan_calledflag)Problem
The Prompt Session owns the scan-first rule, but the contract a scan-signal source must satisfy is implicit: each adapter computes a bare
scan_satisfied: boolits own way and injects it. A third adapter (a new delivery channel for Agent Onboarding, say) would have to rediscover both strategies from source and ADR prose, and could drift.Solution
Introduce a small scan-signal protocol with two sealed implementations —
CliScanSignal(artifact ∨ env ∨ absent-config) andMcpScanSignal(artifact ∨ in-process flag).PromptSessionaccepts the adapter instead of a raw bool. Observable behaviour is identical for both adapters.Benefits
test_prompt_session.pyparameterizes the gate rule over both signal adapters (plus a fake), instead of passing bare booleans.