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:
@@ -126,10 +126,24 @@ def test_raw_text_only_stored_and_re_rendered_on_restore() -> None:
|
||||
def test_save_points_user_on_send_and_brain_on_done() -> None:
|
||||
"""Save points: the user message is stored the moment it is sent (BEFORE
|
||||
the fetch — a failed turn keeps the question); the brain message is
|
||||
stored on `done` with the done metadata (sources/deflected/suggestions)."""
|
||||
stored on `done` with the done metadata (sources/deflected/suggestions).
|
||||
|
||||
Phase 123 (task 02): the user push is conditional — the record gains
|
||||
the optional `image` key (the STORED path from the upload step,
|
||||
A5: never base64) only when an attachment exists; the text-only
|
||||
branch is the pre-phase object verbatim. The save point (push +
|
||||
save before the turn starts) is the contract."""
|
||||
js = _js()
|
||||
user_push = js.find('conversation.push({ who: "user", text })')
|
||||
assert user_push != -1
|
||||
run_turn = js.find("async function runTurn")
|
||||
assert run_turn != -1, "runTurn must exist (the phase-49 extraction)"
|
||||
user_push = js.find("conversation.push(", run_turn)
|
||||
push_block = js[user_push : js.find("saveConversation()", user_push)]
|
||||
assert '{ who: "user", text, image: image.path }' in push_block, (
|
||||
"an attached question stores the image path (A5)"
|
||||
)
|
||||
assert '{ who: "user", text }' in push_block, (
|
||||
"a text-only question keeps the pre-phase record shape"
|
||||
)
|
||||
assert user_push < js.find('fetch("/api/chat"'), (
|
||||
"the user message must be saved before the turn starts"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user