63 KiB
Architecture & Code Review Report
Repository Target: C:\Github\transcription\
Target Stack: Python 3.12+ | FastAPI | NiceGUI | SQLModel/SQLAlchemy | Pydantic V2 | asyncio | OpenRouter
Review Date: 2026-09-02
Canonical Baseline: V6.1 (docs/index.md)
0. Verification Commands and Outcomes
All four commands were executed in this checkout before any finding was written. This report records the exact outcomes rather than assuming them.
| Command | Outcome |
|---|---|
uv run pytest -q -m "not external" |
410 passed, 0 failed, 0 errors (exit 0) |
uv run ruff check . |
All checks passed! |
uv run ruff format --check . |
191 files already formatted |
uv run ty check |
All checks passed! |
The stated green baseline is real. No finding below is a test failure; every finding is a behavior, contract, or guard-coverage defect that the passing suite does not detect.
1. Executive Summary
- The system's core evidence guarantees hold.
ExecutionAttemptis genuinely append-only, attempt numbering is allocated with bounded conflict retry, transport evidence is captured at the HTTP boundary before SDK parsing, and header persistence uses a true allowlist. Provenance invariant families A–E and G pass. - Both competing atomicity invariants in
services/workflows.pyare real and both guards genuinely enforce them. I injected-fault-verified the tests rather than trusting the docstrings:test_pipeline_atomicity.pyfails on a split final-page commit, andtest_workflows_reliability.py:318reads intermediate attempts through a separate session, so it would fail if intermediate pages stopped committing individually. - The most significant defect is a privacy leak that a prior review believed it had closed.
The 2026-08-23 review moved root-cause text out of
AppError.messageintoAppError.detailto keep filesystem paths away from users. That text now reaches users anyway, because the UI rendersExecutionAttempt.error_detailverbatim (HIGH-01). The leak was relocated, not closed. - A second, independent path leak exists in five explicit
raisesites that the existing guard never covered — it tests onlyclassify_unexpected_error(HIGH-02). - Provenance invariant family F (path safety) fails, and it fails inconsistently within one
file:
sources_page.py:443carefully sanitizes a stored path throughpublic_media_path_label, thensources_page.py:484dumps rawerror_detailforty lines later. - The orphan sweep does not do what its docstring claims. It matches definitions by bare name,
so an entirely dead module passes whenever its function names collide with live ones.
ui/pages/tags_page.pyis the proof: 93 lines never imported by anything (MED-01/LOW-01). - On the three flagged open items: the V4/V6.1 doc drift is confirmed (MED-02); the
.env.productioncoupling is real but currently correct and loud-failing, so Medium not High (MED-03); and the Tags roadmap is right — the route is genuinely not registered, so the module is dead code rather than a live retired route. - Two latent concurrency defects carry ordering constraints and must be fixed before the changes that would make them live (MED-04, MED-05), not after.
- Guidance-file accuracy: the recently revised
.github/instructions/*files were verified against code rather than trusted. They are accurate as written; the code is what diverges from them. The one exception is thaterror-handling.instructions.mdstates adetailrule the UI layer has never followed, which makes it an unenforced claim rather than a wrong one.
2. Executive Architecture Assessment
Verdict: architecturally sound, with a concentrated failure in the last mile of error presentation.
Domain cohesion and dependency direction are good and, unusually, mechanically enforced.
test_service_boundaries.py and test_ui_boundaries.py AST-scan for violations using
allowlists rather than blocklists, which is the correct choice — a newly added persistence
helper cannot slip through under an unlisted name. workflows.py imports only the abstract
providers types and never openrouter, so provider details genuinely stop at the adapter.
Transaction ownership is explicit and well-reasoned: ServiceBase._finalize commits for
service-owned sessions and flushes for caller-owned ones, which is what lets orchestration
modules compose multi-aggregate writes without services importing each other.
The evidence layer is the strongest part of the system and shows real care. The distinction
between transport response, SDK-parsed response, and normalized metadata is maintained in code,
not just in prose — _CapturingAsyncClient exists specifically to retain the exact wire body
before the SDK can discard unknown fields, and TransportEvidence(response_received=False)
explicitly represents "no response was received" rather than conflating it with an empty one.
The weakness is at the boundary where internal diagnostic text becomes pixels. Every layer below the UI respects the message/detail split; the UI layer reads the internal field directly and renders it. The architecture defines the contract correctly and then has no enforcement at the one layer that violates it.
Top systemic risks:
- Internal diagnostic text reaches users through the evidence display path (HIGH-01). The rule is documented in three places and enforced in none of them at the UI boundary.
- Path-safety discipline is applied per-call-site rather than structurally (HIGH-02, HIGH-01). It is correct wherever someone remembered; there is no guard that makes forgetting fail.
- Guard coverage is narrower than guard docstrings claim. Two guards
(
test_orphan_sweep.py,test_errors.py) assert something meaningfully weaker than the invariant they are named for, which converts them into a false sense of enforcement. - Worker safety currently rests on single-process sequential execution, not on configuration (MED-04, MED-05). Nothing is wrong today; two plausible future changes each make something wrong.
3. Findings by Severity
Critical Severity
None. No evidence loss, append-only violation, secret leakage, or silent-wrong-output defect was found. The candidates in this class (provider evidence mis-attribution, stale-job double processing) are latent and are reported at High/Medium with their unblocking conditions.
High Severity
[HIGH-01] Internal-only error_detail is rendered directly to users, reopening the leak the 2026-08-23 fix was meant to close
-
Location:
- Write side:
src/transcription/errors.py:99-139(classify_unexpected_error→detail,format_error_detail→ persisted text) - Persist:
src/transcription/services/workflows.py:720(error_detail=format_error_detail(page.error)) - Render (Source Detail):
src/transcription/ui/pages/sources_page.py:481-484 - Render (Sources list):
src/transcription/ui/pages/sources_page.py:121→src/transcription/ui/components/table/sources.py:39,90-95("Error Detail" column) - Render (Maintenance):
src/transcription/ui/pages/settings_page.py:562, written bysrc/transcription/services/maintenance.py:206 - Contract violated:
docs/error_handling.md:107-114;.github/instructions/error-handling.instructions.md:86;docs/invariant/error_handling.md:59;docs/invariant/ai_evidence_and_provenance.md:103
- Write side:
-
Reachability: Live. Concrete path, no configuration required: a page fails with any non-
AppErrorexception →workflows.py:388callsclassify_unexpected_error(exc)→errors.py:118setsdetail=f"{type(exc).__name__}: {exc}"→format_error_detail(errors.py:135-139) emits... | detail=OSError: [Errno 13] Permission denied: '/app/uploads/documents/<uuid>/page-1.jpg' | ...→ persisted toExecutionAttempt.error_detail→ rendered verbatim atsources_page.py:484and in the/sourcestable column. A SQLAlchemyOperationalErrorcarries the database path by the same route. -
Problem & Consequence:
docs/error_handling.md:110statesdetailis "Internal only" and that its only surfaces areformat_error_detail(evidence) and logs;error-handling.instructions.md:86says "Never rendered to users or serialized into an envelope." The UI reads it anyway. The consequence is not hypothetical drift — it is the precise defect the previous review's fix existed to prevent. That fix mademessagegeneric and moved the root cause todetailon the stated grounds thatdetailnever reaches users. That premise was never true:error_detailhad a UI consumer the whole time. The result is that the filesystem-path leak was relocated from the notification banner to the Source Detail card and the Sources table, while the test suite records the leak as fixed (tests/test_errors.py:56-78).The inconsistency is visible inside a single file:
sources_page.py:443deliberately routes a stored path throughpublic_media_path_label(ui/components/media_urls.py:58-72), which correctly degrades an absolute path to its bare filename — and thensources_page.py:484renders unsanitized text that may contain an absolute path. -
Blast Radius: Enumerated by grepping every reader of
.detailanderror_detail:errors.py:137—format_error_detail, the only reader ofAppError.detail. Must keep the root cause.services/workflows.py:720— the only writer ofExecutionAttempt.error_detail.services/maintenance.py:206— the only writer ofMaintenanceRun.error_detail.services/evidence.py:195—build_evidence_exportemitserror_detail. Export is an operator-initiated evidence artifact; per invariant 3.7.1 it must retain it.db/models.py:508-522—Source.latest_error_detailprojection, consumed only bysources_page.py:121.ui/pages/sources_page.py:481-484,ui/components/table/sources.py,ui/pages/settings_page.py:562— the three render sites.- Tests asserting on persisted text:
tests/test_v42_evidence.py:284,tests/services/test_workflows_reliability.py(timeout detail),tests/services/test_maintenance_service.py. A fix that changes what is stored breaks these; a fix that changes what is displayed does not.
-
Recommendation — two invariants conflict here; both must be named.
Invariant 1 (evidence):
ExecutionAttempt.error_detailmust retain the root cause.docs/requirements.md:30(REQ-4-021) anddocs/invariant/ai_evidence_and_provenance.md:33require it; guarded bytests/test_v42_evidence.py::test_attempts_are_append_only_and_exported_with_integrityandtests/services/test_workflows_reliability.py.Invariant 2 (privacy): user-facing surfaces must not expose local filesystem details.
docs/invariant/error_handling.md:59; guarded (partially) bytests/test_errors.py::test_unexpected_error_does_not_leak_filesystem_paths.The over-correction to avoid is stripping root-cause text out of
detailorformat_error_detailto make the UI safe. That is exactly the mistake documented in the reviewer skill's worked example, and it would silently destroy the provenance record this system exists to preserve while making every guard still pass.Fix at the render boundary, not the write boundary. Add a presentation-layer projection and route all three UI sites through it, leaving the persisted evidence untouched:
# src/transcription/ui/components/error_presenter.py (new) def display_failure_detail(error_detail: str | None) -> str | None: """Render persisted failure detail without machine-local paths. `ExecutionAttempt.error_detail` is provenance and keeps the full root cause (docs/error_handling.md). This projection is the only thing a page may show. """It should preserve the
[category],suggestion=, anderror_id=segments (which are what make the display actionable) and reduce any absolute path insidedetail=to its basename, mirroringpublic_media_path_label. The operator keeps diagnosability — required bydocs/ui/pages/sources.md:43anddocs/requirements.md:59(REQ-6-014) — without the container filesystem layout being published to the browser.Then decide and record which resolution was chosen: either the UI shows the sanitized projection (recommended), or
docs/error_handling.md:107-114anderror-handling.instructions.md:86are revised to state that operator-facing evidence displays may rendererror_detailand that the guarantee moves to "no machine-local detail ever entersdetail" — which would be a much harder guarantee to keep. Do not leave the current state, where the docs claim one thing and three pages do another. -
Effort: M
[HIGH-02] Absolute filesystem paths are embedded in user-facing AppError.message at five explicit raise sites
-
Location:
src/transcription/services/sources.py:856—f"Prompt file not found: {prompt_path}"src/transcription/services/sources.py:864—f"Prompt file is empty: {prompt_path}"src/transcription/services/sources.py:914—f"Source file not found: {path}"src/transcription/services/prompts.py:99—f"Prompt directory is unavailable: {root}"src/transcription/services/prompts.py:186-191—_filesystem_errorbuildsf"{message}: {exc}"- Contract violated:
.github/instructions/error-handling.instructions.md:74,85;docs/invariant/error_handling.md:59
-
Reachability: Live, on an ordinary user path.
sources.py:845resolvesprompt_root = runtime_settings.prompt_dir.resolve(), soprompt_pathis absolute (/app/prompts/transcribe_document.mdin the container).load_prompt_textis invoked bybuild_prompt_execution(sources.py:829-831), which runs on every document upload viaservices/store.py:94andstore.py:162. The resultingPromptLoadErroris anAppErrorsubclass, so it flows throughrun_ui_action→show_error(ui/components/error_presenter.py:51-66), which renderserror.messageinto both aui.notifybanner and a card label, and throughbuild_error_envelope(errors.py:88-96) into API responses. -
Problem & Consequence:
error-handling.instructions.md:85requiresmessageto "Stay generic. Never embed exception text, provider payloads, or filesystem paths." These five sites embed exactly that.prompts.py:186-191violates the rule in both directions at once: it puts{exc}— anOSErrorwhosestr()includes the offending filename — intomessage, and it sets nodetail=, so the internal field that is supposed to carry the root cause is empty while the user-facing field carries all of it.This is not a new regression; it is coverage that the existing guard never had.
tests/test_errors.py:56-78verifies only thatclassify_unexpected_error— the catch-all path — does not leak. Every deliberateraise SomeError(f"... {path}")in the codebase is outside its scope, so the suite reports the invariant as enforced while five live sites violate it. -
Blast Radius: Verified by grepping all consumers of these exception types.
PromptLoadError/PromptStoreError/TranscriptionErrormessages are consumed by:ui/components/error_presenter.py:55,63(render),errors.py:92(API envelope),errors.py:135(format_error_detail→ evidence). Because the recommended change adds adetailand shortensmessage,format_error_detailoutput still contains the path — so evidence value is preserved, not reduced. Tests asserting on these messages:tests/test_prompts.py,tests/services/test_prompt_store.py,tests/services/test_transcription_service.py. These assert on message prefixes ("Prompt file not found"), not on the interpolated path, and were checked to survive the change — but re-run them, sinceprompts.py:186currently produces a message whose suffix some assertion could depend on. -
Recommendation: Apply the pattern
errors.py:113-119already establishes — genericmessage, root cause ondetail,raise ... from exc. Usepath.namewhen a filename is genuinely useful to the user.# sources.py:855 — before raise PromptLoadError(f"Prompt file not found: {prompt_path}", ...) # after raise PromptLoadError( f"Prompt file not found: {prompt_path.name}", category=ErrorCategory.INFRA_PERSISTENT, suggestion="Verify PROMPT_DIR and prompt file configuration, then retry.", detail=f"Prompt file missing at {prompt_path}", ) # prompts.py:186 — before return PromptStoreError(f"{message}: {exc}", category=..., suggestion=...) # after return PromptStoreError( message, category=ErrorCategory.INFRA_PERSISTENT, suggestion="Check prompt directory permissions and available disk space, then retry.", detail=f"{type(exc).__name__}: {exc}", )Then widen the guard so this class cannot recur — see MED-07. Note the dependency: HIGH-02 and HIGH-01 must be fixed together, because moving the path from
messagetodetailwhile the UI still renderserror_detailrelocates the leak instead of closing it. That is the same mistake that produced HIGH-01. -
Effort: S (fix) / M (with the guard)
Medium Severity
[MED-01] The orphan sweep matches by bare name and therefore cannot detect a dead module
-
Location:
tests/test_orphan_sweep.py:101-163(_public_definitions,_orphans) -
Reachability: Live — the guard is running now and reporting a clean sweep that is not clean.
-
Problem & Consequence:
_public_definitions()keys definitions by bare name (definitions[node.name], line 111) and_orphans()marks a definition referenced if that bare name appears anywhere insrc/,tests/, ortools/(lines 157-162). Two different modules that define the same public name are therefore indistinguishable, and neither can ever be reported as an orphan.src/transcription/ui/pages/tags_page.pydemonstrates the consequence. Its only public definition isregister_page(line 18). Seven live page modules define a function of the same name andui/__init__.py:37-43calls all seven — soregister_pageis heavily referenced andtags_page.register_pageis scored as reachable. In fact nothing importstags_pageat all (verified: the only repo-wide references to the module are the file itself andtests/ui/test_tags_page.py, which merely asserts the route 404s). 93 lines of code, including a lazy-load-unsafe relationship traversal attags_page.py:71-74, sit outside the sweep's reach.The sweep also never asks whether a module is imported, only whether its definitions' names appear somewhere — so this is a structural gap, not a one-off miss.
-
Blast Radius:
tests/test_orphan_sweep.pyonly;KNOWN_ORPHANSentries are keyed by the same bare/dotted names and would need re-keying if qualification is added. Expect the stricter sweep to surface additional true orphans on first run — triage them intoKNOWN_ORPHANSwith rationales rather than weakening the check. -
Recommendation: Qualify definitions by module (
f"{module_path}:{name}") and add a separate, cheap module-reachability pass: a module undersrc/transcription/is reachable if any other module imports it, or it is a declared entrypoint (app.py,__main__.py,worker_service.py). Report unreachable modules as orphans in their own right. Also fixtest_public_definitions_are_discovered(line 169), whose>= 420snapshot threshold is a weak assertion that drifts upward silently — the 2026-08-23 review already flagged the same pattern at the then-current>= 200and it was raised rather than replaced. -
Effort: M
[MED-02] Canonical invariant document declares a V4 baseline while the canonical baseline is V6.1
-
Location:
docs/invariant/ai_evidence_and_provenance.md:130 -
Reachability: Live (documentation), no runtime impact.
-
Problem & Consequence: Section 6.1 reads "Canonical V4 architecture, schema, requirements, and error-policy documents define how current behavior satisfies this invariant."
docs/index.md:1,29-32establishes V6.1 as the baseline and states that every canonical document asserts the same baseline. This is the ownership clause of the invariant that governs the entire evidence model — the clause that tells a reader which documents are authoritative — and it points at a superseded generation. A reader following it lands on stale authority precisely when resolving an evidence question, which is the highest-stakes case.A baseline-currency guard does exist —
tests/test_meta_contract_guards.py::test_canonical_docs_declare_one_consistent_baseline(lines 89-112) — anddocs/invariant/ai_evidence_and_provenance.mdis not inBASELINE_SCAN_EXCLUSIONS(lines 56-64), so the file is scanned. The claim escapes for two independent reasons, either of which alone would be sufficient:_CURRENT_VERSION_CLAIM(line 67) matches only the wordscurrentoractivebefore a version. This line says "Canonical V4", a third phrasing the pattern does not know.- Both patterns require
V(\d+\.\d+)— a mandatory minor version. The bare tokenV4cannot match either regex under any phrasing.
The guard is therefore not absent but phrase-shaped: it enforces currency only for the two sentence forms someone thought of, against version strings that carry a minor. That is a weaker property than its docstring implies ("Every canonical doc that names the current baseline must name the same one").
-
Blast Radius: Documentation only; no code reads this string. Widening the guard's patterns will re-scan all canonical docs — expect it to surface further stale mentions on first run (
docs/architecture.md,docs/schema.md,docs/requirements.md, anddocs/error_handling.mdeach contain 2-3 version tokens), which should be triaged rather than excluded. -
Recommendation: Two parts, and the second matters more than the first.
- Change "Canonical V4" to "Canonical V6.1" at line 130.
- Fix the guard's shape rather than adding a third phrase to the list. Accept an optional minor
(
V(\d+)(?:\.(\d+))?) and invert the matching: flag everyV<n>token in a scanned canonical doc that is not the declared baseline, rather than only those preceded by an approved adjective. Phrase-list matching fails open — each new phrasing silently reopens the hole — whereas token matching fails closed and forces an explicit exclusion.
See §8.1 for the alternative the maintainer is considering: dropping version labels from canonical docs entirely, which removes the failure mode instead of guarding it.
-
Effort: S
[MED-03] Settings resolve .env.production relative to the process working directory, and the isolation fix exists only in the test harness
-
Location:
src/transcription/config.py:66-75(env_file=".env.production"); workaround attests/conftest.py:27-50; guarded bytests/test_config_isolation.py; depended on by.github/workflows/quality-gate.ymlanddocker-compose.production.yml -
Reachability: Live but currently correct. I verified the production path rather than assuming it:
DockerfilesetsWORKDIR /appin the runtime stage, anddocker-compose.production.ymlmounts./.env.productionto/app/.env.productionfor both theappandworkerservices, so the relative path resolves correctly today. -
Problem & Consequence: Correct configuration loading depends on an implicit, undocumented contract between
config.pyand the process working directory. Nothing inconfig.pystates it, and nothing tests it. The failure mode is not silent —openrouter_api_keyis required with no default, so a wrong cwd produces aValidationErrorat startup rather than a partially configured process — which is why this is Medium rather than High.The more telling symptom is what the coupling forced on the test harness.
conftest.py:45-50cannot escape it by passing an argument; it must mutate the Pydantic class-levelmodel_configdict at runtime and restore it in afinally. That is a global, order-sensitive side effect adopted because the module offers no seam. It also silently repairs a second consumer:ui/runtime_settings_store.py:402reads the sameSettings.model_config["env_file"]to decide where the Settings page writes. Two subsystems are coupled through a mutable class attribute. -
Blast Radius: Every
Settingsconstruction. Consumers ofmodel_config["env_file"]:ui/runtime_settings_store.py:402(write-target resolution, contract documented atdocs/ui/pages/settings.md:27) andtests/conftest.py:45-50. A change must preserve the documented three-step resolution order — explicit override,RUNTIME_SETTINGS_ENV_FILE, then the configured default — ordocs/ui/pages/settings.md:27becomes wrong. -
Recommendation: Introduce one explicit resolution function that both
Settingsconstruction andruntime_settings_storecall, honoring anENV_FILEenvironment variable and falling back to a path anchored to a known root rather than toos.getcwd(). Tests then pass a path instead of mutating class state, andtests/test_config_isolation.pycan assert against the seam rather than against the monkeypatch. If instead the cwd contract is accepted as deliberate, document it inconfig.pyand indocs/production-runbook.mdand add a guard assertingWORKDIR/cwd alignment — an implicit contract with a container image is exactly the kind of rule the invariant routing table exists to place. -
Effort: M
[MED-04] Stale-job reclaim threshold is not derived from maximum job duration; safety currently comes from single-process sequencing
-
Location:
src/transcription/config.py:116-117(worker_provider_timeout_seconds=30.0,worker_stale_job_seconds=30.0); sweep atsrc/transcription/worker.py:222-228; reclaim atsrc/transcription/services/jobs.py:242-268 -
Reachability: Latent. Unblocked by either of: (a) running more than one worker replica (adding
deploy.replicas > 1to theworkerservice indocker-compose.production.yml), or (b) settingRUN_EMBEDDED_WORKER=trueon theappservice while the standaloneworkercontainer is also running. It is safe today only becausedocker-compose.production.ymlsetsRUN_EMBEDDED_WORKER: "false"onappand defines exactly oneworker, and because within a single looprun_worker_loopawaitsprocess_next_queued_jobto completion before returning to the stale sweep — so the sweep can never observe a job that this same process is actively working. -
Problem & Consequence: The stale threshold (30s) equals the per-page provider timeout (30s), leaving zero margin even for a single-page job. A multi-page document is legitimately
PROCESSINGfor up to N × 30s.Job.date_updatedcarries anonupdate(db/models.py:372-375), but between the initial claim and the terminal write the only touch issources.py:522-523reassigningjob.provider/job.modelto values they usually already hold, which SQLAlchemy resolves to no net change and therefore noUPDATE. I did not empirically confirm the no-UPDATEbehavior, so treat that specific step as unverified — but the finding does not depend on it, because even a per-page refresh leaves only a 30s margin against a 30s timeout.With a second concurrent worker, the sweep would requeue a job that is mid-provider-call. Both workers then process the same job, producing duplicate
ExecutionAttemptrows for the same logical work and racing terminal status writes. Append-only history would be preserved but no longer faithful: the evidence would show attempts that do not correspond to distinct application decisions.This is worth flagging because
jobs.py:191-197explicitly implements and documentsSKIP LOCKEDrow locking "so concurrent workers never contend for the same job." The claim path is built for multi-worker operation; the reclaim path is not. A reader who trusts the claim docstring would reasonably scale the worker. -
Blast Radius:
requeue_stale_processing_jobshas one production caller (worker.py:226) and tests intests/test_worker.pyandtests/services/test_job_service.py. Changing the default affectstests/test_config.pydeclared-defaults assertions — check those before editing the default. -
Recommendation: Fix before adding a second worker replica, not after. Two parts: (1) Make the threshold a function of the real bound rather than a coincidental peer of the page timeout — at minimum default
worker_stale_job_secondsto a multiple ofworker_provider_timeout_secondswith headroom, and add a model validator rejecting a stale threshold at or below the provider timeout. (2) Preferably make reclaim heartbeat-based: have_persist_page_outcomebumpJob.date_updatedexplicitly so liveness reflects progress rather than elapsed time since claim. Add a guard asserting a multi-page job in flight is not reclaimed by a concurrently-invoked sweep. -
Effort: M
[MED-05] Provider evidence capture is per-instance mutable state, making the adapter non-reentrant by contract
-
Location:
src/transcription/providers/openrouter.py:197-199, 264-267, 274-275, 297-298, 397-412;_CapturingAsyncClient.last_response/last_bodyatopenrouter.py:66-94; contract atsrc/transcription/providers/base.py:110-118(current_request_manifest,current_transport_evidence) -
Reachability: Latent. Unblocked by any concurrent
transcribe()on a single adapter instance — most plausibly by processing a job's pages in parallel (workflows.py:274is currently a sequentialforloop) or by any second consumer sharing oneSourceService.provider. Verified safe today:workflows.py:272resolves one provider for the loop and awaits each page; the worker'sServiceBundle(worker.py:206) is distinct fromapp.state.services(app.py:43), so the UI cannot share the worker's adapter instance, and the UI only enqueues jobs (ui/pages/jobs_page.py:186-208). -
Problem & Consequence: The
TranscriptionProviderprotocol defines evidence retrieval as "the most recent call" state read after the fact.workflows.py:369-370relies on this on the timeout path, readingprovider.current_request_manifest/current_transport_evidencewhen no result object exists. Under concurrency, page B's response overwrites_CapturingAsyncClient.last_responsebefore page A's timeout handler reads it, and page A'sExecutionAttemptis written with page B's transport evidence.The consequence is evidence mis-attribution — a provenance-integrity failure, which this project's own rubric treats as its most serious class. It would also be near-undetectable after the fact: the attempt row would be well-formed, internally consistent, and wrong. The application-level design that makes this safe (sequential pages) is not expressed in the provider contract, so the constraint lives only in
workflows.py's loop structure. -
Blast Radius: Changing the protocol touches
providers/base.py:102-136,providers/openrouter.py:221-231, the two read sites atworkflows.py:369-370, and the fakes intests/providers/test_openrouter.py,tests/services/test_workflows_reliability.py, andtests/test_provider_boundaries.py, all of which implement or assert the current property-based contract. -
Recommendation: Fix before introducing any intra-job page concurrency. The durable fix is to stop returning evidence through instance state: attach
request_manifestandtransport_evidenceto the raised exception on every failure path — whichProviderErroralready supports (providers/base.py:18-29) and which the timeout path cannot currently use becauseasyncio.wait_forraisesTimeoutErrorfrom outside the adapter. A narrower option is to havetranscribe()accept a caller-owned capture sink so evidence is scoped to the call rather than to the adapter. As an immediate, near-zero-cost step, document the non-reentrancy on the protocol inproviders/base.pyso the constraint is visible where it is depended upon. -
Effort: M
[MED-06] Provider error bodies reach user-facing text while three provider failure paths persist no detail
-
Location:
src/transcription/services/sources.py:923-947(handle_transcription_errors); message construction atsrc/transcription/providers/openrouter.py:414-431(_transport_error_message) -
Reachability: Live for the message half (any provider failure during a UI-initiated transcription surfaces through
show_error). -
Problem & Consequence: Two mirrored halves of the same rule are broken in one function.
sources.py:943buildsf"Provider transcription failed: {exc}", andexcis aProviderErrorwhose message may embed up to 500 characters of the provider's error body (openrouter.py:430). That is a provider payload inmessage, whicherror-handling.instructions.md:85explicitly forbids.- None of the three handlers (lines 929, 935, 942) passes
detail=. Pererror-handling.instructions.md:89-92, omitting it degrades the provenance record.
I checked whether the provenance half is actually harmful before reporting it, and it is substantially mitigated:
workflows.py:391calls_find_provider_error, which walks__cause__/__context__(workflows.py:806-813) to recover the originalProviderErrorand persists itstransport_evidence— status code, safe headers, and the exact response body — onto the attempt. So the root cause is preserved in transport evidence even thougherror_detailis thin. This is why the finding is Medium rather than High. The residual cost is that the human-readable failure summary is uninformative for the two paths (ProviderAuthError,ProviderResponseError) whose messages are entirely generic. -
Blast Radius:
handle_transcription_errorsis used on the transcription path insources.py;TranscriptionError.messageis consumed byerror_presenter.show_error,build_error_envelope, andformat_error_detail. Assertions on these messages live intests/services/test_transcription_service.pyandtests/providers/test_openrouter.py. -
Recommendation: Move the interpolated provider text from
messagetodetailon all three handlers, keeping the generic message the other two already use:except ProviderError as exc: raise TranscriptionError( "Provider transcription failed", category=ErrorCategory.EXTERNAL_PROVIDER, suggestion="Retry the transcription from jobs. If repeated, check provider availability.", retriable=True, detail=f"{type(exc).__name__}: {exc}", ) from excApply the same
detail=addition to theProviderAuthErrorandProviderResponseErrorhandlers. Note the interaction with HIGH-01: until the render boundary is sanitized, moving text intodetailstill reaches users through theerror_detaildisplay. Sequence accordingly. -
Effort: S
[MED-07] No deterministic guard covers the message/detail split at explicit raise sites
-
Location:
tests/test_errors.py:56-78; rule at.github/instructions/error-handling.instructions.md:78-98; canonical statement atdocs/error_handling.md:102-115 -
Reachability: Live — this coverage gap is what allowed HIGH-02 and MED-06 to exist in a fully green suite.
-
Problem & Consequence:
docs/error_handling.md:115namestests/test_errors.py::test_unexpected_error_does_not_leak_filesystem_pathsas the enforcement for the message/detail split. That test exercises exactly one function,classify_unexpected_error. Every directraise SomeAppError(...)insrc/— roughly 50 sites by grep — is unenforced. The documentation therefore overstates the enforcement, which is worse than having no guard: a contributor readingerror_handling.md:115reasonably concludes the rule is mechanically protected.Per the reviewer skill, where a check is unenforced, recommending the deterministic test is itself a finding.
-
Blast Radius: Tests only.
-
Recommendation: Add an AST guard,
tests/test_error_message_safety.py, that scanssrc/forraise <AppError subclass>(...)and fails when the first positional argument is an f-string containing a formatted value whose name matches a path-like or exception-like identifier (path,_path,root,dir,exc,err,e). Model it on the existing AST guards, which are the established pattern here (test_ui_boundaries.py,test_service_boundaries.py,test_orphan_sweep.py). Pair it with a second guard asserting that no UI module readserror_detailwithout routing through the sanitizing projection from HIGH-01 — that one closes the render side, which is where the real leak is. -
Effort: M
Low Severity
[LOW-01] ui/pages/tags_page.py is dead code; the V6.1 roadmap is correct
- Location:
src/transcription/ui/pages/tags_page.py(93 lines); registration list atsrc/transcription/ui/__init__.py:37-43 - Reachability: Not reachable. This resolves the flagged open item: the route is genuinely
not registered.
register_pagescalls seven page registrars andtags_pageis not among them; nothing anywhere imports the module.docs/roadmap_plan.md:47("Retire the Tags page") is accurate, andtests/ui/test_tags_page.pycorrectly asserts/ui/tagsreturns 404 — though it passes trivially, since an unimported module cannot register anything. - Problem & Consequence: No runtime risk; purely stranded code. It is worth noting that if it
were ever re-registered,
tags_page.py:71-74traversesdocument.document_tagsandlink.tag_refinside a page render, and those relationships are configuredlazy="raise"(docs/architecture.md:200-203) — so re-enabling this module without adding eager loads tolist_documentswould raise on first render. - Recommendation: Delete
src/transcription/ui/pages/tags_page.py. Retaintests/ui/test_tags_page.pyas the retirement guard. Fixing MED-01 first would make this finding reproducible by the suite rather than by manual inspection. - Effort: S
[LOW-02] benchmarking.py ships in the runtime package but is referenced only by tests
- Location:
src/transcription/benchmarking.py(69 lines); sole consumerstests/test_v42_evidence.py:15-16(EditorialAssessment,score_transcription) - Reachability: Live as importable API; never invoked by application code.
- Problem & Consequence: No defect. It supports the model-evaluation policy in
docs/invariant/ai_evidence_and_provenance.md:113-126, which is legitimate, but it currently has no production caller and no tooling entrypoint, so it is indistinguishable from drift. - Recommendation: Either move it under
tools/alongside the other operator utilities, or add aKNOWN_ORPHANS-style rationale recording that it is retained as the evaluation-policy implementation. Do not silently keep it unlabeled. - Effort: S
[LOW-03] Two overlapping prompt error types split across modules
- Location:
src/transcription/services/errors.py:15-16(PromptLoadError) andsrc/transcription/services/prompts.py:19(PromptStoreError) - Reachability: Live; no misbehavior observed.
- Problem & Consequence:
services/errors.py:1-8documents itself as the neutral home for exceptions raised by more than one service, precisely so a caller'sexceptclause does not change when an operation moves.PromptStoreErroris defined outside that module and covers an overlapping domain (prompt file access), so a caller wanting to handle "any prompt failure" must import from two modules and know which is which.sources.py:855raisesPromptLoadErrorfor a missing prompt file whileprompts.py:132raisesPromptStoreErrorfor the same condition reached through the Settings page. - Recommendation: Move
PromptStoreErrorintoservices/errors.pynext toPromptLoadError, or make one a subclass of the other so a singleexceptcovers prompt failures. Low urgency; do it opportunistically when HIGH-02 touches both files anyway. - Effort: S
4. Architectural Drift & Gap Analysis
| Area / Component | Direction | Documented / Intended Rule | Actual Implementation State | Severity | Recommended Resolution |
|---|---|---|---|---|---|
| Error presentation | doc->code |
docs/error_handling.md:110 — detail is internal only, surfaced by format_error_detail and logs |
sources_page.py:484, table/sources.py:90, settings_page.py:562 render error_detail verbatim to users |
High | Sanitizing render projection (HIGH-01); do not strip detail |
| User-facing messages | doc->code |
invariant/error_handling.md:59 — no local filesystem detail in user-facing messages |
5 live sites interpolate absolute paths into AppError.message |
High | Generic message, path on detail (HIGH-02) |
| Evidence invariant ownership | doc->doc |
docs/index.md:1 — baseline is V6.1 |
invariant/ai_evidence_and_provenance.md:130 names "Canonical V4"; the currency guard scans the file but its regexes match neither the phrasing nor a minor-less V4 |
Medium | Update text; make the guard token-based, or drop version labels entirely (MED-02, §8.1) |
| Enforcement claim | doc->code |
docs/error_handling.md:115 — split "Enforced by tests/test_errors.py::…" |
That test covers only classify_unexpected_error; explicit raises unguarded |
Medium | Add AST guard (MED-07) |
| Orphan sweep | doc->code |
test_orphan_sweep.py:1-13 — sweep is "deterministic" and "conservative" |
Bare-name matching; cannot see a dead module (tags_page.py) |
Medium | Qualify by module + module-reachability pass (MED-01) |
| Worker scaling | code->doc |
jobs.py:191-197 — SKIP LOCKED so "concurrent workers never contend" |
Claim path is multi-worker-safe; stale-reclaim path is not | Medium | Derive stale threshold from job duration; document single-worker constraint until fixed (MED-04) |
| Provider adapter contract | code->doc |
providers/base.py:110-118 — evidence read as "most recent call" state |
Contract is silently non-reentrant; safety lives in workflows.py's sequential loop |
Medium | Scope evidence to the call; document non-reentrancy (MED-05) |
| Settings env file | code->doc |
config.py:66-75 — env_file=".env.production" |
Correctness depends on an undocumented cwd contract with Dockerfile WORKDIR /app |
Medium | Explicit resolver seam, or document + guard the contract (MED-03) |
| Tags page | (no drift) | roadmap_plan.md:47 — Tags page retired |
Route genuinely unregistered; module is stranded code | Low | Delete the module (LOW-01) |
5. Invariant Inventory & Routing Recommendations
| Invariant / Constraint | Current Location | Recommended Target Layer | Rationale |
|---|---|---|---|
detail/error_detail never rendered to users |
docs + instructions | Deterministic test + sanitizing projection | Stated in three documents and violated in three files; prose has demonstrably failed to hold it |
message carries no paths or exception text |
instructions; partial test | Deterministic test (AST, all raise sites) | Existing guard covers one function; the gap produced HIGH-02 |
ExecutionAttempt.error_detail retains root cause |
docs + test_v42_evidence.py |
Keep in tests — already correct | Counterweight to the above; must be named in any fix so it is not over-corrected |
| Intermediate pages commit individually | workflows.py docstring + test_workflows_reliability.py:318 |
Keep in tests — verified genuine | Cross-session read makes it a real durability assertion |
| Final page atomic with terminal status | services.instructions.md + test_pipeline_atomicity.py |
Keep in tests — verified genuine | Fault injection makes a split commit fail |
| Canonical baseline version consistency | docs/index.md + test_meta_contract_guards.py:89 |
Repair existing test, or remove the labels | Guard exists but matches by approved phrase and requires a minor version, so it fails open on new phrasings (MED-02) |
| Module-level reachability / dead modules | test_orphan_sweep.py (ineffective) |
Deterministic test (repair existing) | Guard exists but cannot detect the case (MED-01) |
| Stale threshold > max job duration | (unenforced) | Config validator + test | Currently a coincidence of two equal defaults (MED-04) |
| Provider adapter non-reentrancy | (unenforced, implicit) | Instructions + protocol docstring | A design constraint callers must know before adding concurrency (MED-05) |
| Env-file resolution independent of cwd | tests/conftest.py monkeypatch |
Code seam + docs/production-runbook.md |
A test-only fix for a production coupling is misrouted enforcement (MED-03) |
6. Stack-Specific Analysis
Python 3.12+. Modern and consistent. PEP 695 generics are used correctly and non-trivially
(RegistryService[ModelT: RegistryEntry] in services/registry.py:58, UiActionOutcome[T],
_get_or_raise[ModelT]), type statements appear in db/session.py:15,50, and X | None is
used throughout. structural Protocol bounds (RegistryEntry, WorkerNotifier,
TranscriptionProvider) are used to avoid type suppressions rather than to decorate. ty passes
clean with no suppressions found. The two # noqa uses (workflows.py:228 PLR0915,
workflows.py:383 BLE001) are both justified in context — the broad catch is a deliberate
per-page containment boundary that immediately classifies and re-records.
FastAPI. Lifespan is handled via @asynccontextmanager (app.py:36), not the deprecated
@app.on_event. Session factories are injected through Depends (SessionFactoryDep,
db/session.py:50) rather than reached as globals from routes. api/errors.py centralizes
envelope translation. One residual: get_settings is @cached and read as a module-level
fallback in ~10 modules; this is acceptable given the documented restart-to-apply contract
(docs/ui/pages/settings.md:28) but means the cache is process-lifetime and unclearable.
NiceGUI (pinned 3.13.0). The pin is a recorded release-stability decision and is not
reported as a defect. Boundaries are enforced structurally: test_ui_boundaries.py uses an
import allowlist, which is the right polarity. Blocking work is dispatched off the event loop
via run_blocking (settings_page.py:818,822). The one boundary that is not enforced is
presentation of internal fields (HIGH-01) — pages are prevented from touching persistence but not
from rendering internal-only text.
SQLModel / SQLAlchemy. Strong. lazy="raise" on relationships forces explicit eager loading;
read paths declare selectinload chains with comments explaining why each is needed
(sources.py:309-316 is a good example). expire_on_commit=False (db/session.py:28) is set
deliberately, which is what makes post-commit attribute access in evidence.py:159-202 safe.
claim_next_queued_job (jobs.py:186-241) branches correctly on dialect — SKIP LOCKED on
PostgreSQL, conditional UPDATE ... RETURNING on SQLite — rather than assuming one engine.
Attempt-number allocation uses begin_nested() with bounded retry (sources.py:598-616), the
right pattern for a monotonic per-parent sequence. No N+1 patterns were found in the read paths
sampled.
Pydantic V2 & Settings. Fully V2; no @validator, class Config, .dict(), or parse_obj
anywhere. Evidence contracts use ConfigDict(extra="forbid", frozen=True) (providers/evidence.py:47),
which is exactly right for persisted provenance — an unexpected field fails loudly rather than
being silently dropped. SecretStr guards the API key. The discriminated
SqliteSettings | PostgresSettings union is clean. normalize_provider_models correctly runs
mode="before" so the derived tuple is produced by construction rather than by mutating a frozen
model — a subtlety that is easy to get wrong. Sole issue: the cwd-coupled env_file (MED-03).
Asyncio Workers. Notably careful. asyncio.shield wraps both the per-page commit and the
terminal commit (workflows.py:601-614, 650-663), with the except CancelledError: await task; raise pattern that actually completes the shielded work rather than merely deferring cancellation —
a detail most implementations get wrong. handle_worker_exceptions (worker.py:157-182)
distinguishes retriable from non-retriable faults and stops the loop rather than spinning.
_advance_job_with_containment (worker.py/workflows.py:507-540) guarantees a claimed job
cannot strand in PROCESSING. worker_consumer_lifespan has a bounded shutdown with escalation to
cancel(). Gaps are MED-04 and MED-05, both latent and both with stated unblocking conditions.
OpenRouter / Adapter Boundary. Encapsulation holds: test_provider_boundaries.py enforces it,
and workflows.py imports only providers abstractions. _CapturingAsyncClient is a
well-judged design — it captures the exact transport body before SDK parsing without altering what
the SDK consumes, including the streamed case. Timeout construction (openrouter.py:200-206)
correctly overrides httpx's 5s per-phase default that would otherwise silently cap the configured
budget. SAFE_RESPONSE_HEADERS (providers/evidence.py:29-41) was reviewed field-by-field:
all nine entries are non-secret correlation, content, or rate-limit headers, and
filter_safe_response_headers is a true allowlist filter with no redaction-after-capture — this
satisfies invariant 3.8.2 exactly. _replace_embedded_media correctly substitutes a source
reference for base64 payloads, satisfying 3.8.3. The one structural weakness is MED-05.
Testing & Quality Tooling. 410 tests, all green, with genuinely strong contract guards
(boundaries, model contract, media path safety, evidence append-only, atomicity). Marker strictness
and asyncio_mode = "strict" are configured, and no unawaited-coroutine warnings appeared. Two
guards, however, assert meaningfully less than their names and docstrings claim
(test_orphan_sweep.py — MED-01; test_errors.py path-leak coverage — MED-07), and the
>= 420 snapshot threshold at test_orphan_sweep.py:169 repeats a weak-assertion pattern the
2026-08-23 review already flagged at >= 200; it was raised rather than replaced with
set-membership.
7. Duplication & Consolidation Report
| Pattern / Duplication | Locations | Proposed Canonical Home | Estimated Lines Removed |
|---|---|---|---|
f"{type(exc).__name__}: {exc}" detail construction |
errors.py:118, maintenance.py:71,95,135,206, runtime_settings_store.py:388,476,554 |
errors.py::exception_detail(exc) |
~8 (consistency > line count) |
Filesystem AppError construction from OSError |
prompts.py:186-191, runtime_settings_store.py:384-389,472-477,550-555 |
errors.py::filesystem_error(message, exc, *, suggestion) |
~20 |
| Overlapping prompt error types | services/errors.py:15, services/prompts.py:19 |
services/errors.py (LOW-03) |
~5 |
Duplicated provider_duration_ms / processing_duration_ms max-clamp arithmetic |
workflows.py:341-345, 361-368, 397-404 |
workflows.py::_page_durations(started_at, finished_at, monotonic_started_at) |
~20 |
_utc_now_naive defined per module |
workflows.py:51, jobs.py:29, db/models.py, sources.py |
Single helper in db/models.py, imported |
~12 |
Proposed Canonical Abstractions
# src/transcription/errors.py
def exception_detail(exc: BaseException) -> str:
"""Internal-only root-cause text for AppError.detail. Never user-facing."""
def filesystem_error[E: AppError](error_type: type[E], message: str, exc: OSError, *, suggestion: str) -> E:
"""Build a filesystem AppError with a generic message and the path on detail."""
# src/transcription/ui/components/error_presenter.py
def display_failure_detail(error_detail: str | None) -> str | None:
"""Sanitize persisted failure detail for UI rendering (HIGH-01)."""
8. Meta-Tooling & Instruction Update Recommendations
docs/invariant/ai_evidence_and_provenance.md:130— resolve the V4 label. Two viable routes, and the maintainer has proposed the second:- (a) Repair the guard. Fix the text to V6.1 and make
test_canonical_docs_declare_one_consistent_baselinetoken-based rather than phrase-based (MED-02). Keeps version labels as navigational anchors. - (b) Remove version labels from canonical docs. While the project has a single principal
user and no released versions to support, "canonical" and "current" are the same thing, so the
label carries no information a reader can act on — it only creates a second thing to keep in
sync. Retain the baseline declaration in
docs/index.mdalone as the release marker, keep version language indocs/roadmap_plan.mdand the migration/deployment docs (already excluded from the scan for exactly this reason), and replace in-body references with unversioned phrasing ("the canonical architecture, schema, requirements, and error-policy documents"). The guard then inverts: assert that no canonical doc outside the exclusion set contains a version token at all, which is a stricter and much cheaper property to hold than agreement between many labels. Requirement IDs (REQ-4-021,REQ-6-014) are stable identifiers, not currency claims, and should be left alone.
- (a) Repair the guard. Fix the text to V6.1 and make
docs/error_handling.md:107-115— either add the sanitizing-projection rule for UI display oferror_detail, or revise thedetail"Surfaces" row to admit operator-facing evidence displays. Update the "Enforced by" line once MED-07's guard lands, since it currently overstates coverage..github/instructions/error-handling.instructions.md— add an explicit clause under "User-Safe Messaging" stating that persistederror_detailis subject to the same no-paths rule at any render boundary. The current table (line 86) states the rule forAppError.detailand stops there, so the persisted-then-rendered path falls between the lines..github/instructions/providers.instructions.md— record the adapter non-reentrancy constraint (MED-05); it is currently an undocumented precondition ofworkflows.py..github/instructions/services.instructions.md— the two competing atomicity invariants are well described and both guards verified; no change needed. Worth adding the stale-reclaim threshold constraint (MED-04) alongside them, since it is a third worker-lifecycle rule with no documented home.tests/test_orphan_sweep.py— repair per MED-01 and replace the>= 420threshold with set-membership assertions.- New
tests/test_error_message_safety.py— AST guard per MED-07, covering both the raise sites and the UI render sites. docs/production-runbook.md— document the cwd/WORKDIRcontract for.env.productionresolution if MED-03 is resolved by documentation rather than by a code seam.
9. Prioritized Dependency-Ordered Action Plan
Phase 1 — Blocking fixes (privacy; ordered, HIGH-01 first)
- HIGH-01 — add
display_failure_detailand routesources_page.py:484,table/sources.py:90-95, andsettings_page.py:562through it. Do this first: it closes the render boundary, so the Phase-1.2 fix cannot relocate a leak again. - HIGH-02 — move paths from
messagetodetailat the five sites, including theprompts.py:186double violation. - MED-06 — move provider payload text to
detail; adddetail=to all threehandle_transcription_errorshandlers.
Phase 2 — Enforcement hardening (make Phase 1 permanent)
4. MED-07 — AST guard for raise-site message safety and for UI reads of error_detail.
5. MED-01 — qualify orphan definitions by module; add module-reachability; replace the
snapshot threshold.
6. MED-02 — fix the V4/V6.1 text and extend the meta-contract guard to baseline-version currency.
Phase 3 — Reliability & concurrency (latent; each must precede its unblocking change)
7. MED-04 — derive worker_stale_job_seconds from worker_provider_timeout_seconds with a
rejecting validator, ideally plus a progress heartbeat. Must land before any second worker
replica.
8. MED-05 — scope provider evidence to the call rather than the instance. Must land before
any intra-job page concurrency. Document non-reentrancy immediately as an interim step.
Phase 4 — Consolidation & refactoring
9. MED-03 — explicit env-file resolution seam shared by Settings and runtime_settings_store;
remove the model_config monkeypatch from conftest.py.
10. LOW-01 — delete tags_page.py (after MED-01, so the suite reproduces the finding).
11. LOW-03 and the §7 consolidations — fold in opportunistically while Phase 1 touches these files.
Phase 5 — Non-blocking governance/documentation depth
12. LOW-02 — relocate or annotate benchmarking.py.
13. Instruction/doc updates §8.3–§8.5, §8.8.
10. Preserved Strengths
- Append-only evidence is real, not aspirational. Every provider call produces a distinct
ExecutionAttempt; no runtime path mutates a historical row. Projection writes ontoSource.raw_transcriptionare clearly separated from history, andpromote_machine_attempt(evidence.py:117-146) repoints the projection without rewriting evidence — with a docstring that explains exactly why that one write lives in a read-oriented service. - Transport-layer terminology is honored in code.
_CapturingAsyncClientexists specifically so the stored body is the application-boundary capture rather than an SDK-parsed object, andTransportEvidence(response_received=False)explicitly represents "no response" instead of conflating it with an empty one. This is invariant 3.4/3.5 implemented rather than asserted. - Header allowlisting is done the hard, correct way — filter-before-store with an explicit
frozenset, never capture-then-redact (
providers/evidence.py:29-41,130-134). - Boundaries are enforced by allowlist, not blocklist.
test_ui_boundaries.py:20-25states the reasoning explicitly; it means a newly added persistence helper cannot slip through under an unlisted name. - The two competing atomicity invariants are both correctly implemented and both genuinely guarded, with the tests structured so that the naive over-correction fails.
- Cancellation safety in the worker is unusually well handled —
asyncio.shieldplusawait taskonCancelledErroractually completes the commit rather than merely deferring cancellation. - Comments explain rationale, not mechanics.
workflows.py:269-271,openrouter.py:200-202,config.py:114-115, andjobs.py:191-197each record why a non-obvious choice was made, several citing the review log entry that motivated it. This is what made verifying the atomicity and timeout invariants tractable in this review. - Documentation-to-code traceability is strong overall. Page contracts, schema field tables, and requirement IDs are maintained and guarded; the drift found in this review is narrow and specific rather than systemic.
Appendix A — Repo-Specific Deterministic Checks
| # | Check | Result | Evidence |
|---|---|---|---|
| 1 | Service boundary rule: no service-to-service imports | Pass | tests/test_service_boundaries.py green; AST scan, allowlist-based; workflows.py composes via ServiceBundle |
| 2 | UI boundary rule: no persistence access from pages/components | Pass (structurally) | tests/test_ui_boundaries.py green. Caveat: it guards data access, not presentation of internal-only fields — see HIGH-01 |
| 3 | Status vocabulary conformance; no stringly-typed literals | Pass | tests/test_model_contract_guards.py green; enum members verified against db/models.py |
| 4 | Evidence ownership: append-only history, projections not history mutation | Pass | test_v42_evidence.py::test_attempts_are_append_only_and_exported_with_integrity verified non-vacuous (asserts both retained attempts and export integrity at lines 281-290) |
| 5 | Canonical authority: findings resolve against docs/* first |
Pass with defect | test_canonical_authority_references_are_present green. The companion baseline-currency guard (test_canonical_docs_declare_one_consistent_baseline) scans the offending file but fails open on its phrasing and on minor-less version tokens — MED-02 |
| 6 | Schema contract fidelity: docs/schema.md field-accurate |
Pass | test_model_contract_guards.py + test_meta_contract_guards.py green |
| 7 | Media boundary: record-validated media, controlled URL resolver | Pass | test_media_path_safety.py, tests/ui/test_media_urls.py green; public_media_path_label verified path-safe |
| 8 | Eager-loading conformance vs lazy="raise" |
Pass | Declaration-side guard green; sampled read paths declare explicit selectinload chains. Note: dead tags_page.py:71-74 would violate it if re-registered (LOW-01) |
| 9 | Cross-cutting error conformance | FAIL | Guards green but coverage is narrower than documented: HIGH-01, HIGH-02, MED-06, MED-07 |
| 10 | Orphan/dead-code conformance | FAIL | Guard green but structurally unable to detect a dead module: MED-01, proven by LOW-01 |
Appendix B — Evidence & Provenance Auditor Families
| Family | Subject | Result | Evidence |
|---|---|---|---|
| A | Attempt history append-only | Pass | No update/delete path to ExecutionAttempt; insert-only with begin_nested + bounded sequence retry (sources.py:556-616) |
| B | Attempt numbering monotonic per source | Pass | insert_with_sequence_retry; uniqueness constraint plus retry on conflict |
| C | Transport evidence captured at the transport boundary | Pass | _CapturingAsyncClient retains the exact wire body pre-SDK-parse (openrouter.py:66-94) |
| D | Absent response distinguished from empty response | Pass | TransportEvidence.response_received is explicit, not inferred |
| E | Response header persistence is allowlist-based | Pass | SAFE_RESPONSE_HEADERS (providers/evidence.py:29-41) — all nine entries verified non-secret; filter-before-store |
| F | No machine-local detail on user-facing surfaces | FAIL | error_detail rendered verbatim at three UI sites (HIGH-01); paths in message at five sites (HIGH-02) |
| G | Request manifest excludes embedded media payloads | Pass | _replace_embedded_media (openrouter.py:377-395) substitutes a source reference for base64 data |
| H | Evidence attribution is correct under concurrency | Pass today / at risk | Correct in the current sequential single-worker deployment; the contract itself is non-reentrant (MED-05) and reclaim has no margin (MED-04) |