import { afterEach, describe, expect, it, vi } from 'vitest'; interface FakeEntry { type: string; customType?: string; data?: unknown; } interface SentMessage { message: { customType: string; content: string; display: boolean; }; options?: { triggerTurn?: boolean; deliverAs?: 'steer' | 'followUp' | 'nextTurn'; }; } interface FakeContext { cwd: string; ui: { notifications: Array<{ message: string; level?: string }>; statuses: Map; notify(message: string, level?: string): void; setStatus(key: string, value: string | undefined): void; }; sessionManager: { getBranch(): FakeEntry[]; }; isIdle(): boolean; hasPendingMessages(): boolean; abort(): void; } type EventHandler = ( event: Record, context: FakeContext, ) => unknown | Promise; type CommandHandler = (args: string, context: FakeContext) => unknown | Promise; interface FakeToolResult { content: Array<{ type: string; text: string }>; details?: unknown; terminate?: boolean; } interface FakeTool { name: string; execute( toolCallId: string, params: Record, signal: AbortSignal | undefined, onUpdate: undefined, context: FakeContext, ): Promise; } interface GoalExtensionFactory { (api: FakePiApi): void; } interface GoalExtensionModule { default: GoalExtensionFactory; } interface FakePiApi { on(event: string, handler: EventHandler): void; registerCommand(name: string, options: { description: string; handler: CommandHandler }): void; registerTool(tool: FakeTool): void; appendEntry(customType: string, data?: unknown): void; sendMessage(message: SentMessage['message'], options?: SentMessage['options']): void; } function isGoalExtensionModule(value: unknown): value is GoalExtensionModule { if (typeof value !== 'object' || value === null) return false; return typeof Reflect.get(value, 'default') === 'function'; } const goalExtensionUrl = new URL('../../framework/runtime/pi/goal-extension.ts', import.meta.url) .href; const importedGoalExtension: unknown = await import(goalExtensionUrl); if (!isGoalExtensionModule(importedGoalExtension)) { throw new Error('Pi goal extension must export a default registration function'); } const registerGoalExtension = importedGoalExtension.default; function isRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value); } class FakePi { readonly handlers = new Map(); readonly commands = new Map(); readonly tools = new Map(); readonly entries: FakeEntry[] = []; readonly sentMessages: SentMessage[] = []; readonly notifications: Array<{ message: string; level?: string }> = []; readonly statuses = new Map(); branch: FakeEntry[] = []; idle = true; pending = false; abortCount = 0; readonly context: FakeContext = { cwd: '/tmp/project', ui: { notifications: this.notifications, statuses: this.statuses, notify: (message: string, level?: string): void => { this.notifications.push({ message, level }); }, setStatus: (key: string, value: string | undefined): void => { this.statuses.set(key, value); }, }, sessionManager: { getBranch: (): FakeEntry[] => [...this.branch], }, isIdle: (): boolean => this.idle, hasPendingMessages: (): boolean => this.pending, abort: (): void => { this.abortCount += 1; }, }; readonly api: FakePiApi = { on: (event: string, handler: EventHandler): void => { this.handlers.set(event, [...(this.handlers.get(event) ?? []), handler]); }, registerCommand: ( name: string, options: { description: string; handler: CommandHandler }, ): void => { this.commands.set(name, options.handler); }, registerTool: (tool: FakeTool): void => { this.tools.set(tool.name, tool); }, appendEntry: (customType: string, data?: unknown): void => { const entry: FakeEntry = { type: 'custom', customType, data }; this.entries.push(entry); this.branch.push(entry); }, sendMessage: (message: SentMessage['message'], options?: SentMessage['options']): void => { this.sentMessages.push({ message, options }); }, }; constructor(initialBranch: FakeEntry[] = []) { this.branch = [...initialBranch]; registerGoalExtension(this.api); } async emit(event: string, value: Record = {}): Promise { const results: unknown[] = []; for (const handler of this.handlers.get(event) ?? []) { results.push(await handler(value, this.context)); } return results; } async goal(args: string): Promise { const handler = this.commands.get('goal'); if (handler === undefined) throw new Error('/goal was not registered'); await handler(args, this.context); } async report(params: Record): Promise { const tool = this.tools.get('mosaic_goal_report'); if (tool === undefined) throw new Error('mosaic_goal_report was not registered'); return await tool.execute('goal-report-1', params, undefined, undefined, this.context); } } function latestGoalStateData(pi: FakePi): Record { for (let index = pi.entries.length - 1; index >= 0; index -= 1) { const entry = pi.entries[index]; if (entry?.customType === 'mosaic-goal-state' && isRecord(entry.data)) return entry.data; } throw new Error('No persisted Mosaic goal state found'); } function stateField(pi: FakePi, field: string): unknown { return latestGoalStateData(pi)[field]; } function activeGoalStatementFromContext(result: unknown): string { if (!isRecord(result)) throw new Error('Context handler did not return an object'); const messages = result['messages']; if (!Array.isArray(messages)) throw new Error('Context result did not include messages'); const goalMessage = messages.find( (message: unknown): boolean => isRecord(message) && message['customType'] === 'mosaic-goal-context', ); if (!isRecord(goalMessage) || typeof goalMessage['content'] !== 'string') { throw new Error('Goal context message was not injected'); } return goalMessage['content']; } afterEach(() => { vi.useRealTimers(); vi.unstubAllEnvs(); }); describe('Mosaic Pi goal extension commands', () => { it('shows help and handles controls safely when no goal exists', async () => { const pi = new FakePi(); await pi.goal(''); expect(pi.notifications.at(-1)?.message).toContain('/goal set'); await pi.goal('status'); expect(pi.notifications.at(-1)?.message).toContain('No Mosaic goal is set'); for (const command of ['pause', 'resume', 'cancel']) { await pi.goal(command); expect(pi.notifications.at(-1)?.level).toBe('warning'); } expect(pi.entries).toHaveLength(0); expect(pi.sentMessages).toHaveLength(0); }); it('sets, reports, pauses, resumes, and cancels a bounded goal', async () => { const pi = new FakePi(); await pi.goal('set Deliver the local goal extension with tests'); expect(stateField(pi, 'phase')).toBe('active'); expect(stateField(pi, 'statement')).toBe('Deliver the local goal extension with tests'); expect(pi.sentMessages).toHaveLength(1); expect(pi.sentMessages[0]?.options?.triggerTurn).toBe(true); await pi.goal('status'); expect(pi.notifications.at(-1)?.message).toContain('Deliver the local goal extension'); expect(pi.notifications.at(-1)?.message).toContain('active'); await pi.goal('pause'); expect(stateField(pi, 'phase')).toBe('paused'); pi.sentMessages.length = 0; await pi.emit('agent_settled'); expect(pi.sentMessages).toHaveLength(0); await pi.goal('resume'); expect(stateField(pi, 'phase')).toBe('active'); expect(pi.sentMessages).toHaveLength(1); await pi.goal('cancel'); expect(stateField(pi, 'phase')).toBe('cancelled'); pi.sentMessages.length = 0; await pi.emit('agent_settled'); expect(pi.sentMessages).toHaveLength(0); }); it('accepts /goal shorthand but refuses to replace an active goal', async () => { const pi = new FakePi(); await pi.goal('First goal'); const firstGoalId = stateField(pi, 'goalId'); await pi.goal('set Second goal'); expect(stateField(pi, 'goalId')).toBe(firstGoalId); expect(stateField(pi, 'statement')).toBe('First goal'); expect(pi.notifications.at(-1)?.level).toBe('warning'); expect(pi.notifications.at(-1)?.message).toContain('/goal cancel'); }); it('rejects invalid phase transitions, aborts busy work, and supports clear as cancel', async () => { const pi = new FakePi(); await pi.goal('set Preserve transition safety'); await pi.goal('resume'); expect(pi.notifications.at(-1)?.message).toContain('cannot be resumed'); pi.idle = false; await pi.goal('pause maintenance window'); expect(stateField(pi, 'stopReason')).toBe('maintenance window'); expect(pi.abortCount).toBe(1); await pi.goal('pause'); expect(pi.notifications.at(-1)?.message).toContain('cannot be paused'); await pi.goal('clear'); expect(stateField(pi, 'phase')).toBe('cancelled'); expect(pi.abortCount).toBe(2); }); it('rejects empty and oversized goal statements without starting a run', async () => { const pi = new FakePi(); await pi.goal('set'); await pi.goal(`set ${'x'.repeat(8_001)}`); expect(pi.entries).toHaveLength(0); expect(pi.sentMessages).toHaveLength(0); expect(pi.notifications.at(-1)?.level).toBe('warning'); }); }); describe('Mosaic Pi goal lifecycle', () => { it('injects one fresh active contract before every model context', async () => { const pi = new FakePi(); await pi.goal('set Keep the agent oriented'); const existingGoalContext = { role: 'custom', customType: 'mosaic-goal-context', content: 'stale', }; const existingContinuation = { role: 'custom', customType: 'mosaic-goal-continuation', content: 'stale continuation', }; const first = await pi.emit('context', { messages: [existingGoalContext, existingContinuation], }); const second = await pi.emit('context', { messages: [] }); expect(activeGoalStatementFromContext(first[0])).toContain('Keep the agent oriented'); expect(activeGoalStatementFromContext(first[0])).toContain('mosaic_goal_report'); expect(activeGoalStatementFromContext(first[0])).not.toContain('stale'); if (!isRecord(first[0]) || !Array.isArray(first[0]['messages'])) { throw new Error('Expected filtered context messages'); } expect(first[0]['messages']).toHaveLength(1); expect(activeGoalStatementFromContext(second[0])).toContain('Keep the agent oriented'); await pi.goal('cancel'); expect( await pi.emit('context', { messages: [existingGoalContext, existingContinuation], }), ).toEqual([{ messages: [] }]); }); it('lets an existing busy run adopt the goal and waits behind a pending message', async () => { const busy = new FakePi(); busy.idle = false; await busy.goal('set Join the current run safely'); expect(busy.sentMessages).toHaveLength(0); busy.pending = true; await busy.emit('agent_settled'); expect(busy.sentMessages).toHaveLength(0); busy.pending = false; busy.idle = true; await busy.emit('agent_settled'); expect(busy.sentMessages).toHaveLength(1); }); it('records every turn and continues once when an active run settles', async () => { const pi = new FakePi(); await pi.goal('set Finish all acceptance criteria'); pi.sentMessages.length = 0; await pi.emit('turn_end', { turnIndex: 0, message: {}, toolResults: [] }); expect(stateField(pi, 'turnCount')).toBe(1); expect(stateField(pi, 'lastCheckSource')).toBe('turn'); await pi.emit('agent_settled'); await pi.emit('agent_settled'); expect(pi.sentMessages).toHaveLength(1); expect(pi.sentMessages[0]?.message.content).toContain('Goal remains active'); await pi.emit('agent_start'); await pi.emit('agent_settled'); expect(pi.sentMessages).toHaveLength(2); }); it('requires two consecutive evidence-bearing achievement reports', async () => { const pi = new FakePi(); await pi.goal('set Prove the feature works'); pi.sentMessages.length = 0; const first = await pi.report({ status: 'achieved', summary: 'Focused tests pass', evidence: ['pnpm test: 12 passed'], }); expect(first.terminate).toBe(true); expect(stateField(pi, 'phase')).toBe('verifying'); expect(stateField(pi, 'verificationPasses')).toBe(1); await pi.emit('agent_settled'); expect(pi.sentMessages).toHaveLength(1); expect(pi.sentMessages[0]?.message.content).toContain('verification pass'); await pi.emit('agent_start'); const second = await pi.report({ status: 'achieved', summary: 'Independent recheck confirms completion', evidence: ['rerun: 12 passed', 'framework path verified'], }); expect(second.terminate).toBe(true); expect(stateField(pi, 'phase')).toBe('achieved'); expect(stateField(pi, 'verificationPasses')).toBe(2); pi.sentMessages.length = 0; await pi.emit('agent_settled'); expect(pi.sentMessages).toHaveLength(0); }); it('rejects malformed progress reports and reports submitted without an active goal', async () => { const noGoal = new FakePi(); await expect( noGoal.report({ status: 'continue', summary: 'work', evidence: [] }), ).rejects.toThrow(/No active Mosaic goal/); const pi = new FakePi(); await pi.goal('set Validate report boundaries'); const invalidReports: Record[] = [ {}, { status: 'invalid', summary: 'work', evidence: [] }, { status: 'continue', evidence: [] }, { status: 'continue', summary: ' ', evidence: [] }, { status: 'continue', summary: 'x'.repeat(2_001), evidence: [] }, { status: 'continue', summary: 'work', evidence: 'not-an-array' }, { status: 'continue', summary: 'work', evidence: Array.from({ length: 21 }, () => 'x') }, { status: 'continue', summary: 'work', evidence: [4] }, { status: 'continue', summary: 'work', evidence: [''] }, { status: 'continue', summary: 'work', evidence: ['x'.repeat(1_001)] }, { status: 'continue', summary: 'work', evidence: [], nextStep: 4 }, { status: 'continue', summary: 'work', evidence: [], nextStep: ' ' }, { status: 'continue', summary: 'work', evidence: [], nextStep: 'x'.repeat(2_001) }, ]; for (const report of invalidReports) { await expect(pi.report(report)).rejects.toThrow(); } expect(stateField(pi, 'phase')).toBe('active'); }); it('rejects an achievement claim without evidence', async () => { const pi = new FakePi(); await pi.goal('set Require evidence'); await expect( pi.report({ status: 'achieved', summary: 'Trust me', evidence: [] }), ).rejects.toThrow(/evidence/i); expect(stateField(pi, 'phase')).toBe('active'); }); it('redacts credential-shaped goal and report text before persistence or display', async () => { const githubToken = `ghp_${'a'.repeat(32)}`; const anthropicKey = `sk-ant-api03-${'b'.repeat(40)}`; const bearerToken = 'header.payload.signature-canary'; const databaseUrl = 'postgresql://mosaic:password-canary@db.example.invalid/mosaic'; const password = 'password-canary'; const pi = new FakePi(); await pi.goal(`set Rotate ${githubToken} without retaining it`); const context = await pi.emit('context', { messages: [] }); expect(activeGoalStatementFromContext(context[0])).not.toContain(githubToken); const result = await pi.report({ status: 'achieved', summary: `Validated ${anthropicKey}`, evidence: [`Authorization: Bearer ${bearerToken}`, `DATABASE_URL=${databaseUrl}`], nextStep: `password=${password}`, }); await pi.goal('status'); const persisted = JSON.stringify(latestGoalStateData(pi)); const displayed = pi.notifications.at(-1)?.message ?? ''; const toolOutput = JSON.stringify(result); for (const secret of [githubToken, anthropicKey, bearerToken, databaseUrl, password]) { expect(persisted).not.toContain(secret); expect(displayed).not.toContain(secret); expect(toolOutput).not.toContain(secret); } expect(persisted).toContain('[REDACTED-SECRET]'); }); it('preserves ordinary typed fields that resemble sensitive assignment names', async () => { const typedFields = 'token: string, password: boolean, secret: false'; const pi = new FakePi(); await pi.goal(`set Preserve TypeScript fields: ${typedFields}`); await pi.report({ status: 'continue', summary: `Schema still contains ${typedFields}`, evidence: [`interface Config { ${typedFields} }`], nextStep: `Keep ${typedFields} unchanged`, }); expect(stateField(pi, 'statement')).toContain(typedFields); expect(JSON.stringify(stateField(pi, 'lastReport'))).toContain(typedFields); expect(JSON.stringify(latestGoalStateData(pi))).not.toContain('[REDACTED-SECRET]'); }); it('stops autonomous continuation when the max-turn limit is reached', async () => { vi.stubEnv('MOSAIC_GOAL_MAX_TURNS', '2'); const pi = new FakePi(); await pi.goal('set Bound this run'); pi.sentMessages.length = 0; await pi.emit('turn_end', { turnIndex: 0, message: {}, toolResults: [] }); await pi.emit('turn_end', { turnIndex: 1, message: {}, toolResults: [] }); expect(stateField(pi, 'phase')).toBe('exhausted'); expect(pi.abortCount).toBe(1); await pi.emit('agent_settled'); expect(pi.sentMessages).toHaveLength(0); }); it('resets the no-progress sequence when a continuation report changes', async () => { const pi = new FakePi(); await pi.goal('set Track changing progress'); await pi.report({ status: 'continue', summary: 'First checkpoint', evidence: ['file A changed'], nextStep: 'Run focused tests', }); await pi.report({ status: 'continue', summary: 'Second checkpoint', evidence: ['focused tests passed'], nextStep: 'Review the diff', }); expect(stateField(pi, 'phase')).toBe('active'); expect(stateField(pi, 'noProgressReports')).toBe(1); await pi.goal('status'); expect(pi.notifications.at(-1)?.message).toContain('Next step: Review the diff'); expect(pi.notifications.at(-1)?.message).toContain('focused tests passed'); }); it('stops after a bounded number of identical no-progress reports', async () => { vi.stubEnv('MOSAIC_GOAL_MAX_NO_PROGRESS', '2'); const pi = new FakePi(); await pi.goal('set Detect stalled work'); const report = { status: 'continue', summary: 'No change yet', evidence: ['same observation'], nextStep: 'Try again', }; await pi.report(report); await pi.report(report); expect(stateField(pi, 'phase')).toBe('exhausted'); expect(stateField(pi, 'noProgressReports')).toBe(2); pi.sentMessages.length = 0; await pi.emit('agent_settled'); expect(pi.sentMessages).toHaveLength(0); }); it('rejects a goal report mixed with another tool result in the same turn', async () => { const pi = new FakePi(); await pi.goal('set Require a sole final report'); await pi.report({ status: 'achieved', summary: 'Premature mixed claim', evidence: ['one observation'], }); await pi.emit('turn_end', { turnIndex: 0, message: {}, toolResults: [{ toolName: 'mosaic_goal_report' }, { toolName: 'read' }], }); expect(stateField(pi, 'phase')).toBe('active'); expect(stateField(pi, 'verificationPasses')).toBe(0); expect(stateField(pi, 'lastCheckOutcome')).toBe('mixed-goal-report-rejected'); expect(pi.notifications.at(-1)?.level).toBe('warning'); }); it('marks blocked reports terminal until the operator resumes', async () => { const pi = new FakePi(); await pi.goal('set Stop on a real blocker'); await pi.report({ status: 'blocked', summary: 'Missing required access', evidence: ['provider returned 403'], }); expect(stateField(pi, 'phase')).toBe('blocked'); pi.sentMessages.length = 0; await pi.emit('agent_settled'); expect(pi.sentMessages).toHaveLength(0); await pi.goal('resume'); expect(stateField(pi, 'phase')).toBe('active'); expect(stateField(pi, 'turnCount')).toBe(0); expect(pi.sentMessages).toHaveLength(1); }); }); describe('Mosaic Pi goal compaction and restoration', () => { it('resets provisional verification and defers manual-compaction continuation until idle', async () => { vi.useFakeTimers(); const pi = new FakePi(); await pi.goal('set Survive compaction'); await pi.report({ status: 'achieved', summary: 'Initial claim', evidence: ['focused test passed'], }); expect(stateField(pi, 'phase')).toBe('verifying'); pi.sentMessages.length = 0; await pi.emit('session_compact', { reason: 'manual', willRetry: false }); expect(stateField(pi, 'phase')).toBe('active'); expect(stateField(pi, 'verificationPasses')).toBe(0); expect(stateField(pi, 'compactionCount')).toBe(1); expect(stateField(pi, 'lastCheckSource')).toBe('compact'); expect(pi.sentMessages).toHaveLength(0); await vi.runAllTimersAsync(); expect(pi.sentMessages).toHaveLength(1); expect(pi.sentMessages[0]?.message.content).toContain('compaction'); }); it('does not re-enter an active automatic compaction and relies on the settled backstop', async () => { vi.useFakeTimers(); const pi = new FakePi(); await pi.goal('set Avoid compaction races'); pi.sentMessages.length = 0; pi.idle = false; await pi.emit('session_compact', { reason: 'threshold', willRetry: false }); await vi.runAllTimersAsync(); expect(pi.sentMessages).toHaveLength(0); pi.idle = true; await pi.emit('agent_settled'); expect(pi.sentMessages).toHaveLength(1); }); it('restores branch-specific state on session start and tree navigation', async () => { vi.useFakeTimers(); const source = new FakePi(); await source.goal('set Restore this exact branch goal'); const activeState = latestGoalStateData(source); const restored = new FakePi([ { type: 'custom', customType: 'mosaic-goal-state', data: activeState }, ]); await restored.emit('session_start', { reason: 'resume' }); expect(restored.statuses.get('mosaic-goal')).toContain('active'); const context = await restored.emit('context', { messages: [] }); expect(activeGoalStatementFromContext(context[0])).toContain('Restore this exact branch goal'); await restored.goal('pause'); const pausedState = latestGoalStateData(restored); restored.branch = [{ type: 'custom', customType: 'mosaic-goal-state', data: pausedState }]; await restored.emit('session_tree', {}); await vi.runAllTimersAsync(); expect(stateField(restored, 'phase')).toBe('paused'); expect(restored.sentMessages).toHaveLength(0); }); it('restores only fully valid persisted states and ignores malformed entries', async () => { vi.useFakeTimers(); const source = new FakePi(); await source.goal('set Validate persisted branch state'); await source.report({ status: 'continue', summary: 'Valid report', evidence: ['valid evidence'], nextStep: 'Continue validation', }); const valid = latestGoalStateData(source); const validRestore = new FakePi([ { type: 'custom', customType: 'mosaic-goal-state', data: valid }, ]); await validRestore.emit('session_start', { reason: 'resume' }); expect(stateField(validRestore, 'statement')).toBe('Validate persisted branch state'); await validRestore.emit('session_shutdown', { reason: 'reload' }); const validReport = latestGoalStateData(source)['lastReport']; if (!isRecord(validReport)) throw new Error('Expected a valid persisted report fixture'); const integerFields = [ 'turnCount', 'reportCount', 'verificationPasses', 'requiredVerificationPasses', 'noProgressReports', 'maxTurns', 'maxNoProgressReports', 'compactionCount', ]; const corruptions: Array<(state: Record) => unknown> = [ (): unknown => null, (state): unknown => ({ ...state, version: 99 }), (state): unknown => ({ ...state, goalId: '' }), (state): unknown => ({ ...state, statement: '' }), (state): unknown => ({ ...state, statement: 'x'.repeat(8_001) }), (state): unknown => ({ ...state, phase: 'unknown' }), (state): unknown => ({ ...state, lastCheckSource: 'unknown' }), (state): unknown => ({ ...state, startedAt: 4 }), (state): unknown => ({ ...state, lastCheckAt: 4 }), (state): unknown => ({ ...state, lastReport: null }), (state): unknown => ({ ...state, lastProgressFingerprint: 4 }), (state): unknown => ({ ...state, stopReason: 4 }), ...integerFields.map((field: string) => (state: Record): unknown => ({ ...state, [field]: -1, })), ]; corruptions.push( (state: Record): unknown => ({ ...state, maxTurns: 501 }), (state: Record): unknown => ({ ...state, maxNoProgressReports: 101 }), (state: Record): unknown => ({ ...state, requiredVerificationPasses: 3 }), ); const reportCorruptions: Array> = [ { ...validReport, status: 'bad' }, { ...validReport, summary: '' }, { ...validReport, evidence: 'bad' }, { ...validReport, evidence: [4] }, { ...validReport, fingerprint: '' }, { ...validReport, reportedAt: '' }, { ...validReport, nextStep: 4 }, ]; for (const corruptReport of reportCorruptions) { corruptions.push((state: Record): unknown => ({ ...state, lastReport: corruptReport, })); } for (const corrupt of corruptions) { const candidate = corrupt(structuredClone(valid)); const restored = new FakePi([ { type: 'custom', customType: 'mosaic-goal-state', data: candidate }, ]); await restored.emit('session_start', { reason: 'resume' }); expect(restored.statuses.get('mosaic-goal')).toBeUndefined(); expect(await restored.emit('context', { messages: [] })).toEqual([undefined]); } const failClosed = new FakePi([ { type: 'custom', customType: 'mosaic-goal-state', data: valid }, { type: 'custom', customType: 'mosaic-goal-state', data: { ...valid, version: 99 } }, ]); await failClosed.emit('session_start', { reason: 'resume' }); expect(failClosed.statuses.get('mosaic-goal')).toBeUndefined(); }); it('fails closed instead of reusing credential-bearing legacy branch state', async () => { const source = new FakePi(); await source.goal('set Build a valid restore fixture'); const cleanState = latestGoalStateData(source); const legacyState = structuredClone(cleanState); legacyState['statement'] = `Legacy secret ghp_${'z'.repeat(32)}`; const restored = new FakePi([ { type: 'custom', customType: 'mosaic-goal-state', data: legacyState }, { type: 'custom', customType: 'mosaic-goal-state', data: cleanState }, ]); await restored.emit('session_start', { reason: 'resume' }); expect(restored.statuses.get('mosaic-goal')).toBeUndefined(); expect(await restored.emit('context', { messages: [] })).toEqual([undefined]); expect(restored.notifications.at(-1)?.level).toBe('warning'); expect(restored.notifications.at(-1)?.message).toContain('was not restored'); expect(restored.entries).toHaveLength(0); }); it('schedules an active tree-restored goal and preserves terminal state through compaction', async () => { vi.useFakeTimers(); const source = new FakePi(); await source.goal('set Restore active tree work'); const active = latestGoalStateData(source); const restored = new FakePi(); restored.branch = [{ type: 'custom', customType: 'mosaic-goal-state', data: active }]; await restored.emit('session_tree', {}); await vi.runAllTimersAsync(); expect(restored.sentMessages).toHaveLength(1); expect(restored.sentMessages[0]?.message.content).toContain('tree navigation'); await restored.goal('cancel'); await restored.emit('session_compact', { reason: 'manual', willRetry: false }); expect(stateField(restored, 'phase')).toBe('cancelled'); expect(stateField(restored, 'compactionCount')).toBe(1); }); it('cancels deferred continuation when the session shuts down', async () => { vi.useFakeTimers(); const pi = new FakePi(); await pi.goal('set Do not leak a stale timer'); pi.sentMessages.length = 0; await pi.emit('session_compact', { reason: 'manual', willRetry: false }); await pi.emit('session_shutdown', { reason: 'reload' }); await vi.runAllTimersAsync(); expect(pi.sentMessages).toHaveLength(0); }); });