Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
190 changes: 179 additions & 11 deletions vessel/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@
return 'Voice failed this turn.';
}

export default function App() {

Check failure on line 488 in vessel/src/app/page.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 280 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=DHCross_Shipyard&issues=AZ3LeQ7mvH2tZB9B3SEN&open=AZ3LeQ7mvH2tZB9B3SEN&pullRequest=457
const router = useRouter();
const { tier } = useTier();
const [user, setUser] = useState<User | null>(null);
Expand Down Expand Up @@ -579,11 +579,19 @@
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;
Expand Down Expand Up @@ -1789,6 +1797,7 @@
setPrimaryOnlyScope(false);
setStagedContexts([]);
setPendingSoloMirrorResolver(null);
setPendingSymbolicMomentResolver(null);
advancedClockSuggestionShownForSessionRef.current = null;
setAdvancedClocksOpen(false);
setLastDebugInfo(null);
Expand Down Expand Up @@ -1841,6 +1850,7 @@
);
setPendingSemanticDepth('plain');
setPendingSoloMirrorResolver(null);
setPendingSymbolicMomentResolver(null);
setIsFlightRecorderOpen(false);
setReadLaneArmed(snapshot.structuredReadingOptIn === true && snapshot.promptMode !== 'CHAT');
});
Expand Down Expand Up @@ -2557,6 +2567,7 @@

const handleHeaderModeSelect = useCallback((mode: 'CHAT' | 'SOLO_MIRROR' | 'FIELD_REPORT') => {
setPendingSoloMirrorResolver(null);
setPendingSymbolicMomentResolver(null);
if (mode === 'CHAT') {
setStructuredReadingOptIn(false);
setActiveMode('CHAT');
Expand All @@ -2570,13 +2581,16 @@
appendClientRuntimeEvent('HEADER_MODE_SELECTED', { mode, structured: !structuredReadLocked });
}, [appendClientRuntimeEvent, structuredReadLocked]);

const handleGuidedEntry = useCallback((label: string, desc: string) => {

Check failure on line 2584 in vessel/src/app/page.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 40 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=DHCross_Shipyard&issues=AZ3LeQ7mvH2tZB9B3SEO&open=AZ3LeQ7mvH2tZB9B3SEO&pullRequest=457
const intent = resolveGuidedEntryIntent(label);
const safeDesc = typeof desc === 'string' && desc.trim().length > 0 ? desc : label;
const intentRequiresArchitect = guidedEntryRequiresArchitect(intent);
if (intent !== 'CHECK_BONES' && pendingSoloMirrorResolver) {
setPendingSoloMirrorResolver(null);
}
if (intent !== 'SYMBOLIC_MOMENT' && pendingSymbolicMomentResolver) {
setPendingSymbolicMomentResolver(null);
}

if (structuredReadLocked && intentRequiresArchitect) {
const requestedMode = resolveGuidedEntryTargetMode(intent);
Expand Down Expand Up @@ -2693,18 +2707,13 @@
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') {
Expand Down Expand Up @@ -2759,7 +2768,7 @@
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,
Expand Down Expand Up @@ -2973,9 +2982,60 @@
targetModeOverride: 'SOLO_MIRROR',
structuredReadingOptInOverride: structuredSoloMirror,
}).catch(() => { });

}, [armCorridorLoadingMode, primeAlignmentCorridor, requestReadArm, sendMessage, sendScopedMessage, structuredReadLocked]);

const runSymbolicMomentLane = useCallback((
desc: string,
options?: {
forcePrimaryScope?: boolean;
stagedOnlyScope?: 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;
}
if (options?.stagedOnlyScope) {
void sendMessage(safeDesc, {
stagedContextsOverride: observerStagedProfiles.map((p) => ({ profile: p, role: 'OBSERVER' as const })),
targetModeOverride: 'FIELD_REPORT',
structuredReadingOptInOverride: structuredFieldReport,
}).catch(() => { });
return;
}
void sendScopedMessage(safeDesc, {

Check failure on line 3033 in vessel/src/app/page.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of the "void" operator.

See more on https://sonarcloud.io/project/issues?id=DHCross_Shipyard&issues=AZ3LeQ7mvH2tZB9B3SEP&open=AZ3LeQ7mvH2tZB9B3SEP&pullRequest=457
targetModeOverride: 'FIELD_REPORT',
structuredReadingOptInOverride: structuredFieldReport,
}).catch(() => { });
}, [armCorridorLoadingMode, observerStagedProfiles, primeAlignmentCorridor, requestReadArm, sendMessage, sendScopedMessage]);
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runSymbolicMomentLane uses maybePrimeAlignmentCorridor(...), but the useCallback deps don’t include maybePrimeAlignmentCorridor (and include primeAlignmentCorridor, which isn’t referenced). This risks a stale closure if maybePrimeAlignmentCorridor changes. Include maybePrimeAlignmentCorridor in the deps array and drop unused deps.

Suggested change
}, [armCorridorLoadingMode, observerStagedProfiles, primeAlignmentCorridor, requestReadArm, sendMessage, sendScopedMessage]);
}, [armCorridorLoadingMode, maybePrimeAlignmentCorridor, observerStagedProfiles, requestReadArm, sendMessage, sendScopedMessage]);

Copilot uses AI. Check for mistakes.

const handleCounterpartSoloMirror = useCallback((profile: VaultProfile, source: string) => {
appendClientRuntimeEvent('COUNTERPART_QUICK_ACTION_SELECTED', {
action: 'solo_mirror',
Expand Down Expand Up @@ -4047,6 +4107,114 @@
</div>
)}

{pendingSymbolicMomentResolver && observerStagedProfiles.length > 0 && (
<div className="mb-2 rounded-xl border border-amber-500/30 bg-amber-950/20 p-3">
<p className="text-[10px] font-mono uppercase tracking-[0.15em] text-amber-200">
Symbolic Moment Target
</p>
<p className="mt-1 text-xs text-slate-200 leading-relaxed">
{observerStagedProfiles.length === 1
? `${observerStagedProfiles[0].name} is staged. Choose who or what to read in this moment.`
: `${observerStagedProfiles.length} profiles staged. Choose the focus or read them together.`}
</p>
<div className="mt-2 flex flex-wrap items-center gap-2">
<button
type="button"
onClick={() => {
setPendingSymbolicMomentResolver(null);
setSoloFocusProfileId(null);
runSymbolicMomentLane(
pendingSymbolicMomentResolver.desc,
{ forcePrimaryScope: true, source: 'guided_entry_symbolic_moments_primary' },
);
}}
className="px-3 py-1.5 rounded-md border border-emerald-400/40 bg-emerald-500/20 text-emerald-100 text-[10px] font-mono uppercase tracking-wider hover:bg-emerald-500/30 transition-colors"
>
Read For Me {activeProfile?.name ? `(${activeProfile.name})` : ''}
</button>
{observerStagedProfiles.map((profile) => (
<button
key={`symbolic-moment-target-${profile.id}`}
type="button"
onClick={() => {
setPendingSymbolicMomentResolver(null);
setSoloFocusProfileId(profile.id);
appendConsoleNotice(
`Solo focus is now set to ${profile.name}. Upcoming turns will read ${profile.name}'s chart unless you tap Restore Me.`,
);
runSymbolicMomentLane(
pendingSymbolicMomentResolver.desc,
{ profileOverride: profile, source: `guided_entry_symbolic_moments_${profile.id}` },
);
}}
className="px-3 py-1.5 rounded-md border border-cyan-400/40 bg-cyan-500/20 text-cyan-100 text-[10px] font-mono uppercase tracking-wider hover:bg-cyan-500/30 transition-colors"
>
Symbolic Moment: {profile.name}
</button>
))}
{observerStagedProfiles.length > 1 && (
<button
type="button"
onClick={() => {
setPendingSymbolicMomentResolver(null);
setSoloFocusProfileId(null);
runSymbolicMomentLane(
pendingSymbolicMomentResolver.desc,
{ stagedOnlyScope: true, source: 'guided_entry_symbolic_moments_all_staged' },
);
}}
className="px-3 py-1.5 rounded-md border border-indigo-400/40 bg-indigo-500/20 text-indigo-100 text-[10px] font-mono uppercase tracking-wider hover:bg-indigo-500/30 transition-colors"
title={`Read symbolic moment for: ${observerStagedProfiles.map((p) => p.name).join(', ')}`}
>
Read All Staged ({observerStagedProfiles.length})
</button>
)}
{observerStagedProfiles.length > 0 && (
<button
type="button"
onClick={() => {
setPendingSymbolicMomentResolver(null);
setSoloFocusProfileId(null);
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “All In Scope” action clears soloFocusProfileId but not primaryOnlyScope. If primaryOnlyScope is currently true, runSymbolicMomentLane(...) will fall through to sendScopedMessage(...), which will still force a primary-only request and ignore staged profiles. Clear primaryOnlyScope (or otherwise force shared/map scope) before launching this lane so “All In Scope” actually includes staged profiles.

Suggested change
setSoloFocusProfileId(null);
setSoloFocusProfileId(null);
setPrimaryOnlyScope(false);

Copilot uses AI. Check for mistakes.
runSymbolicMomentLane(
pendingSymbolicMomentResolver.desc,
{ source: 'guided_entry_symbolic_moments_all_in_scope' },
);
}}
className="px-3 py-1.5 rounded-md border border-sky-400/40 bg-sky-500/20 text-sky-100 text-[10px] font-mono uppercase tracking-wider hover:bg-sky-500/30 transition-colors"
title="Read the symbolic moment for everyone in scope (you + all staged profiles)"
>
All In Scope ({observerStagedProfiles.length + 1})
</button>
)}
<button
type="button"
onClick={() => {
setPendingSymbolicMomentResolver(null);
setSoloFocusProfileId(null);
handleGuidedEntry(GUIDED_ENTRY_LABELS.MAP_US, 'Map us across the staged counterpart.');
}}
className="px-3 py-1.5 rounded-md border border-violet-400/40 bg-violet-500/20 text-violet-100 text-[10px] font-mono uppercase tracking-wider hover:bg-violet-500/30 transition-colors"
>
{GUIDED_ENTRY_LABELS.MAP_US}
</button>
<button
type="button"
onClick={openVaultPanel}
className="px-3 py-1.5 rounded-md border border-slate-500/40 bg-slate-700/30 text-slate-200 text-[10px] font-mono uppercase tracking-wider hover:bg-slate-700/50 transition-colors"
>
Stage From Vault
</button>
<button
type="button"
onClick={() => setPendingSymbolicMomentResolver(null)}
className="px-2.5 py-1.5 rounded-md border border-transparent text-[10px] font-mono uppercase tracking-wider text-slate-500 hover:text-slate-300"
>
Dismiss
</button>
</div>
</div>
)}

{(() => {
const inlineStagePersonControl = (
<button
Expand Down