You can upload files and images today — they store, and images are even interpreted by the vision skill. But the interpretation gets dropped: it never becomes a Memory assertion, so you can't confirm it, recall it, or reason over it. An uploaded photo of Fenwick's leaf produces a description that goes nowhere.
This CR wires the last connection: an uploaded image's description (and a document's extracted content) becomes a held assertion you confirm — flowing through the same capture→confirm→commit grammar as a spoken note. Upload a photo → the machine describes it → proposes it as a held note ("I see a Monstera with a new fenestrated leaf — save this?") → your confirm commits it to Memory. The file is stored AND its content becomes recallable memory.
This completes Room 1's intake: Memory can now take in what you hand it, not only what you speak.
Seed §Memory line 43: a contribution carries how it arrived — "typed, spoken, extracted from a document, described from an image." The seed already names described-from-image and extracted-from-document as first-class contribution kinds with automatic lineage. Line 45: "A correction is a contribution. A retraction is a contribution. Both are preserved" — the append-only assertion model.
So this CR does NOT introduce a new commitment — it closes the gap between the seed's standing commitment (intake includes described-from-image / extracted-from-document, as contributions) and the current Rail-B code (which interprets but drops the result at event grain). We make the code honor what the seed already says. No seed conflict; the seed mandates the target.
The mechanism (how extraction/description becomes a held assertion, the routing, storage) is specification-level, governed by the comprehensive specification — this CR builds the mechanism for the seed's commitment.
image_vision_analysis (Claude Vision), image_ocr, image_metadata, pdf/docx/text/audio extraction — all real, complete code.add_assertion → held → commit_assertion, the capture→confirm→commit loop (_route_add_knowledge).POST /engagements/{id}/uploads.contributions.py:280 / image_description.py) — the reference: upload → interpret → held assertion already works on the older rail. Copy the pattern onto Rail B.Wire Rail B's interpretation output → held assertion. Today uploads.py:661 hardcodes assertions_drafted=[], and no consumer converts the interpretation into an assertion — the description lives only as an object-store blob + an upload_event_received row, unreachable by recall (recall reads committed assertions only).
The fix: when an upload is interpreted (vision describes an image, extraction summarizes a document), call add_assertion with the interpretation as content, producing a held assertion with provenance recording the "how" (described-from-image / extracted-from-document, per seed §Memory line 43) and lineage to the source file. It enters the capture→confirm→commit loop: proposed as held, the Operator confirms (machine describes → proposes → human approves — Operator-authority). Mirror Rail A (image_description.py / contributions.py:280) as the reference implementation.
needs_clarification BEFORE running anything (the "0% picker" / blank "I think this is —"). Fix: an image with no explicit skill request defaults to vision (describe) — the common case — rather than dead-ending at disambiguation. OCR/metadata remain available when explicitly requested. (Consistent with "only show what is available" — removes a dead-end picker rather than adding one.)system_config.loomworks_llm_key / engagement key), not an env var. Confirm it's populated so vision can run in dev; if absent, the failure must surface truthfully (transformation_failed with a clear message), NOT silently produce no description. (Truthfulness — same discipline as Finding 3.)put_blob → 503 if no live MinIO. Ensure a dev MinIO is available or the failure surfaces truthfully. (Environment, not code — confirm/document.)webkitRelativePath dropped). Flat-list is fine for now (Operator decision 2026-07-01); preserving directory hierarchy is a refinement for later, only if a folder's structure carries meaning.An uploaded image or file becomes a held assertion (machine describes → proposes → Operator confirms) that is recallable Memory, with provenance recording it as described-from-image / extracted-from-document. The file is also stored. Failures surface truthfully. Verified at runtime by uploading a real image and confirming it enters the held tray, commits on confirm, and recalls. Suite green.
Room 1's intake works: Memory takes in files, folders, and images (with interpreted descriptions), not only spoken/typed notes. Combined with the four conformance findings (120/127/128/129), the Memory room does its full job — accumulate with provenance, from every intake path — and is genuinely done.
Then the queue (unchanged): semantic-meaning foundation (recall + correction-recognition + routing substrate), content-to-engagement routing consistency, held-tray robustness edges (incl. dismiss-verb/number-resolution, over-eager-capture), wide-net reduction, boundaries arc.