phase: 123_chat_image_questions
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:
@@ -696,11 +696,11 @@ def test_chat_actions_wrapper_holds_both_pills_in_order() -> None:
|
||||
from the top of the column to the bottom: below the ``#messages``
|
||||
section, directly above the composer; nothing but the row's own
|
||||
comment lands between ``#messages`` and the row, and nothing but the
|
||||
phase-104 ``#char-count`` counter + the composer comment lands
|
||||
between the row and the composer (the counter is hidden by default —
|
||||
zero height, the pinned-cluster geometry untouched). No
|
||||
other page carries ``.chat-actions`` (chat-page only, like the
|
||||
pills)."""
|
||||
phase-104 ``#char-count`` counter + the phase-123 attach preview
|
||||
strip + the composer comment lands between the row and the composer
|
||||
(both hidden by default — zero height at rest, the pinned-cluster
|
||||
geometry untouched). No other page carries ``.chat-actions``
|
||||
(chat-page only, like the pills)."""
|
||||
html = _index()
|
||||
start = html.find('<div class="chat-actions">')
|
||||
assert start != -1, "index.html must carry the .chat-actions wrapper"
|
||||
@@ -734,8 +734,20 @@ def test_chat_actions_wrapper_holds_both_pills_in_order() -> None:
|
||||
after = html[end:composer_idx]
|
||||
# Phase 104 (owner 2026-09-12): the ONE permitted child between the
|
||||
# row and the composer is the hidden-by-default question-length
|
||||
# counter — everything else (ids, buttons, sections, forms) is
|
||||
# still excluded from the gap.
|
||||
# counter; phase 123 (task 02, TODO L6) added the second — the
|
||||
# attach preview strip (the selected image above the input row:
|
||||
# thumbnail + filename + remove button), also `hidden` by default
|
||||
# (the global [hidden] rule — zero height at rest, the
|
||||
# pinned-cluster geometry untouched). Everything else (ids,
|
||||
# buttons, sections, forms) is still excluded from the gap — the
|
||||
# strip's own element block is stripped (like the counter) so the
|
||||
# exclusions below stay meaningful.
|
||||
strip_start = after.find('<div class="attach-preview"')
|
||||
assert strip_start != -1, (
|
||||
"the phase-123 attach preview strip sits above the composer"
|
||||
)
|
||||
strip_end = after.find("</div>", strip_start) + len("</div>")
|
||||
after = after[:strip_start] + after[strip_end:]
|
||||
after_minus_counter = after.replace(
|
||||
'<p class="char-count" id="char-count" hidden></p>', ""
|
||||
)
|
||||
@@ -746,8 +758,9 @@ def test_chat_actions_wrapper_holds_both_pills_in_order() -> None:
|
||||
and "<section" not in after_minus_counter
|
||||
and "<form" not in after_minus_counter
|
||||
), (
|
||||
"nothing but the phase-104 counter + the composer comment lands "
|
||||
"between the row and the composer"
|
||||
"nothing but the phase-104 counter + the phase-123 attach "
|
||||
"preview + the composer comment lands between the row and the "
|
||||
"composer"
|
||||
)
|
||||
# Phase 76 (task 02): the folded view files are gone (the shell's
|
||||
# chat view is the one and only carrier of the row — pinned above);
|
||||
|
||||
Reference in New Issue
Block a user