DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path phases/companion-surface-phase1/loomworks-companion-inengagement-conformance-checklist-v0_2.md

Companion in-engagement surface — conformance checklist — v0_2 (post-fix-batch: type scale + upload restore)

Version. v0.2 (supersedes v0.1; v0.1 stays as the baseline element/behavior record). Date. 2026-06-27 Branch. loomworks feat-companion-surface (commits: integration foundation → responsive surface → named type scale → upload restore). Scope of v0.2. Carries forward all v0.1 element/behavior rows (mockups 1 & 2) unchanged, and records two deliberate post-conformance fixes. Conversation-content and the across-engagements home remain separately queued. Verification. Vitest 441 / 76 files green (3 new upload tests + the type sweep) + live render of E0007 at desktop & mobile.

This is the principle both fixes share, stated once: the mockup is the floor for LAYOUT and BEHAVIOR, not a catalogue of which capabilities exist, and its literal px are illustration, not the type spec. Two places the fresh build took the mockup too literally are corrected here — neither is drift; both are baseline-exceeds the Operator directed.


FIX 1 — Type scale (deliberate baseline-exceeds; pays down deferred type debt)

What. The mockup's px (13px body, 11px labels) were sized for a small illustration frame; in the full-viewport surface they read too small. Introduced a small named type-scale as tokens and applied it across the in-engagement surface, replacing the literal px.

| Item | Status | |---|---| | Named role tokens --font-heading / --font-body / --font-label / --font-meta in tokens.css | ✅ roomy reading sizes (18 / 16 / 14 / 12, tunable on sight) | | Wired to Tailwind utilities text-heading / text-body / text-label / text-meta | ✅ via @theme (Tailwind v4 font-size namespace is --text-; --font- is family) | | Every reading-text literal in the surface swept onto the role utilities | ✅ ConversationPane, WorkCard, MemoryRoom, RenderingRoom, RoomView, WorkspacePane, InEngagementHeader, MobileSurface, InEngagementSurface | | Hierarchy preserved (body > label > meta; heading above) | ✅ 18 > 16 > 14 > 12 | | Type only — spacing untouched | ✅ spacing stays deferred | | Template-clean (tokens, no new one-offs) | ✅ | | Mic ● glyph sizes (18/20px) | left as icon sizing (not reading type) | | NotFoundPanel (pre-existing serif error panel, not a mockup surface) | out of scope (its own non-token styling; error path only) |

Recorded position — type-deferral paid down deliberately. Type was previously deferred debt (the designed type-scale system was named "defer; its own pass later"). That deferral was waiting on a clean hierarchy to scale from. The fresh build provided exactly that clean hierarchy, so the debt is being paid down now — deliberately, not by drift. The prior "defer type" position is preserved as the reason this is the right moment: the deferral was correct then (no clean hierarchy yet), and paying it down is correct now (hierarchy exists). This is a small named scale, NOT the full designed type-scale system (which remains deferred); only the reading-role floor is set.


FIX 2 — File upload affordance (regression-fix)

What. The fresh surface dropped the composer attach affordance to match mockup 1 (which shows none). Upload is a real built capability; dropping it to match the mockup was the wrong trade (the same error as stripping the kept sort-order toggle). Restored.

End-to-end verification (engine + frontend code):

| Pathway | Supported? | Wired? | |---|---|---| | Single-file | ✅ COMPLETE (engine: ObjectStore + purpose-classification + per-file result; frontend: the proven /chat postUpload chain) | ✅ | | Multi-file | ✅ COMPLETE (engine loops files → parent + per_file) | ✅ | | Folder (directory ingestion) | ⚠ NOT complete end-to-end | ❌ not surfaced |

Why folder is not surfaced. The engine is readyPOST /engagements/{id}/uploads builds a folder_manifest with a per-file relative_path derived from the multipart filename. But the frontend postUpload appends file.name (basename), never file.webkitRelativePath, so directory structure is silently flattened; and the Phase 60 OL folder "composer-completeness" piece is unwired. It would not 500 (it degrades to flat multi-file), but it is not true directory ingestion. Per only-show-what's-available, no folder button is shown. Folder upload waits for the Phase 60 OL work (frontend must transmit webkitRelativePath + the completeness piece must land).

| Item | Status | |---|---| | Attach (paperclip) affordance in the bottom-anchored input row, beside mic+field (desktop) | ✅ token-styled, file-only | | Attach affordance in the mobile bottom bar | ✅ (size-40 variant) | | Multi-file selection | ✅ <input multiple> | | NO folder affordance | ✅ (gated off; documented above) | | Upload runs the real pathway (useEngagementUploadpostUpload) | ✅ test asserts postUpload({engagementId, files}) | | Per-file results render | ✅ desktop: UploadResultCard on the Companion turn (incl. clarification → sends the chosen reading, mirroring /chat). Mobile: no transcript (mockup) → Companion ack surfaces transiently in the hint line, consistent with spoken replies | | useConversation extended with upload turn primitives (append turns, attach results, uploading gate) | ✅ |


Net

Both fixes hold the surface to its purpose over a literal reading of the mockup: behavior/layout conform to the mockups (v0.1 rows unchanged), while real capability (upload) and readable type are restored/raised as the Operator directed. Suite green; halt before push.

DUNIN7 — Done In Seven LLC — Miami, Florida Companion in-engagement surface — conformance checklist — v0_2 — 2026-06-27