- Add 6 retrieval-augmented routing tests (3 live retrieval, 3 off-topic
fake memories) to unblock Phase 5
- Defer Composio tool loading and graph construction to first use so
expired or missing keys don't crash imports
- Atomic cache write in retriever via temp file (open item #2)
- Log rotation in weekly_summary.py, pruning JSONL >90 days (open item #3)
CI's `pytest --collect-only` was exiting 5 (no tests collected) because
the test module used `pytest.skip(..., allow_module_level=True)` at
import time — skipped modules never enter the collection phase.
Switch to a per-test `skipif` marker driven by ANTHROPIC_API_KEY +
COMPOSIO_API_KEY presence, and lazy-import `graph.router_node` inside
the test body so module import works without COMPOSIO_API_KEY.
Result: 21 tests collect in both environments; live tests run only
when both keys are present. Verified locally with env -i.
Phase 1 stabilization. Removes the four-copy prompt/agent-description drift
surface and the silent router fallback.
- models.py: hoist model IDs to module-level constants; add with_retries()
helper (2 retries on Anthropic+OpenAI transient errors via with_retry).
- agents.py: single AGENTS dict (model_fn, prompt, description) and a
make_agent_node() factory that collapses six near-identical node functions.
- graph.py: router prompt is generated from AGENTS; router_node uses
with_structured_output(RouteDecision) and returns an explicit "unknown"
route instead of the silent "researcher" fallback. New unknown_node wires
to END. All LLM invocations go through with_retries.
- state.py: add "unknown" to the route Literal.
- run.py: --route-only now imports router_node from graph.py, killing the
fourth prompt copy.
- tests/: pytest golden-set (20 labelled tasks + size guard). Skips cleanly
without ANTHROPIC_API_KEY or COMPOSIO_API_KEY. Validated 21/21 passing.