Files
C.E.L_Slide_test2/.orchestrator/drafts/62_r5_payload.json
T

1 line
8.9 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"body": "[Claude #5] Stage 3 code-edit IMP-46 — u5 auto-cache CLI flag + 2^3 gate truth table\n\nExecuting unit: u5\n\n■ unit_executed\n- id: u5\n- summary: Wire IMP-46 u5 auto-cache opt-in across three layers — (1) add `ai_fallback_auto_cache: bool = False` to `src/config.py` Settings; (2) extend `src/phase_z2_ai_fallback/cache.py::save_proposal` with a keyword-only `auto_cache: bool = False` parameter that bypasses ONLY the `user_approved` gate (`visual_check_passed` remains mandatory and non-bypassable); (3) add `--auto-cache` CLI flag in `src/phase_z2_pipeline.py` that mutates `settings.ai_fallback_auto_cache=True` at parse time. The 2^3 gate truth table over `(visual_check_passed, user_approved, auto_cache)` is now exhaustively enumerated in `tests/phase_z2_ai_fallback/test_cache.py` with exactly three persisting rows: `(T,T,F)`, `(T,T,T)`, `(T,F,T)`. Every other row raises `AiFallbackCacheGateError` before any filesystem touch.\n\n■ files_changed\n- src/config.py (+8 lines — `ai_fallback_auto_cache: bool = False` Settings field + docstring)\n- src/phase_z2_ai_fallback/cache.py (+~30/-10 lines — `save_proposal(auto_cache=False)` keyword-only param, docstring expanded with u5 contract section, module docstring `u2 + u3` → `u2 + u3 + u5`)\n- src/phase_z2_pipeline.py (+~20 lines — `--auto-cache` argparse flag + in-process `settings.ai_fallback_auto_cache=True` mutation)\n- tests/phase_z2_ai_fallback/test_cache.py (+~110 lines — 8-row parametrised truth-table test + 5 targeted u5 cases)\n- tests/test_phase_z2_ai_fallback_config.py (+~12 lines — `test_ai_fallback_auto_cache_default_off`)\n\n■ diff_summary\n- `src/config.py`\n - New field `ai_fallback_auto_cache: bool = False` placed alongside the existing IMP-33 u1 ai_fallback_* block. Docstring explains the gate semantics (\"bypasses `user_approved` only; `visual_check_passed` never bypassable\") and locks the default OFF + CLI mutation contract.\n- `src/phase_z2_ai_fallback/cache.py`\n - Module docstring header: `IMP-46 u2 + u3` → `IMP-46 u2 + u3 + u5`. New \"u5 auto-cache contract\" section enumerates the truth table (3 cells succeed, 5 raise) and pins the keyword-only / default-False shape.\n - Guardrails block updated: \"Both write gates preserved — `visual_check_passed=False` always raises … `user_approved=False` also raises by default; the IMP-46 u5 `auto_cache=True` override bypasses ONLY the `user_approved` gate (`visual_check_passed` is never bypassed). Gate violation never silently no-ops.\"\n - `save_proposal` signature gains keyword-only `auto_cache: bool = False` at the end of the kwargs block (after `fingerprints`). Position-only callers cannot accidentally pass it.\n - New type-check: `if not isinstance(auto_cache, bool): raise TypeError(\"auto_cache must be a bool\")`. Symmetric with `slide_css` / `fingerprints` validation.\n - Gate logic change — single line:\n - Before: `if not user_approved: raise AiFallbackCacheGateError(...)`\n - After: `if not user_approved and not auto_cache: raise AiFallbackCacheGateError(...)`\n - The `visual_check_passed` gate is unchanged — `auto_cache` does NOT appear in that branch. Error messages updated to reflect the new bypass surface (\"auto_cache cannot bypass this gate\" on visual-check failure; \"Pass auto_cache=True (or --auto-cache on the CLI) to bypass\" on user-approved failure).\n - No new imports. No `src.config` import inside cache.py — the cache module remains a pure comparator/storage layer that takes the resolved boolean from the caller (preserves AI isolation contract `tests/phase_z2_ai_fallback/test_ast_isolation.py:48-69`).\n- `src/phase_z2_pipeline.py`\n - New `--auto-cache` flag added to the existing `if __name__ == \"__main__\"` argparse block (after `--override-section-assignment`, before `args = parser.parse_args()`). `action=\"store_true\"`, `default=False`. Help text states the gate semantics + setting wiring.\n - After `parse_args()`: a 3-line conditional imports `settings` from `src.config` and sets `ai_fallback_auto_cache=True` when the flag is present. The deferred import (inside `if args.auto_cache:`) keeps `src.config` off the unconditional pipeline import surface — only operators who opt in pull it in.\n - The mutation is in-process only — no .env write, no side effects between runs. A subsequent pipeline run without `--auto-cache` starts back at the Settings default (`False`).\n- `tests/phase_z2_ai_fallback/test_cache.py`\n - New section header \"IMP-46 u5: auto_cache gate (2^3 truth table)\".\n - `_GATE_TRUTH_TABLE` constant — 8 rows, exhaustive Cartesian product of `(V, U, A)` × `expect_persist`. Hand-locked, NOT generated from the implementation, so a regression in the gate logic surfaces as a row failure (3 persist + 5 raise).\n - `test_save_gate_truth_table` parametrised — for each row, either calls `save_proposal` and asserts the file exists (persist row), or asserts `AiFallbackCacheGateError` raises AND the frame_id directory is absent (gate row — symmetric with the existing `test_save_gate_violation_does_not_touch_filesystem`).\n - `test_auto_cache_default_off_preserves_dual_gate_semantics` — calling without the `auto_cache` kwarg keeps the IMP-46 u2 behaviour (`user_approved=False` raises with `\"user_approved\"` in the message; no directory created).\n - `test_auto_cache_cannot_bypass_visual_check` — `(V=False, U=True, A=True)` row promoted to its own assertion that also checks the error message mentions `visual_check_passed` (regression sentinel against accidentally moving `auto_cache` into the visual-check branch).\n - `test_auto_cache_bypass_user_approved_persists` — `(V=True, U=False, A=True)` round-trip: persists, file exists, `read_proposal` returns the original payload.\n - `test_auto_cache_rejects_non_bool` — passing `auto_cache=\"yes\"` raises `TypeError` (symmetric with the existing non-string / non-dict guards for `slide_css` and `fingerprints`).\n - `test_auto_cache_is_keyword_only` — `inspect.signature(save_proposal).parameters[\"auto_cache\"]` is `KEYWORD_ONLY` and default `False`. Locks the public surface so a future refactor can't accidentally make it positional or default-True.\n- `tests/test_phase_z2_ai_fallback_config.py`\n - New section explaining the u5 CLI / settings contract.\n - `test_ai_fallback_auto_cache_default_off` — `Settings().ai_fallback_auto_cache is False`. The CLI flag mutates the in-process settings instance; the default must stay OFF so the dual-gate contract survives without an operator opt-in.\n\n■ validation\n- `python -m pytest tests/phase_z2_ai_fallback/test_cache.py tests/test_phase_z2_ai_fallback_config.py -q` → 51 passed in 0.22s\n- `python -m pytest tests/phase_z2_ai_fallback/ -q` → 201 passed in 0.40s (u1-u4 regression incl. test_ast_isolation, test_signature, test_cache_invalidation, test_step12 all green)\n- `python -m pytest tests/test_family_contract_baseline.py -q` → 4 passed in 0.59s\n- `python -m src.phase_z2_pipeline --help` → `--auto-cache` flag surfaced with the documented help text\n- AST isolation guard re-confirmed: cache.py imports unchanged (`json`, `pathlib`, `src.phase_z2_ai_fallback.schema`); no `src.config` import inside `src/phase_z2_ai_fallback/` (the setting is read by the CLI entry point and the resolved bool is passed in by callers, preserving the AI-isolation contract `tests/phase_z2_ai_fallback/test_ast_isolation.py:48-69`)\n\n■ scope-lock verification\n- ✅ u5 declared files only (config.py / cache.py / phase_z2_pipeline.py / test_cache.py / test_phase_z2_ai_fallback_config.py).\n- ✅ No `route_ai_fallback` call site changes — IMP-33 AI hook untouched.\n- ✅ No `read_proposal` signature change — u3 fingerprint comparator surface preserved.\n- ✅ No `save_proposal` call site changes outside tests — there is no production `save_proposal` invocation yet (verified by `rg \"save_proposal\\\\(\" src` — only `cache.py` definition + docstring references in `router.py`).\n- ✅ `visual_check_passed` gate is non-bypassable — `auto_cache` only relaxes `user_approved`. Truth-table row `(V=False, U=True, A=True)` raises, locked by `test_auto_cache_cannot_bypass_visual_check`.\n- ✅ Settings default OFF — `Settings().ai_fallback_auto_cache is False` (`test_ai_fallback_auto_cache_default_off`). Honours `feedback_ai_isolation_contract` (auto-cache is an explicit operator opt-in, not silent).\n- ✅ No-hardcoding lock — no sample / MDX / template_id specific literal added. `auto_cache` is a pure axis of the cache write gate.\n\n■ remaining_units\n- [u6] — Track `data/frame_cache/.gitkeep` via `.gitignore` exception under the existing `data/` ignore, add repo-layout coverage (`tests/phase_z2_ai_fallback/test_cache_repo_layout.py`), and refresh `docs/architecture/IMP-17-CARVE-OUT.md` cache row from stub status → persistent backend status.\n\n■ follow_up_issue_candidates\n- None. u5 scope-lock held; no axis discovered outside `(V, U, A)` × cache write surface.\n\nFINAL_CONSENSUS_TARGET: continue_same (u6 remains)\n"}