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:
@@ -313,9 +313,11 @@ def test_chat_bottom_unit_is_last_child_of_the_chat_shell() -> None:
|
||||
`.chat-shell` is the `.chat-bottom` wrapper — NO id (nothing in JS
|
||||
binds it; the bindings live on the inner elements, the move is pure
|
||||
HTML/CSS) — holding the `.chat-actions` row, the phase-104
|
||||
`#char-count` counter, and the `#composer` form, in that order: the
|
||||
row + counter + composer are ONE sticky unit, and the wrapper owns
|
||||
the shell's bottom slot, so the sticky shift range is still that
|
||||
`#char-count` counter, the phase-123 `#attach-preview` strip (hidden
|
||||
by default — zero height at rest, the sticky geometry untouched),
|
||||
and the `#composer` form, in that order: the row + counter +
|
||||
preview + composer are ONE sticky unit, and the wrapper owns the
|
||||
shell's bottom slot, so the sticky shift range is still that
|
||||
column's box (a sibling after it would carve the range away and
|
||||
re-break the pin). The composer form keeps `novalidate` and its
|
||||
contract ids."""
|
||||
@@ -331,11 +333,12 @@ def test_chat_bottom_unit_is_last_child_of_the_chat_shell() -> None:
|
||||
"elements"
|
||||
)
|
||||
kids = last["children"]
|
||||
assert len(kids) == 3, (
|
||||
"the unit holds exactly three element children: .chat-actions, "
|
||||
"then #char-count (phase 104), then #composer"
|
||||
assert len(kids) == 4, (
|
||||
"the unit holds exactly four element children: .chat-actions, "
|
||||
"then #char-count (phase 104), then #attach-preview (phase 123), "
|
||||
"then #composer"
|
||||
)
|
||||
row, counter, form = kids
|
||||
row, counter, preview, form = kids
|
||||
assert row["tag"] == "div" and (
|
||||
row["attrs"].get("class") or ""
|
||||
).split() == ["chat-actions"], (
|
||||
@@ -354,6 +357,18 @@ def test_chat_bottom_unit_is_last_child_of_the_chat_shell() -> None:
|
||||
"the counter ships hidden — it appears only from 80% of the "
|
||||
"4,000-char cap (app.js updateCharCount)"
|
||||
)
|
||||
# Phase 123 (task 02, TODO L6): the attach preview strip — a
|
||||
# hidden-by-default div between the counter and the composer (the
|
||||
# selected image above the input row; zero height while hidden, the
|
||||
# pinned-cluster geometry untouched).
|
||||
assert preview["tag"] == "div" and (
|
||||
preview["attrs"].get("id") == "attach-preview"
|
||||
), "the third child is the phase-123 #attach-preview strip"
|
||||
assert (preview["attrs"].get("class") or "") == "attach-preview"
|
||||
assert "hidden" in preview["attrs"], (
|
||||
"the strip ships hidden — it appears only while a file is "
|
||||
"attached (app.js attachedImage)"
|
||||
)
|
||||
assert form["tag"] == "form" and form["attrs"].get("id") == "composer"
|
||||
assert "novalidate" in form["attrs"], (
|
||||
"phase 48: the composer form stays `novalidate` (a `required` "
|
||||
|
||||
Reference in New Issue
Block a user