phase: 123_chat_image_questions
Build and Push Containers / build-and-push-app (push) Successful in 1m54s
Build and Push Containers / build-and-push-db (push) Failing after 13s

All gates green. Verification complete.

**Phase 123 — final verification pass (all 4 tasks already in `complete/`)**

- Verified the full implementation is in the working tree: `app/api/chat_images.py` (upload/serve pair), `ChatRequest.image`/`ChatMessage.image` (path-validated, omitted-when-None), toggle-off + stale-file hinted error frames, `build_user_content` multimodal build at both sites (chat.py deflected branch + `run_agent`), config-gated composer attach/preview/upload-then-send, restore + shared rendering, CSP `img-src 'self' data:` carve-out, mock-LLM capture buffer.
- `uv run pytest` → **2796 passed**, exit 0 (unit + integration).
- `uv run pytest --cov=app --cov-report=term-missing` → **TOTAL 99%** (29/4615 missed; phase-123 modules 99–100%).
- `uv run pytest tests/e2e/test_chat_image_questions.py -v --no-cov` → **5 passed** in isolation.
- `uv run ruff check . && uv run pyright` → clean (0 errors).

**Completion criteria:** (1) attach→send→multimodal text+image to the model, bubble/reload/shared all render it, saved chat stores the PATH with `"base64" not in json.dumps(stored)` — **verified** (E2E tests 1–4 + integration round-trip); (2) `BOR_IMAGES=false` — control hidden, exact hinted error frame, zero model calls / no query_log row — **verified** (E2E test 5 + integration); (3) text-only byte-identical (`content` stays a plain `str`) — **verified** (unit + integration); (4) all gates green — **verified**; (5) commit + phase move — left to the harness per pipeline rules (no `git add`/`commit` run).

No defects found; no live-infrastructure changes (repo + local dev DB only). **Next pending phase: none** — 123 is the last phase in `todo/`.
This commit is contained in:
2026-09-25 05:19:18 -04:00
parent a19d78d284
commit bef24e05e2
48 changed files with 3910 additions and 71 deletions
+6 -4
View File
@@ -8,7 +8,11 @@ API JSON, static assets, even the static catch-all's 404s — carries:
* ``Content-Security-Policy`` — the exact A1 string (``default-src 'self';
base-uri 'none'; frame-ancestors 'none'`` → clickjacking closed, no
inline anything because the No-CDN frontend has none);
inline anything because the No-CDN frontend has none), extended by
the phase-123 ``img-src 'self' data:`` carve-out (the question-image
composer's data-URL preview + live bubble — see ``CSP`` in
``app/core/security_headers.py``; the ``data:`` allowance is scoped
to img-src only);
* ``X-Frame-Options: DENY`` — the legacy no-framing fallback;
* ``X-Content-Type-Options: nosniff`` — the MIME-confusion belt.
@@ -69,9 +73,7 @@ def test_page_carries_all_three_headers(client: TestClient, db: Session) -> None
response = client.get("/")
assert response.status_code == 200
_assert_security_headers(response)
assert response.headers["content-security-policy"] == (
"default-src 'self'; base-uri 'none'; frame-ancestors 'none'"
)
assert response.headers["content-security-policy"] == CSP
def test_api_health_carries_all_three_headers(client: TestClient) -> None: