Mira shipped in May as a persistent 1:1 development partner and has not entered regular use. This plan changes three things: the container becomes a full-window Room rather than a docked inspector, each conversation becomes a bounded segment with a beginning, an end, and an artifact, and whispers stop handing out chores. A card deck at the door replaces the empty chat box, so arriving at Mira means choosing how you want to work rather than staring at a prompt.
The problem
MiraContainerView’s own header comment still reads
“Mira’s Room — full-screen destination per project. Header with 88px
avatar.” The full-window Room was the original design and was compressed into a macOS
.inspector panel. That compression is visible in the interior: the Conversation /
Mira-led control is a two-tab toggle carrying an explicit comment saying it is a toggle rather
than a side-by-side split because it has to render at 320pt.
Width is not the whole story. MiraConversationView was forked from
DeepDiveConversationView, so it already has the transcript, the token streaming and
the attachments. What it lacks is everything that makes Deep Dive read as a 1:1 — the
pacing choreography, the chapter dividers at move boundaries, the idle suggestion affordance, and
a process that begins and ends. Mira’s thread has no beginning, no end, and no product.
The whispers compound it. All four bodies are hardcoded templates that turn an observation into an assignment:
workflowTransition — “All implementation tasks on X are done. Want
to run /cr-entry or /cr-complete?”
planIdle — “X is 70% done and hasn’t seen a checkin in a couple
days. Want to talk it through?”
repeatedConcern — “3 recent checkins on X all touched Foo.swift. Worth
a closer look?”
contradiction — “Before you commit to this — 4 days ago you
decided something that points the other way. Worth a look first.”
Each one ends in a question that hands over more work. None of them says anything. Every whisper body carries a comment promising LLM-generated copy honoring the Whisper voice register in a later phase; that phase never shipped, so all four speak template English wearing a person’s name.
The evidence that none of this is working sits in .claude/mira-friction.md, the
dogfood log gating every deferred Mira v2 phase. It holds one entry, dated 2026-05-09, tagged
[good]. The last Mira commit is 2026-08-15.
Approach
A studio container, matching Ideation, Design and Launch
The Room becomes a project-scoped destination reached from the ambient anchor, ⌘⇧M, and a Project Overview entry point. It gets the full detail column with an 88px avatar header and a real side-by-side split — conversation left, Mira-led right — instead of a segmented toggle forced by a 320pt panel.
One thread per project, with first-class segments
Rejected: separate sessions per card. MiraThread carries a single
cliSessionId that has been accumulating project context for months, and that
accumulation is what produced the single response worth logging — Mira picking one
Chronicle record out of 133 and naming the 4,000px threshold from memory. Separate sessions
start cold and re-inject context on every card click.
The target model is already written down. MiraParkedTopic’s header
comment says “Mira’s thread is one unbroken stream per project, so this
captures a topic within that stream rather than closing a conversation.” A segment
generalizes a parked topic from retroactive-and-optional to declared-up-front-and-structural.
Cards inject framing, not isolation
A card that changes how Mira opens injects a framing preamble on the user turn, the way
buildParkedTopicResumePrompt already does and the way
voiceOverlay(for:) already switches register mid-thread. No card needs its own CLI
session, its own store, or its own recovery path.
Whispers carry findings; the rest moves to surfaces that already exist
Three of the four triggers count rather than read — task percentages, elapsed hours, file-repeat tallies — and counting can only produce a reminder. Workflow transitions belong to the pipeline stepper, which exists for exactly that. Plan idleness belongs in the session digest. Contradiction stays live because it is the one case where timing matters.
Closed segments collapse, which is what makes one thread survivable
A closed segment renders as a single row — title, date, artifact chip — expanding on click. That converts an unbounded transcript into a browsable history of bounded conversations, and it is the direct answer to the concern that argued for separate sessions in the first place.
Decisions made for you
MiraParkedTopicStore folds into the segment store
Two overlapping models of “a topic within the thread” will drift. Parked topics
migrate to segments with state .parked, carrying their recaps and open questions.
Reversible by keeping both stores and having segments reference topic ids, at the cost of
maintaining both.
RepeatedConcernTrigger is deleted rather than repaired
“Recent checkins all touched Foo.swift” is nearly always true of the file you are actively working in, so the trigger flags your main file as a concern. The signal worth keeping — the same concern described twice — requires an LLM read over checkin narratives and is deferred to Pass 2.
The 90-second whisper auto-tuck is removed
A card that interrupts and then leaves costs attention twice. With a global two-per-week budget in place, a whisper that survives to the screen is rare enough to wait for you.
Three of six cards ship visibly disabled
Run it without me, Show me what’s drifted and Tell me what you’d do next render at reduced opacity with a reason on hover rather than being absent. The deck reads as a designed set from the first night, and the Pass 2 scope is visible rather than implied.
The work
Phase 1 — Segment model, store, and migration
12 tasks 12 of 12 complete- Create
Code Raven/Models/MiraSegment.swiftwithMiraSegmentKind(.ongoing,.brainstorm,.meeting,.drift,.direction,.resumed), each withlabel,systemImage, andaccent: Color - Add
MiraSegmentState(.open,.parked,.closed) to the same file - Add
MiraSegmentArtifact— an enum wrapping.meeting(UUID),.planDraft(UUID),.scratchNote(UUID),.none— so a closed segment can point at what it produced - Define
struct MiraSegment: Codable, Identifiable, Equatablewithid,projectId,title,kind,startTurnId,endTurnId: UUID?,state,artifact,recap: String,openQuestions: [String],openedAt,updatedAt,lastResumedAt: UUID? - Give
MiraSegmenta custominit(from decoder:)usingdecodeIfPresent ?? defaulton every field per project CLAUDE.md — synthesized Codable treats a defaulted non-optional as required and would throwkeyNotFoundon older files - Create
Code Raven/Services/Mira/MiraSegmentStore.swiftas@MainActor final class MiraSegmentStore: ObservableObjectwithstatic let shared - Storage path
~/Library/Application Support/Code Raven/Mira/segments/{projectId}.json, writes on a serial utilityioQueue, mirroringMiraThreadStore - Implement
segments(for:),openSegment(for:),open(kind:title:startTurnId:projectId:),close(_:artifact:recap:),park(_:),resume(_:),update(_:) - Enforce one open segment per project —
open(...)closes any currently-open segment before creating the new one - Implement
MiraSegmentMigration.runIfNeeded()converting everyMiraParkedTopicto aMiraSegmentwithkind: .resumed,state: .parked,startTurnId= firstsourceTurnIdsentry,endTurnId= last, carryingrecapandopenQuestionsacross - Guard the migration behind a
UserDefaultsversion key so it runs once per install - Preserve
exportedNoteIdas.scratchNote(id)on the migrated segment'sartifact
Phase 2 — Card deck
8 tasks 8 of 8 complete- Create
Code Raven/Models/MiraCard.swiftdefiningMiraCardwithid,title,blurb,systemImage,accent,kind: MiraSegmentKind, andavailability: MiraCardAvailability - Add
MiraCardAvailability(.ready,.comingInPass2(String)) so Pass 2 cards render visibly disabled with a reason rather than being absent - Populate
MiraCard.deckwith six cards phrased as ways of working: Think out loud with me (brainstorm), Put a room together (meeting), Pick up where we left off (resumed), Run it without me (Pass 2), Show me what's drifted (Pass 2), Tell me what you'd do next (Pass 2) - Create
Code Raven/Views/Mira/MiraCardDeck.swiftrendering aLazyVGridofMiraCardTiles, two or three columns depending on available width MiraCardTile: 3px top accent bar, title at 13pt medium, blurb at 11pt secondary, hover CTA fading 0→1, matchingMeetingGroupCard's established card grammar- Disabled tiles render at reduced opacity with the
comingInPass2reason as a.helptooltip - "Pick up where we left off" renders a compact list of parked segments beneath it when any exist, each resumable in one click
- Deck header reads the project name and the relative time of the last conversation, so arriving tells you where you left things
Phase 3 — The Room and its routing
10 tasks 9 of 10 complete- Create
Code Raven/Views/Mira/MiraRoomView.swift— the full-window destination, project-scoped - Room layout:
AppNavBar-height custom header with 88pxMiraAvatar, project name, "Mira", and the single-line state copy, matching the size the original header comment specifies - Body is an
HStacksplit: conversation on the left atminWidth: 480, Mira-led pane on the right at a fixed 340pt, divided by acmBorderPrimaryhairline - Collapse the right pane to zero width below a 900pt total width and restore the segmented toggle only in that narrow case
- Arrival state: when the open segment is nil, the conversation column shows
MiraCardDeckabove the transcript rather than replacing it, so history stays visible - Add
case mira(Project)toAppMainView.MainContentView - Route
.mira(let project)inmainContenttoMiraRoomView(project:)with.id(project.id) - Add an
onOpenMiracallback toProjectOverviewView's initializer and wire it toselectedView = .mira(project)— NOT DONE, superseded: the entry point posts.navigateToMiraRoomdirectly instead, which avoids threading a callback through seven-plus call sites - Add a "Mira" entry point in
ProjectOverviewView's header area beside the existing Foundation and Chronicle affordances - Extend
handleAppNavTabChangeso leaving the Room returns to.projectOverview(project)— DEFERRED: the Room has no back affordance yet; you leave via the sidebar
Phase 4 — Retire the panel
12 tasks 10 of 12 complete- Delete the
.inspectormodifier block fromMiraPanelHostand theinspectorPresentedbinding that drives it - Delete
MiraFloatingWrapperand the.overlay(alignment: .topTrailing)that hosts it - Keep the
VStackof content +MiraStatusBar, and keep the whisper card overlay - Remove
mode,detach(),dock(),floatingOffset,floatingSize, anddockedWidthfromMiraPanelManager, along with thePanelModeenum - Replace
MiraPanelManager.open(projectId:projectName:)withrequestRoom(projectId:)publishing a navigation intent rather than mutatingisOpen - Add
NavigateToMiraRoomas aNotification.Namefollowing the existingNavigateToReferenceArtifactspattern, and observe it inAppMainView - Point
MiraAmbientAnchor.togglePanel()and the ⌘⇧M shortcut at the room navigation intent - Point the whisper card's "Talk it through" at the same intent, preserving
engageWhisper's seeded citation turn - Make
panelContextonMiraContainerViewnon-optional-free by deleting thePanelContextenum and every branch that reads it - Keep
MiraContainerViewcompiling as the narrow-width fallback body used byMiraRoomView, or fold it intoMiraRoomViewif no caller remains — folded in and the file deleted;MiraRoomViewcarries the narrow-width toggle itself - Delete the now-unreferenced
PriorDecisionHostPayloadsheet plumbing fromMiraPanelHostand re-presentPriorDecisionSheetfrom the Room — NOT DONE, still referenced: the interrupt whisper's "View the prior decision" CTA fires from the host, where the card lives - Remove the
ProjectOverviewView.swift:939fixed-column-width workaround comment and the widths it justifies, since no inspector reflows that row any more — DEFERRED: cosmetic, and touching that row's layout risks a regression worth its own verification pass
Phase 5 — Chapters and pacing
10 tasks 10 of 10 complete- Create
Code Raven/Views/Mira/MiraSegmentDivider.swift— a centered rule with a label, ported fromDeepDiveConversationView's move-boundary divider - Render a divider in
MiraConversationViewat each segment'sstartTurnId, showing kind icon, title, and relative date - Render closed segments collapsed to a single row — title, date, artifact chip — expanding in place on click
- Keep the open segment and all turns after it expanded by default
- Create
Code Raven/Services/Mira/PacedTurnScript.swiftcollecting the Ideation delay choreography:acknowledgmentBeat()returningDouble.random(in: 0.8...1.2),questionDelayat 0.3,sectionDwellat 1.5 - Apply
acknowledgmentBeat()before Mira's first token renders on a card-launched segment so the opening does not arrive instantly - Port the idle suggestion affordance from
InterviewTextInputinto the Room composer, firing on a randomized 10–15s idle and dismissing on any keystroke - Give the idle affordance a Mira-specific provider drawing from the open segment's
openQuestionsrather thanSparkExampleBank - Add
.lineSpacing(4)to assistant turn bodies inMiraConversationView, which Ideation's V2 bubbles omit and which materially affects long-form readability - Replace the three hardcoded
Color.whitevalues inherited fromDeepDiveConversationViewwithcmBgPrimary— NOT APPLICABLE: the fork never inherited them; the only.whiteinMiraConversationViewis badge text on a filled capsule, which is correct
Phase 6 — Whisper reassignment
10 tasks 9 of 10 complete- Delete
WorkflowTransitionTriggerfromMiraWhisperCoordinator's registered set; the workflow pipeline stepper already owns stage transitions - Delete
PlanIdleTriggerfrom the registered set and route its signal into the session digest instead - Delete
RepeatedConcernTriggeroutright — counting file repeats flags whichever file you are actively working in - Keep
ContradictionTriggeras the only live interrupt - Add
MiraWhisperBudgettoMiraWhisperCoordinator: a global cap of two surfaced whispers per rolling seven days across all triggers, persisted besideWhisperSuppressionStore - Enforce the budget in the coordinator before any trigger evaluates, so per-trigger cooldowns stop being the only bound
- Add a "Stop telling me this" action to the whisper card that writes a permanent suppression for the trigger id with no scope, mirroring the editorial layer's "Silence this"
- Remove the 90-second auto-tuck; the card persists until dismissed or engaged
- Surface retired-trigger signals in
MiraLedPaneViewunder the existing criticality bands rather than dropping them — NOT DONE: the three retired signals are currently dropped, not rehomed. Workflow transitions remain visible in the pipeline stepper; plan idleness is not surfaced anywhere right now - Delete the
/cr-entryand/cr-completecommand buttons fromMiraWhisperCardalong withworkflowCommandAction, since the trigger that showed them is gone
Phase 7 — Wire the three ready cards
7 tasks 6 of 7 complete- Think out loud with me — opens a
.brainstormsegment, injects a framing preamble asking Mira to open with an observation rather than a question, appends her opening as the segment's first turn - Put a room together — opens a
.meetingsegment, asks Mira to propose a roster from the tenPreComposedGroupcards plus a topic, renders her proposal as a turn with a Convene action - Convene from that turn presents
StandupSetupSheetprefilled with Mira's proposedagentIdsand topic, reusing the existingMeetingSetupPayloadpath - On meeting conclusion, close the segment with
artifact: .meeting(id)and append Mira's synthesis as anisUnpromptedturn — NOT DONE:MiraMeetingHandoffcarries thesegmentIdacross, but nothing consumes it at conclusion yet, so a convened meeting leaves its segment open and unlinked - Pick up where we left off — resumes a parked segment through
MiraSegmentStore.resume(_:), appending the recap as an unprompted turn, replacing the currentresumeParkedTopiccall path - Add
buildSegmentOpeningPrompt(kind:projectId:)toMiraPromptLibraryproducing the per-card framing preamble - Close the open segment automatically when a different card is chosen, drafting a recap via the existing
draftParkedTopicextraction
Phase 8 — Build and verify
4 tasks 4 of 4 completexcodebuild -scheme "Code Raven" -destination 'platform=macOS' buildreturns successscripts/verify-build-product.shconfirms a fresh binary, sinceBUILD SUCCEEDEDalone is not a witness- Grep for every deleted symbol (
MiraFloatingWrapper,PanelContext,dockedWidth,RepeatedConcernTrigger) and confirm no references remain - Confirm
MiraSegmentMigrationruns once against a real project and that existing parked topics survive with their recaps intact — UNVERIFIED at runtime; compiles and is called from both the Room'sonAppearand the park sheet's save
Phase 9 — Review-pass fixes
9 tasks 9 of 9 complete- Shrink the Room header from an 88px avatar to 40px — at real window size it reserved a band of vertical space for identity already known
- Rename the header's "Set aside" to "Shelf" with a tooltip naming what it holds; the old label read as a verb with no object
- Stop the card deck and the transcript competing for the column — the deck owns it when expanded, collapses to a one-line bar once a conversation exists, and auto-collapses when a card is chosen
- Label the composer's
@and bookmark buttons ("Ask an expert", "Shelve this") with tooltips explaining what each does - Fix
deferralCount: it incremented onfirstTriagedSession < sessionalone, which counts app launches rather than deferrals, and>= 4force-escalates to.criticaland pins.persistedCriticalforever - Add
MiraTriageItem.lastCountedProducedAtso a deferral requires an agent to have actually re-reported the finding - Add
MiraDeferralRepairto zero out counts already inflated past the escalation threshold - Add dedup to
MiraDraftPlanStore.add— it inserted unconditionally while both finding stores dedup onstableKey, so two fixture invocations produced two indistinguishable records - Surface
MiraDraftPlanStore.delete, which had existed with zero call sites, as a right-click action on the draft row
Phase 10 — Handoff reports
18 tasks 17 of 18 complete- Define
MiraHandoff— title, purpose, outcome, confidence + reason, benefits, risks, actions, source finding keys, sent state - Define
MiraHandoffActionwithdetailfor drill-down,sourceFindingKeysfor provenance, andisIncludedso items can be trimmed before sending - Define
MiraHandoffReportandMiraHandoffSource(.backlog/.meeting), all with defensive decoders - Create
MiraHandoffStore— per-project JSON at~/Library/Application Support/Code Raven/Mira/handoffs/{projectId}.json, reports accumulate rather than replace - Create
MiraHandoffGeneratorwithgenerateFromBacklogandgenerateFromMeeting, both fresh one-shot sessions rather than resuming Mira's thread - Add
buildBacklogHandoffPromptandbuildMeetingHandoffPrompttoMiraPromptLibrary, sharing one handoff contract - Cap the meeting prompt at the last 24 messages so a long meeting cannot blow the context
- Build
MiraHandoffReportView— per-handoff card with confidence badge, gains/costs columns, and a checkable action list - Drill-down on an action reveals its
detailplus the original agent text of its source findings - Send routes through
ScratchNote→NotePlanningPromptBuilder→CopyPromptPreviewSheet, reusing the CR-NOTE plan-capture pipeline so a sent handoff becomes a tracked plan - Excluded actions are dropped from the composed prompt rather than listed as skipped
- Add the backlog entry point to
MiraLedPaneView, above the findings list - Add "Ask Mira for handoffs" to
MeetingHandoffSheetso any concluded meeting can produce a report - Add a per-handoff
notesfield, edited inline and bundled into the sent prompt above the checklist so it reads as constraint rather than footnote - Commit note drafts on focus loss rather than per keystroke, and flush an uncommitted draft before sending
- Surface every report holding unsent handoffs, not only the newest — showing
latestReportalone made an older report unreachable the moment a new one was generated - Keep fully-sent reports reachable behind a "finished reports" disclosure, with a right-click delete on any row
- Verify a generated report at runtime — the generator, its JSON contract, and the send path have never been exercised against a live model
Phase 11 — Coherence, voice, and pane hierarchy
13 tasks 11 of 13 complete- Retire a sent handoff's source findings so the backlog shrinks as you act — without it, regenerating re-clusters work already sent
- Add
retireItemIntoHandoffrather than reusingacceptItem, which mints one Mira-led task per finding and would recreate the pile-up handoffs exist to prevent - Add an uncovered-findings signal to the generate button so the app says when a new report is worth making, rather than leaving it to judgment
- Give Mira awareness of her own handoffs via
handoffContextSummary, injected beside the triage summary - Give Mira awareness of her own segments via
segmentContextSummary, so she knows which conversation she is inside and what is set aside - Invert the Mira-led pane: handoff reports lead, raw findings collapse behind a disclosure once anything has been grouped
- Keep the raw findings leading when no report exists yet, since then they are the only thing to act on
- Add
buildWhisperVoicePromptwiring the long-unusedwhisperVoiceOverlay/interruptVoiceOverlayregisters - Add
MiraWhisperVoicer— a one-turn pass that rewrites a trigger's template body in Mira's voice, with a 25s timeout and fallback to the template - Sanitize voiced output: reject empty, over-length, fenced, or bulleted responses that would read worse than the template
- Charge the whisper budget at detection rather than at surfacing, so a voicing failure cannot retry past the cap
- Register
miraas a subsystem in CodeRaven's Blueprints view so a blueprint can be generated — BLOCKED:/cr-blueprintvalidates against the project's stored subsystem list andmirais absent, so generating now would fail on import - Generate the Mira blueprint once the subsystem is registered
Phase 12 — The three deferred cards
18 tasks 16 of 18 complete- Tell me what you'd do next —
MiraDirectionGeneratorproduces a realMiraPlanDraftwith decision forks, giving that review flow its first producer since the DEBUG fixture - Validate forks on parse: choice kinds need two or more options, freeText must carry none, and a fork violating either is dropped rather than made unanswerable
- Resolve
recommendedOptionLabelto an option id on this side, so the model never has to invent UUIDs - Route the direction card into
MiraDraftReviewSheetwith send / send-with-concerns / reject / discuss wired toMiraDraftCoordinator - Add a per-tile busy state to the card deck so a card taking half a minute is not a click that appears to do nothing
- Run it without me —
MiraDelegatedMeetingRunnerstarts a standup, lets it run unattended, concludes it, and posts Mira's account back as an unprompted turn - Validate the roster is 2–4 agents from the live project before
startMeeting, sinceStandupMeeting.initenforces that with apreconditionthat crashes the app - Guard against clobbering an in-progress meeting —
startMeetingoverwritesactiveMeetingunconditionally and only one meeting exists globally - Poll for the loop's terminal state rather than observing
isGenerating, whose early-return paths leave it true, with a 90-minute wall-clock backstop - Restrict delegation to
.standup—.consultationstops after one turn by design and.viewEvaluationblocks on a human advancing views - Generate a handoff report from the concluded meeting so the result is work to act on rather than a transcript to read
- Show me what's drifted — repair
FoundationStalenessDetectorfirst: per-layer timestamps from each layer's own file rather than one manager-widelastSavedAt - Add file patterns for
.visualDesign,.cognitive, and.presence, which had none and so could never be flagged - Drop the three-changed-file floor that made a single important change invisible
- Return not-stale for a Foundation with no generated layers, instead of nine critical layers at 999 days
- Add
MiraDriftScannerranking blueprint fingerprint drift above time decay, and feed the result into her opening so she leads with what matters rather than listing everything - Verify the delegated meeting end to end — it has never run unattended against a live model
- Verify a generated direction draft promotes cleanly through
MiraDraftCoordinator.send
Phase 13 — Polish pass
11 tasks 9 of 11 complete- Enhance the generated
custom-mirablueprint: correct the finding-provenance error (findings come from agent runs viaFindingsIngestor, never fromMeetingFlagBridge), drop the removedMiraPanelManager.open/toggleentry points, and add fourteen gotchas earned this session - Correct
expert-meetings.md, which warned about aconcludePausedMeetingstate swap removed in commitc059088— the same stale claim I repeated as fact earlier - Add the real meeting hazards to that blueprint: the 2–4 agent
precondition,startMeeting's unconditional clobber, and the absent completion signal - Correct
foundation-layer.md, whose four documentedFoundationStalenessDetectordefects are now all fixed - Surface service errors in the Room —
MiraService,MiraDirectionGenerator,MiraDelegatedMeetingRunnerandMiraHandoffGeneratorall seterrorand nothing rendered any of them, so a failed turn looked identical to nothing happening - Add a back affordance to the Room (⌘[) — it is a full-window destination with no chrome, so the sidebar was the only way out
- Add
MiraQuietSignalsand a "Sitting quietly" section, rehoming the plan-idle and ready-to-close signals the retired whisper triggers used to carry - Close the segment when convening a meeting, so a thread does not keep landing turns inside a segment named "Convening" forever
- Fix the resume card reading
MiraParkedTopicStorewhile the list beneath it readsMiraSegmentStore— it now resumes the newest parked segment - Reconcile the header's Shelf button, which still browses parked topics rather than segments — two stores showing the same migrated records
- Link a convened meeting back to its segment's artifact;
MiraMeetingHandoffcarries thesegmentIdbut nothing consumes it at conclusion
Manual Testing
20 tasks yours, in the running app- Open a project and press ⌘⇧M — the Room opens full-window rather than a panel sliding inuser
- Confirm the ambient anchor and status bar still render on every other viewuser
- Click Think out loud with me and confirm a segment divider appears and Mira opens with an observationuser
- Click a second card and confirm the first segment closes with a recap and collapsesuser
- Confirm parked topics from before the update appear under "Pick up where we left off"user
- Leave the composer idle for fifteen seconds and confirm the suggestion fades inuser
- Confirm no whisper appears for a plan whose implementation tasks just hit 100%user
- Click "Turn these into handoffs" and confirm Mira returns three to six coherent groups rather than a restatement of the listuser
- Uncheck an action, send the handoff, and confirm the excluded item is absent from the promptuser
- Confirm a sent handoff creates a Scratchpad note tagged handoff/mira and that the resulting plan links back to ituser
- Confirm the triage list comes back around 3 critical / 17 standard / 4 ambient after the deferral repair runsuser
- Ask Mira "what are we working on" and confirm she names her handoffs, not just raw findingsuser
- Send a handoff and confirm its findings leave the triage list and the generate button's count dropsuser
- Confirm the findings list arrives collapsed once a report exists, and expands on clickuser
- Click Run it without me and confirm the tile reports convening → talking → wrapping up, and that Mira posts her account when it landsuser
- Click Show me what's drifted and confirm she names one specific stale document rather than listing everythinguser
- Confirm a brand-new project reports no Foundation drift rather than nine critical layersuser
- Press ⌘[ in the Room and confirm it returns to the project overviewuser
- Confirm a failed card surfaces a dismissible error banner rather than failing silentlyuser
- Confirm "Sitting quietly" lists any plan past 70% that has gone two days without a checkinuser
Risks & edge cases
The live meeting transcript is not render-optimized
StandupMeetingView's live message list is not Equatable-optimized; only the
concluded MeetingTranscriptView is. Every state change re-renders every bubble.
Pass 1 keeps meetings in their existing view, so this does not bite yet. It becomes the first
problem the moment the Room hosts a live meeting inline.
Segment boundaries when you keep typing
A card opens a segment, but nothing stops the conversation from wandering past whatever the
card was for. Pass 1 closes a segment only when another card is chosen. A conversation that
drifts for a week lives in one enormous .brainstorm segment, which is no worse than
today's single stream but is not the intended shape.
The migration is one-way
MiraSegmentMigration converts parked topics into segments and the parked-topic
store stops being written. Rolling back to a prior build leaves topics created after the
migration invisible. The topics themselves are not deleted, so the data survives; only the new
ones are unreachable from the old UI.
The whisper budget can silence a real contradiction
A global two-per-week cap means a third genuine contradiction in one week does not surface. With three of four triggers retired, contradiction is the only consumer of the budget, so exhausting it requires three real reversals in seven days. The mitigation is that suppressed whispers still land in the Mira-led pane.
Foundation staleness detection cannot be trusted
foundation-layer.md:184-187 documents that FoundationStalenessDetector
uses one manager-wide lastSavedAt for all nine layers, maps no file patterns to
.visualDesign / .cognitive / .presence, requires three
changed files so a single important change never fires, and reports a brand-new project as
all-nine-stale at 999 days old. This is exactly why Show me what's drifted is deferred
rather than shipped disabled-in-name-only.
Out of scope
Run it without me — the faceless meeting
Roughly 80% built already: the standup loop is autonomous between user messages, closing the
meeting view does not cancel conversationTask, and concludePausedMeeting()
produces the full artifact set with no view mounted. Missing are a one-shot start→run→conclude
entry point and a landing place for the result. Deferred because
concludePausedMeeting() temporarily mutates activeMeeting,
meetingAgents and projectPath before restoring them, and a card whose
whole premise is that you walk away makes concurrent meetings the normal case rather than the
rare one. That needs its own concurrency pass.
Show me what's drifted
BlueprintStalenessDetector is solid — SHA fingerprint drift, real changed-file
lists, human-readable recommendation strings. Foundation staleness is not, per the risk above.
Shipping a cross-subsystem view on half-trustworthy signal produces a card that lies on day one.
Tell me what you'd do next
This is the missing producer for machinery that already shipped:
MiraPlanDraft and the whole MiraDraftCoordinator
send / send-with-concerns / discuss / reject flow exist, and the only thing that ever creates a
draft is MiraDraftPlanFixture in the DEBUG menu. Worth doing properly rather than
quickly.
LLM-generated whisper copy (the never-shipped Phase 5)
All four whisper bodies carry a comment promising copy generated through the Whisper voice
register. The overlays exist in MiraPromptLibrary and no trigger uses them. With
only contradiction surviving, one template is tolerable for a night. It is the first thing to
fix in Pass 2.
Cross-project Mira, avatar animation, and voice interaction
Already deferred to v3+ by the existing v2 plan. Nothing here changes that.
Final review
What this does well. It removes a container that was never the intended one
and restores the design the code still documents in its own header comment. The segment model is
not invented; it generalizes a concept MiraParkedTopic already describes, which
means the migration is a widening rather than a replacement. Retiring three whisper triggers is
mostly deletion, and deletion is the cheapest reliable improvement available here.
Where it is weakest. Phase 4 is a wide deletion across
MiraPanelHost, MiraPanelManager, MiraContainerView and
MiraAmbientAnchor, and those four files are mutually referential. If anything in
this plan overruns, it is that phase. Phase 5's port of the idle-suggestion affordance depends on
InterviewTextInput, which hardwires SparkCategory,
SparkExampleBank and ScratchpadManager; generalizing it properly wants
a provider protocol that this plan only gestures at.
What I would adjust if pushed. Phases 1, 2 and 3 could ship alone and would already deliver the change that matters most — Mira as a place you go, with a door that offers you something. If the night runs short, Phase 4 is the one to split, keeping the panel alive alongside the Room until the deletion can be done carefully.
High on the diagnosis, because the code states the intended design in its own comments and the empty friction log corroborates it. High on the segment model, because the constraint that decides it is concrete — one cliSessionId carrying months of accumulated project context — and the target shape is already written down in MiraParkedTopic. The missing 17 is mostly Phase 4's blast radius across four mutually referential files, and the open question of when a segment should close on its own rather than waiting for the next card.