Document: cr-create-conversation-opening-turn-v0_1
Version: v0.1
Date: 2026-06-28
Type: Phase change request — Operator Layer frontend only (loomworks). No engine change — the engine's speak-first machinery is already built. This is the door-1 piece of the engagement create-stage three-door design.
Consumer: Claude Code (executor).
Branch base: OL main @ 3f2fca8 (the merged Companion-surface arc; branch fresh off main).
Governing scoping note: loomworks-create-stage-three-doors-scoping-note-v0_1 (door 1 — the conversation, the common finishing room).
Grounding: the create-page opening-turn inspection this session (the create_engagement_entry template, the ChatView mount behavior, the dropped Workshop greeting, the unwired empty-state copy — engine 5ccc3d7, OL pre-merge 3f2fca8).
When you go to create an engagement, the page lands as a bare "Message Companion…" box on an empty screen — nothing greets you, nothing tells you what to do. The Companion is built to speak first (greet you and ask the first question), but the screen never gives it the chance to. This fixes that: when the create conversation opens, the Companion greets you and asks the first thing — what the work is, who it's for. One change, frontend only; the engine already knows how to greet. This is the first of three "doors" into creating an engagement (the others — a quick generator and a document converter — come later and feed into this same conversation).
The opening turn is a frontend wiring gap over a ready engine — confirmed by inspection:
create_engagement_entry intent template (intent_instructions/create_engagement_entry.md) opens the conversation: "The Operator has just signaled they want to start a new project… Greet them warmly… then ask the first elicitation question… Good first questions: what the project is for, who's involved." The handler _route_create_engagement_entry (router.py:1742-1773) accepts an empty message, does no work, returns operation_data={"creation_mode":"entry"} — its docstring says "the responder composes the greeting + first elicitation question from the entry template." The converse endpoint (converse.py:788-793) accepts intent_hint and bypasses the classifier with confidence=1.0. An empty message + intent_hint="create_engagement_entry" routes straight to the greeting.ChatView only sends a converse call from handleSend, which early-returns on an empty message (ChatView.tsx:259 — if (!trimmed || thinking) return;) and only then attaches intent_hint (:302-305). There is no mount effect that opens the conversation. The only converse call in create mode is the one the Operator triggers by typing. The engine is designed to speak first; the frontend never gives it the opening.ConversationFlow.tsx OpeningDoor, "What would you like to be working on?", Phase 31) — the rebuild's ChatView (Phase 46) never ported it. And ChatView doesn't even render the generic empty-state copy that standard mode shows (ConversationTranscript.tsx:179-183, SURFACE.conversationEmpty = "No conversation yet — say something, or type a note below.") — the string exists, ChatView just never imports it.Net: the create surface is worse than a normal empty conversation — engine ready to greet, frontend never asks it, both fallback greetings absent. The screenshot's bare box is the precise signature of all three.
Aligned. Door 1 is the conversational authoring of the seed — the seed's own machinery (the create conversation already writes the reviewed conversation's facts into the engagement's Memory at commit, Operator-authority). A warm open is the entry to that authoring act. The greeting comes from the engine template (persona-aware, plain-terms) — consistent with the Companion's voice. No seed commitment is touched; this is the opening of the conversation that later produces the seed.
intent_hint="create_engagement_entry"); the engine's existing template greets and asks the first question. Do NOT revive the old static OpeningDoor string (that would be a second, competing greeting source).handleSend empty-guard — a dedicated mount effect, not handleSend (which early-returns on empty by design, correctly, for typed sends).ChatView.tsx:156-159) — the opening turn is generated, not fetched; the skip stays.conversationEmpty wired into ChatView so something priming shows instantly, before the engine round-trip). Lean: not in this CR. The engine greeting with a loading state is the clean single-source fix; a static fallback flashing then being replaced by the greeting adds a flash-then-replace wrinkle. If the round-trip proves slow enough to feel empty in the live check, add the fallback then. Named, not built.OpeningDoor — one greeting source (the engine).Per-step commits, suite green at each, halt-before-push. Frontend only; no engine, no migration.
Confirm against OL main @ 3f2fca8:
main at 3f2fca8.ChatView create-mode initialization — the mount/effect structure (:156-159 history-skip, :259 handleSend empty-guard, :302-305 intent_hint attach). Confirm where a mount effect that fires once on create-mode entry attaches, and that it can call the converse path without going through handleSend's empty-guard.ChatView uses (the client converse function / hook) — confirm an opening call can send an empty message + intent_hint="create_engagement_entry" (the endpoint accepts it per grounding; confirm the client wrapper doesn't itself guard against empty).ChatView already shows during a normal turn — confirm it can cover the opening round-trip (so mount → loading → greeting, not mount → blank → greeting).Report findings, then build.
In ChatView (create mode only):
intent_hint="create_engagement_entry", bypassing the handleSend empty-guard (call the underlying converse function directly, or a dedicated sendOpening path — not handleSend).
Suite expectation: a test that mounting ChatView in create mode fires one opening converse call with intent_hint="create_engagement_entry" and an empty message; that it fires exactly once; that the greeting turn renders. Existing ChatView tests unaffected (standard mode doesn't fire the opening call — confirm standard mode is untouched).
Verify (test + manual) that opening an existing engagement's conversation (standard mode) does NOT fire the opening call — the opening greeting is create-mode-only. Standard mode keeps its existing behavior (history fetch, no auto-greeting).
Suite expectation: a test asserting standard mode fires no opening call on mount. (May fold into Step 1's test file.)
/operator/create-engagement (via the create button or directly) shows the Companion greeting and asking the first elicitation question — not a bare empty box. The page invites activity.create_engagement_entry.md), not a revived static string.ChatView standard mode, the create flow's own conversation/commit, and the surrounding surface unchanged; the opening call is additive.
Note on lint: this CR adds a useEffect that fires a call on mount — adjacent to the setState-in-effect pattern that makes up the standing lint debt. Structure the effect to not add a new lint error (the call is a side-effect dispatch, not a setState-in-effect; if it trips the rule, that's worth getting right rather than adding to the debt this very session named).
DUNIN7 — Done In Seven LLC — Miami, Florida Change Request — Create conversation opening turn (door 1) — v0.1 — 2026-06-28 Fires the engine's ready opening greeting on create-mode mount (empty message + intent_hint="create_engagement_entry"), bypassing the handleSend empty-guard, once, with a loading state over the round-trip. Frontend-only — the engine's speak-first machinery is built; ChatView just never gave it the opening. Closes the empty-create-conversation regression and stands up door 1 (the conversation finishing room) of the create-stage three-door design. One greeting source (the engine template); the static OpeningDoor is not revived; the empty-state fallback is deferred. Base OL main 3f2fca8.