fix(corporate-memory): CLI catches fail-fast ValueError, exits 1 with clean message (Devin Review on #179)

The PR's #176 fail-fast change made collect_all() raise ValueError when
neither an ai: block nor ANTHROPIC_API_KEY/LLM_API_KEY was available.
verification_detector's CLI was updated to handle it; corporate_memory's
CLI was missed and crashed with an unhandled traceback.

services/corporate_memory/collector.py:main() now wraps the collect_all
call in try/except ValueError, prints a one-line actionable message
to stderr, and returns rc=1.

Regression test:
test_llm_connector.py::TestCorporateMemoryCollector::test_main_returns_1_on_no_ai_config_instead_of_traceback.
This commit is contained in:
ZdenekSrotyr 2026-05-05 06:45:10 +02:00
parent e68c2d3f0f
commit 9f9aabd72b
3 changed files with 30 additions and 1 deletions

View file

@ -43,6 +43,7 @@ Five-defect fix for the silently-broken session pipeline on default Compose depl
- **Defect 2 — side-car services gated by `profiles: [full]` were silently skipped on default deploys.** Both stanzas dropped (Changed above); the scheduler-v2 cron is the sole driver. - **Defect 2 — side-car services gated by `profiles: [full]` were silently skipped on default deploys.** Both stanzas dropped (Changed above); the scheduler-v2 cron is the sole driver.
- **Defect 1 — `/corporate-memory` filtered `status IN ('approved','mandatory')` with no hint that pending items existed.** Admin banner added (Added above). - **Defect 1 — `/corporate-memory` filtered `status IN ('approved','mandatory')` with no hint that pending items existed.** Admin banner added (Added above).
- **#179 review — `/api/admin/run-session-collector` would SystemExit the worker.** The endpoint called `collector.main()`, whose `argparse.parse_args()` parsed uvicorn's `sys.argv` (`['app.main:app', '--host', …]`) and called `sys.exit(2)` on the unrecognised flags. `SystemExit` inherits from `BaseException`, escapes FastAPI's exception machinery, and propagates through the thread pool — every scheduler tick that fired the endpoint either 500-ed or risked killing the uvicorn worker. Fix: `services/session_collector/collector.py` now exposes an argv-free `run(dry_run, verbose) -> (rc, stats)` helper; `main()` is a thin CLI shim around it and the admin endpoint calls `run()` directly. Audit log now carries the per-run stats (`users_processed`, `files_copied`, `files_skipped`) instead of just the rc. Regression tests in `tests/test_session_collector.py::TestRunHelper`. - **#179 review — `/api/admin/run-session-collector` would SystemExit the worker.** The endpoint called `collector.main()`, whose `argparse.parse_args()` parsed uvicorn's `sys.argv` (`['app.main:app', '--host', …]`) and called `sys.exit(2)` on the unrecognised flags. `SystemExit` inherits from `BaseException`, escapes FastAPI's exception machinery, and propagates through the thread pool — every scheduler tick that fired the endpoint either 500-ed or risked killing the uvicorn worker. Fix: `services/session_collector/collector.py` now exposes an argv-free `run(dry_run, verbose) -> (rc, stats)` helper; `main()` is a thin CLI shim around it and the admin endpoint calls `run()` directly. Audit log now carries the per-run stats (`users_processed`, `files_copied`, `files_skipped`) instead of just the rc. Regression tests in `tests/test_session_collector.py::TestRunHelper`.
- **#179 review — `python -m services.corporate_memory` crashed on missing LLM config instead of exiting cleanly.** The PR's fail-fast change made `collect_all()` raise `ValueError` when neither an `ai:` block nor `ANTHROPIC_API_KEY`/`LLM_API_KEY` was available. The `verification_detector` CLI was updated to catch it; the corporate-memory CLI was missed. Now also wrapped — operators get a one-line `Corporate Memory cannot run: <factory message>` on stderr and rc=1 instead of a raw traceback. Regression test in `tests/test_llm_connector.py::TestCorporateMemoryCollector::test_main_returns_1_on_no_ai_config_instead_of_traceback`.
### Internal ### Internal

View file

@ -24,6 +24,7 @@ import hashlib
import json import json
import logging import logging
import os import os
import sys
import tempfile import tempfile
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
@ -624,7 +625,13 @@ def main() -> int:
print("Data cleared. Running fresh collection...\n") print("Data cleared. Running fresh collection...\n")
logger.info("Starting knowledge collection...") logger.info("Starting knowledge collection...")
stats = collect_all(dry_run=args.dry_run) try:
stats = collect_all(dry_run=args.dry_run)
except ValueError as e:
# collect_all() now fail-fasts on missing ai: config + env (#176).
# Print the actionable message instead of a raw traceback.
print(f"\nCorporate Memory cannot run: {e}", file=sys.stderr)
return 1
print("\nCollection complete:") print("\nCollection complete:")
if stats["skipped"]: if stats["skipped"]:

View file

@ -1057,6 +1057,27 @@ class TestCorporateMemoryCollector:
): ):
collect_all(dry_run=True) collect_all(dry_run=True)
def test_main_returns_1_on_no_ai_config_instead_of_traceback(self, tmp_path, monkeypatch, capsys):
"""CLI main() must catch the new fail-fast ValueError from collect_all() and exit cleanly.
Regression for Devin Review on #179: previously the CLI crashed with an
unhandled traceback when env + ai: config were both missing.
"""
from services.corporate_memory import collector as cm
monkeypatch.setattr("sys.argv", ["corporate_memory"])
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
monkeypatch.delenv("LLM_API_KEY", raising=False)
monkeypatch.setattr(cm, "CORPORATE_MEMORY_DIR", tmp_path / "cm")
monkeypatch.setattr(cm, "COLLECTION_LOG", tmp_path / "cm" / "log")
monkeypatch.setattr(cm, "collect_all", lambda dry_run=False: (_ for _ in ()).throw(ValueError("LLM not configured")))
rc = cm.main()
assert rc == 1
err = capsys.readouterr().err
assert "Corporate Memory cannot run" in err
assert "LLM not configured" in err
# =================================================================== # ===================================================================
# Corporate Memory collector - helper function tests # Corporate Memory collector - helper function tests