{ "intent": "Bring code_tutor/_llm.py under the 250-line cap by moving the Anthropic-API backend into its own module, clearing the last code_tutor source violation.", "scope": [ "python_pkg/code_tutor/_claude_backend.py (new)", "python_pkg/code_tutor/_llm.py (295 -> 217)", "python_pkg/code_tutor/tests/test_llm.py (import and patch paths only)", "Non-goal: no assertion changed, no test deleted, skipped or weakened" ], "changes": [ "Moved ClaudeBackend into _claude_backend.py. The seam falls between the two backends precisely because each pins a different plain import: test_llm.py patches requests.post on _llm for OllamaBackend and anthropic.Anthropic for ClaudeBackend, so separating the classes separates the two pinned modules cleanly.", "_llm.py keeps its anthropic import, because complete_with_fallback still catches anthropic.APIError, and imports ClaudeBackend back since it constructs one.", "Repointed the four anthropic patch strings belonging to the ClaudeBackend tests. The two in complete_with_fallback tests stay on _llm, since that is the module whose globals the except clause resolves through." ], "verification": [ { "command": "timeout 90 env PYTHONPATH=. python3 -m pytest python_pkg/code_tutor/tests -q", "result": "pass", "evidence": "217 passed - unchanged from the count established by the previous commit." }, { "command": "PYTHONPATH=. python3 -c \"from python_pkg.code_tutor import _llm as l, _claude_backend as c; l.requests; l.anthropic; l.ClaudeBackend; l.OllamaBackend; c.anthropic; c.ClaudeBackend\"", "result": "pass", "evidence": "resolve OK - anthropic is a real attribute of both modules, which is what makes the per-test split of its patch strings valid." }, { "command": "bash ~/utils/scripts/check_file_length.sh --all | grep code_tutor", "result": "pass", "evidence": "Only test modules are listed now: every code_tutor source file is under the 250-line cap." }, { "command": "pre-commit run --files ", "result": "pass", "evidence": "ruff flagged E501 on a patch string the longer module name pushed past 88 columns; auto-formatted, then clean. pytest-with-coverage passes at 100%." } ], "risks": [ "anthropic is now imported by two modules and patched on both, for different reasons. A future change that stops complete_with_fallback catching APIError would make the _llm import unused, and ruff would strip it and break the two patches that remain there.", "_llm imports ClaudeBackend from _claude_backend; the reverse import would be a cycle." ], "rollback": [ "git revert the commit; _llm.py returns to 295 lines and _claude_backend.py disappears.", "After rollback, re-run the package suite and confirm it still reports 217 passed." ] }