refactor(AI-006): single shared verdict-JSON extractor in ai_runtime#100
Conversation
The three Claude-agent subsystems each carried a private _extract_json_object copy (fundamental_agent, technical_agent, sixty_seven/agent). The copies were logic-identical (verified line by line - only docstrings differed), which is the same drift-prone shape as the _run_sync bridge REFACTOR-003 deduplicated after it DID drift once. Two of the copies even said "kept local so the agents stay independent" while being character-for-character the same logic. - backend/ai_runtime.py: add extract_json_object(text) - the tolerant fenced-block-then-outermost-span extractor, verbatim. - Each agent now does `from backend.ai_runtime import extract_json_object as _extract_json_object` so every call site, parse-fallback path, and test seam is unchanged; the now-unused `import re` lines are removed. - ADR amendment (docs/architecture/refactor-003-ai-runtime.md): the extractor met the same shared-and-identical/correctness-critical/ drift-prone test as the bridge; the options-construction blocks remain per-agent (this does NOT reopen Option B). - tests/test_ai_runtime.py: 8 extractor tests (bare object, json fence, fence without language tag, surrounding prose, empty text, no braces, unparseable braces, reversed braces). Behavior lock: the three agent suites (212 tests) pass unmodified. Gates: 1,394 passed, coverage 88.17% (floor 87); pre-commit validate, compileall, ruff, mypy (119 files), bandit, pip-audit all clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
DoRmAmMu1997
left a comment
There was a problem hiding this comment.
Self-review (posted as COMMENT — the bot runs as the PR author and cannot APPROVE). Verdict: Approve.
Checks performed against the diff:
- Verbatim move — the three replaced copies were compared line-by-line before the move: same fence regex, same
find/rfindoutermost-span fallback, samejson.JSONDecodeErrorguard, sameisinstance(parsed, dict)gate. Only docstrings differed. The shared implementation is character-identical in logic to all three. - Call sites untouched — the aliased import (
extract_json_object as _extract_json_object) re-creates each module's private global, so the three internal call sites resolve identically; repo-wide grep confirms no test references the private name. - Behavior lock holds — the three agent suites (212 tests) pass with zero modifications to their files, and 8 new extractor tests in
tests/test_ai_runtime.pycover the ticket-spec cases (fenced / prose-wrapped / no-JSON) plus edges (missing language tag, empty text, unparseable braces, reversed braces →None, not an exception). - No cycle, no dead imports —
ai_runtimeremains a stdlib-only leaf;import reremoved from all three agents (no remainingre.usage);jsonretained where still used. - ADR amendment — honest about re-opening REFACTOR-003's accepted Con, explicit that Option B (options-construction/retry/error-taxonomy unification) stays rejected; usage-limit helpers deliberately left single-sourced in
fundamental_agent. - Known pre-existing quirk, deliberately preserved — a message with two fenced JSON blocks greedily captures across both and returns
None(no span fallback). Identical in all three replaced copies; changing it would be out-of-scope behavior change.
Gates: 1,394 passed + 1 skipped, coverage 88.17% (floor 87); agent suites 212/212 unmodified; ruff/mypy(119)/bandit/pip-audit/pre-commit/compileall clean; constraints/pyproject diff vs main empty. Security review: no findings (parse-only helper; Pydantic validation and the injection quarantine downstream are untouched).
|
Codex review plan before fixes:
I’ll implement these with TDD, run the full gates and diff-security review, and push the follow-up to this branch. |
DoRmAmMu1997
left a comment
There was a problem hiding this comment.
Codex review finding for AI-006: the shared parser must define a strict JSON boundary because all three agents now trust it.
Co-authored-by: Hemant <hemantdhamija@gmail.com> Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Hemant <hemantdhamija@gmail.com> Co-authored-by: Codex <codex@openai.com>
Completion verdictComplete — no remaining reportable findings. This is a completion comment, not an approval. Commits A formal diff-security review found no remaining reportable findings. |
Ticket scope (AI-006)
Goal: eliminate the last copy-paste-drift hazard in the Claude-agent subsystems. The three agents (
backend/fundamentals/fundamental_agent.py,backend/technical/technical_agent.py,backend/sixty_seven/agent.py) each carried a private_extract_json_object— the tolerant "fenced block, then outermost{...}span" verdict extractor. REFACTOR-003 (#93) deduplicated the sync bridge after one of its three copies drifted; its ADR accepted the extractor leftover as a Con. This ticket re-opens exactly that accepted Con, deliberately.In scope
backend/ai_runtime.py: new publicextract_json_object(text) -> dict | None, moved verbatim (the three copies were verified logic-identical line-by-line before the move — only docstrings differed).from backend.ai_runtime import extract_json_object as _extract_json_object— the alias keeps every call site, parse-fallback path, and test seam byte-identical. Now-unusedimport relines removed.docs/architecture/refactor-003-ai-runtime.md: documents why the extractor met the same three-part test as the bridge (shared-and-identical / correctness-critical / drift-prone) and why this does not reopen the rejected Option B (options-construction blocks differ for real domain reasons and stay per-agent).tests/test_ai_runtime.py: bare object, ```json fence, fence without language tag, surrounding prose, empty text, no braces, unparseable braces, reversed braces.Out of scope (deliberate)
_usage_limit_from_message/_mentions_usage_limitare already single-sourced infundamental_agentand imported by the other two; no drift risk exists there.Behavior lock: the three agent suites (212 tests) pass unmodified — no test file for the agents was touched.
This is ticket 3 of a 10-ticket second improvement wave (TEST-007 #98, REF-003 #99, AI-006, IPO-006, TEST-006, PERF-002, DEPLOY-004, QUAL-006, QUAL-007, DOC-003), each shipped as its own PR off
main.Gates (all green, local, Python 3.13)
pytest -q --cov=backend --cov=screeners --cov=ui --cov-fail-under=87pre_commit validate-configcompileallruff checkmypy(119 files)banditpip_audit -r constraints.txtconstraints.txt/pyproject.tomldiff vs main: empty (as required).Review
/code-review: Approve — the moved function is verbatim (diff-verified against all three originals); the aliased import preserves each module's private name so no call site changed; deadimport reremovals confirmed by ruff./security-review: no findings — the extractor consumes model output that is already inside the trust boundary established by the agents' prompt-injection quarantine, and the shared implementation removes the risk of one copy silently diverging on parse tolerance (a consistency win for the fail-closed verdict validation downstream).🤖 Generated with Claude Code