From 65bf42c4dbf96e5943e51b0ffff80b01360cad26 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 25 Apr 2026 02:36:39 +0000 Subject: [PATCH 1/2] feat(symbolic-moment): add person picker for Symbolic Moment with staging Surfaces an explicit target picker when the user kicks off a Symbolic Moment with one or more staged counterparts, mirroring the Solo Mirror flow. Adds a `pendingSymbolicMomentResolver` state and a `runSymbolicMomentLane` callback that accepts `profileOverride`/`forcePrimaryScope` so each picker option can route the read at the correct anchor profile. Also offers Map Us, Stage From Vault, and Dismiss escape hatches so people-selection from vault or after staging is now obvious instead of being limited to the relational read lane. https://claude.ai/code/session_01WiNwPjtGzyNaAMab6srzqy --- vessel/src/app/page.tsx | 145 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 11 deletions(-) diff --git a/vessel/src/app/page.tsx b/vessel/src/app/page.tsx index fd3813f2..73afffcc 100644 --- a/vessel/src/app/page.tsx +++ b/vessel/src/app/page.tsx @@ -579,11 +579,19 @@ export default function App() { const [pendingSoloMirrorResolver, setPendingSoloMirrorResolver] = useState<{ desc: string; } | null>(null); + const [pendingSymbolicMomentResolver, setPendingSymbolicMomentResolver] = useState<{ + desc: string; + } | null>(null); useEffect(() => { if (!pendingSoloMirrorResolver) return; if (observerStagedProfiles.length > 0) return; setPendingSoloMirrorResolver(null); }, [observerStagedProfiles.length, pendingSoloMirrorResolver]); + useEffect(() => { + if (!pendingSymbolicMomentResolver) return; + if (observerStagedProfiles.length > 0) return; + setPendingSymbolicMomentResolver(null); + }, [observerStagedProfiles.length, pendingSymbolicMomentResolver]); useEffect(() => { if (observerStagedProfiles.length > 0) return; if (!primaryOnlyScope) return; @@ -1789,6 +1797,7 @@ export default function App() { setPrimaryOnlyScope(false); setStagedContexts([]); setPendingSoloMirrorResolver(null); + setPendingSymbolicMomentResolver(null); advancedClockSuggestionShownForSessionRef.current = null; setAdvancedClocksOpen(false); setLastDebugInfo(null); @@ -1841,6 +1850,7 @@ export default function App() { ); setPendingSemanticDepth('plain'); setPendingSoloMirrorResolver(null); + setPendingSymbolicMomentResolver(null); setIsFlightRecorderOpen(false); setReadLaneArmed(snapshot.structuredReadingOptIn === true && snapshot.promptMode !== 'CHAT'); }); @@ -2557,6 +2567,7 @@ export default function App() { const handleHeaderModeSelect = useCallback((mode: 'CHAT' | 'SOLO_MIRROR' | 'FIELD_REPORT') => { setPendingSoloMirrorResolver(null); + setPendingSymbolicMomentResolver(null); if (mode === 'CHAT') { setStructuredReadingOptIn(false); setActiveMode('CHAT'); @@ -2577,6 +2588,9 @@ export default function App() { if (intent !== 'CHECK_BONES' && pendingSoloMirrorResolver) { setPendingSoloMirrorResolver(null); } + if (intent !== 'SYMBOLIC_MOMENT' && pendingSymbolicMomentResolver) { + setPendingSymbolicMomentResolver(null); + } if (structuredReadLocked && intentRequiresArchitect) { const requestedMode = resolveGuidedEntryTargetMode(intent); @@ -2693,18 +2707,13 @@ export default function App() { return; } if (intent === 'SYMBOLIC_MOMENT') { - const structuredFieldReport = true; setRelationshipMappingActive(false); - enterReadMode('FIELD_REPORT', structuredFieldReport); - if (!requestReadArm('guided_entry_symbolic_moments')) { + const symbolicDesc = safeDesc || 'Read my symbolic moment(s).'; + if (observerStagedProfiles.length > 0 && !soloScopedProfile) { + setPendingSymbolicMomentResolver({ desc: symbolicDesc }); return; } - armCorridorLoadingMode(safeDesc || 'Read my symbolic moment(s).'); - maybePrimeAlignmentCorridor('guided_entry', { targetMode: 'FIELD_REPORT' }); - queueGuidedSend(safeDesc || 'Read my symbolic moment(s).', { - targetModeOverride: 'FIELD_REPORT', - structuredReadingOptInOverride: structuredFieldReport, - }); + runSymbolicMomentLane(symbolicDesc, { source: 'guided_entry_symbolic_moments' }); return; } if (intent === 'SET_LOCATION') { @@ -2759,7 +2768,7 @@ export default function App() { structuredReadingOptInOverride: true, }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [activeMode, activeProfile, appendClientRuntimeEvent, appendConsoleNotice, appendStructuredUpgradeGateNotice, armCorridorLoadingMode, clearCorridorPrimeTimers, observerStagedProfiles, observerStagedProfiles.length, pendingSoloMirrorResolver, primeAlignmentCorridor, quickScopeCounterpart?.name, requestReadArm, soloScopedProfile, stagedContexts.length, structuredReadLocked, structuredReadingOptIn]); + }, [activeMode, activeProfile, appendClientRuntimeEvent, appendConsoleNotice, appendStructuredUpgradeGateNotice, armCorridorLoadingMode, clearCorridorPrimeTimers, observerStagedProfiles, observerStagedProfiles.length, pendingSoloMirrorResolver, pendingSymbolicMomentResolver, primeAlignmentCorridor, quickScopeCounterpart?.name, requestReadArm, soloScopedProfile, stagedContexts.length, structuredReadLocked, structuredReadingOptIn]); const sendScopedMessage = useCallback(( text: string, @@ -2973,9 +2982,51 @@ export default function App() { targetModeOverride: 'SOLO_MIRROR', structuredReadingOptInOverride: structuredSoloMirror, }).catch(() => { }); - + }, [armCorridorLoadingMode, primeAlignmentCorridor, requestReadArm, sendMessage, sendScopedMessage, structuredReadLocked]); + const runSymbolicMomentLane = useCallback(( + desc: string, + options?: { + forcePrimaryScope?: boolean; + profileOverride?: VaultProfile | null; + source?: string; + }, + ) => { + const structuredFieldReport = true; + setRelationshipMappingActive(false); + chatInputRef.current?.setText?.(''); + const safeDesc = typeof desc === 'string' && desc.trim().length > 0 ? desc : 'Read my symbolic moment(s).'; + setStructuredReadingOptIn(structuredFieldReport); + setActiveMode('FIELD_REPORT'); + if (!requestReadArm(options?.source || 'guided_entry_symbolic_moments')) { + return; + } + armCorridorLoadingMode(safeDesc); + maybePrimeAlignmentCorridor('guided_entry', { targetMode: 'FIELD_REPORT' }); + if (options?.profileOverride) { + void sendMessage(safeDesc, { + profileOverride: options.profileOverride, + stagedContextsOverride: [], + targetModeOverride: 'FIELD_REPORT', + structuredReadingOptInOverride: structuredFieldReport, + }).catch(() => { }); + return; + } + if (options?.forcePrimaryScope) { + void sendMessage(safeDesc, { + stagedContextsOverride: [], + targetModeOverride: 'FIELD_REPORT', + structuredReadingOptInOverride: structuredFieldReport, + }).catch(() => { }); + return; + } + void sendScopedMessage(safeDesc, { + targetModeOverride: 'FIELD_REPORT', + structuredReadingOptInOverride: structuredFieldReport, + }).catch(() => { }); + }, [armCorridorLoadingMode, primeAlignmentCorridor, requestReadArm, sendMessage, sendScopedMessage]); + const handleCounterpartSoloMirror = useCallback((profile: VaultProfile, source: string) => { appendClientRuntimeEvent('COUNTERPART_QUICK_ACTION_SELECTED', { action: 'solo_mirror', @@ -4047,6 +4098,78 @@ export default function App() { )} + {pendingSymbolicMomentResolver && observerStagedProfiles.length > 0 && ( +
+

+ Symbolic Moment Target +

+

+ A counterpart is staged. Choose whose chart should anchor the symbolic moment, or map both together. +

+
+ + {observerStagedProfiles.map((profile) => ( + + ))} + + + +
+
+ )} + {(() => { const inlineStagePersonControl = ( + )} + {observerStagedProfiles.length > 0 && ( + + )}