claude-sonnet-5 review: Phase 2 by gpt-5.3-codex
Quality Gate / gate (push) Failing after 11s

This commit is contained in:
Jim Lancaster
2026-08-20 15:22:23 -05:00
parent afd1dba4d4
commit 796216087c
10 changed files with 172 additions and 74 deletions
-33
View File
@@ -1,33 +0,0 @@
You are working in the `transcription` repository (Python 3.12+, FastAPI, NiceGUI, SQLModel/SQLAlchemy, Pydantic V2, asyncio). Follow `.github/instructions/ui.instructions.md` and `.github/instructions/services.instructions.md` for any code you touch, and keep `docs/ver4/*` as canonical authority for intended behavior. Do not modify unrelated code.
**Prerequisite:** This prompt assumes Phase 1 (`docs/phase1-codex-prompt.md`) is already merged — the SQLite atomic job claim and the `JobSource(job_id, source_id)` uniqueness constraint should already exist. If they do not, stop and flag this before proceeding, since this phase's regression tests depend on that groundwork.
## Goal
Implement **Phase 2 (Enforcement hardening)** from `docs/architecture-code-review-2026-08-20.md`:
### 1. [HIGH-02] General UI media resolution falls back to basename instead of failing closed
**Location:** `src/transcription/ui/components/media_urls.py:41-74`, `src/transcription/ui/pages/sources_page.py:261-265`
**Problem:** When a stored path is not provably under `upload_dir` and does not match an approved prefix, `resolve_media_url` still returns `/uploads/{path_obj.name}`. The source detail page passes that URL directly to the viewer. If an unmanaged or stale DB path shares a basename with another upload, the UI can render the wrong file instead of the recorded source — violating record fidelity and the "controlled resolver paths" rule (`REQ-4-031`). This is distinct from print/export media, which is already correctly record-validated in `src/transcription/api/v4_print.py:31-54`.
**Required fix:**
- In `resolve_media_url` (`src/transcription/ui/components/media_urls.py`), remove the basename fallback. Return `None` (or an explicit "unavailable" sentinel/token consistent with how the rest of the UI layer signals missing/invalid media — check `error_presenter.py` and existing viewer components for the established pattern) unless the path is validated as either (a) safely resolvable under the configured `upload_dir`, or (b) an already-approved upload-relative form recognized elsewhere in the codebase (mirror the validation used by `v4_print.py`).
- Update `src/transcription/ui/pages/sources_page.py:261-265` (and any other call site relying on the old fallback behavior) to handle the `None`/unavailable case gracefully — e.g. show a clear "media unavailable" state in the viewer rather than crashing or rendering a blank/broken image.
- Add a dedicated resolver test suite (new or extended, e.g. `tests/ui/test_media_urls.py`) covering: a validated path under `upload_dir` (should resolve), an unmanaged absolute path outside `upload_dir` (should fail closed), a stale/nonexistent relative path (should fail closed), and a basename-collision scenario where an unmanaged path shares a filename with a legitimate upload (must NOT resolve to the wrong file). This directly satisfies the meta-tooling recommendation in `docs/architecture-code-review-2026-08-20.md` section 8: "Add a deterministic resolver test suite for `resolve_media_url`, not just `public_media_path_label`."
### 2. Deterministic regression tests for Phase 1 fixes
**Rationale:** Phase 1 fixed the underlying atomicity/uniqueness issues; this phase locks them in with deterministic, always-run tests so no future change can silently regress them (per the review's Action Plan, item 2, and section 8 meta-tooling recommendations).
**Required work:**
- Confirm (or add if missing) a concurrency regression test that races two concurrent `claim_next_queued_job` calls against the same queued job on SQLite and asserts exactly one caller wins. If Phase 1 already added this test, review it for robustness (e.g. does it actually force a race rather than relying on incidental ordering?) and strengthen it if needed — for example by using two independent sessions/connections and asserting via `asyncio.gather` that exactly one result is non-`None`.
- Confirm (or add if missing) a deterministic test asserting that creating a second `JobSource` for an existing `(job_id, source_id)` pair is rejected/handled predictably (not a silent duplicate). Ensure this test exercises the actual insertion code path used by `services/sources.py`, not just the raw model constraint.
- Both tests should live alongside the existing service test files for `jobs.py`/`sources.py` (check `tests/services/` for the correct location and naming convention).
## Validation
- Run `pytest` (via the project's normal invocation, e.g. `uv run pytest`, potentially through `tools/run_destructive_tests.py --auto-restore -- pytest` if that's how destructive/DB tests are run in this repo — check `pyproject.toml`/README) and ensure all tests pass, including new/strengthened tests.
- Run `ruff check` and `ty check` and ensure no new issues are introduced by your changes (do not attempt to fix the pre-existing baseline debt — that is Phase 5's responsibility).
- Do not touch error taxonomy (Phase 3), UI/service consolidation (Phase 4), or provenance/env config and ruff/ty baseline cleanup (Phase 5) — those are out of scope for this task.
Report back with: files changed, the fail-closed validation logic chosen for `resolve_media_url`, the new/updated test coverage, and final `pytest`/`ruff`/`ty` results.